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}
81
82pub struct Parser {
83 tokens: Vec<(Token, usize)>,
84 pos: usize,
85 next_rate_limit_slot: u32,
87 suppress_indirect_paren_call: u32,
90 pipe_rhs_depth: u32,
96 block_depth: u32,
103 no_pipe_forward_depth: u32,
111 suppress_scalar_hash_brace: u32,
114 next_desugar_tmp: u32,
116 error_file: String,
118 declared_subs: std::collections::HashSet<String>,
120 suppress_parenless_call: u32,
124 pending_thread_input: Option<Expr>,
129 suppress_slash_as_div: u32,
132 pub suppress_m_regex: u32,
135 suppress_colon_range: u32,
139 suppress_tilde_range: u32,
145 thread_last_mode: bool,
148 pub parsing_module: bool,
151 list_construct_close_pos: Option<usize>,
159 pending_synthetic_subs: Vec<Statement>,
164 next_overload_anon_id: u32,
166 pub bare_positional_indices: std::collections::HashSet<usize>,
173 current_package: String,
179}
180
181impl Parser {
182 pub fn new(tokens: Vec<(Token, usize)>) -> Self {
183 Self::new_with_file(tokens, "-e")
184 }
185
186 pub fn new_with_file(tokens: Vec<(Token, usize)>, file: impl Into<String>) -> Self {
187 Self {
188 tokens,
189 pos: 0,
190 next_rate_limit_slot: 0,
191 suppress_indirect_paren_call: 0,
192 pipe_rhs_depth: 0,
193 no_pipe_forward_depth: 0,
194 suppress_scalar_hash_brace: 0,
195 next_desugar_tmp: 0,
196 error_file: file.into(),
197 declared_subs: std::collections::HashSet::new(),
198 suppress_parenless_call: 0,
199 pending_thread_input: None,
200 suppress_slash_as_div: 0,
201 suppress_m_regex: 0,
202 suppress_colon_range: 0,
203 suppress_tilde_range: 0,
204 thread_last_mode: false,
205 pending_synthetic_subs: Vec::new(),
206 next_overload_anon_id: 0,
207 parsing_module: false,
208 list_construct_close_pos: None,
209 bare_positional_indices: std::collections::HashSet::new(),
210 block_depth: 0,
211 current_package: "main".to_string(),
212 }
213 }
214
215 fn alloc_desugar_tmp(&mut self) -> u32 {
216 let n = self.next_desugar_tmp;
217 self.next_desugar_tmp = self.next_desugar_tmp.saturating_add(1);
218 n
219 }
220
221 #[inline]
225 fn in_pipe_rhs(&self) -> bool {
226 self.pipe_rhs_depth > 0
227 }
228
229 fn pipe_supplies_slurped_list_operand(&self) -> bool {
232 self.in_pipe_rhs()
233 && (matches!(
234 self.peek(),
235 Token::Semicolon
236 | Token::RBrace
237 | Token::RParen
238 | Token::Eof
239 | Token::Comma
240 | Token::PipeForward
241 ) || self.peek_line() > self.prev_line())
242 }
243
244 #[inline]
249 fn pipe_placeholder_list(&self, line: usize) -> Expr {
250 Expr {
251 kind: ExprKind::List(vec![]),
252 line,
253 }
254 }
255
256 fn is_block_then_list_pipe_builtin(name: &str) -> bool {
261 matches!(
262 name,
263 "pfirst"
264 | "pany"
265 | "any"
266 | "all"
267 | "none"
268 | "first"
269 | "find_index"
270 | "firstidx"
271 | "first_index"
272 | "take_while"
273 | "drop_while"
274 | "skip_while"
275 | "reject"
276 | "grepv"
277 | "tap"
278 | "peek"
279 | "group_by"
280 | "chunk_by"
281 | "partition"
282 | "min_by"
283 | "max_by"
284 | "zip_with"
285 | "count_by"
286 )
287 }
288
289 fn lift_bareword_to_topic_call(expr: Expr) -> Expr {
300 let line = expr.line;
301 let topic = || Expr {
302 kind: ExprKind::ScalarVar("_".into()),
303 line,
304 };
305 match expr.kind {
306 ExprKind::Bareword(ref name) => Expr {
307 kind: ExprKind::FuncCall {
308 name: name.clone(),
309 args: vec![topic()],
310 },
311 line,
312 },
313 ExprKind::Unlink(ref args) if args.is_empty() => Expr {
315 kind: ExprKind::Unlink(vec![topic()]),
316 line,
317 },
318 ExprKind::Chmod(ref args) if args.is_empty() => Expr {
319 kind: ExprKind::Chmod(vec![topic()]),
320 line,
321 },
322 ExprKind::Stat(_) => expr,
324 ExprKind::Lstat(_) => expr,
325 ExprKind::Readlink(_) => expr,
326 ExprKind::Rev(ref inner) => {
328 if matches!(inner.kind, ExprKind::List(ref v) if v.is_empty()) {
329 Expr {
330 kind: ExprKind::Rev(Box::new(topic())),
331 line,
332 }
333 } else {
334 expr
335 }
336 }
337 _ => expr,
338 }
339 }
340
341 fn parse_assign_expr_stop_at_pipe(&mut self) -> StrykeResult<Expr> {
349 self.no_pipe_forward_depth = self.no_pipe_forward_depth.saturating_add(1);
350 let r = self.parse_assign_expr();
351 self.no_pipe_forward_depth = self.no_pipe_forward_depth.saturating_sub(1);
352 r
353 }
354
355 fn syntax_err(&self, message: impl Into<String>, line: usize) -> StrykeError {
356 StrykeError::new(ErrorKind::Syntax, message, line, self.error_file.clone())
357 }
358
359 fn try_parse_coderef_listop_args(&mut self, line: usize) -> StrykeResult<Option<Vec<Expr>>> {
366 if !matches!(self.peek(), Token::ScalarVar(_) | Token::Backslash) {
367 return Ok(None);
368 }
369 let f = self.parse_assign_expr_stop_at_pipe()?;
370 let _ = self.eat(&Token::Comma);
371 let list = if self.in_pipe_rhs()
372 && matches!(
373 self.peek(),
374 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof | Token::PipeForward
375 ) {
376 self.pipe_placeholder_list(line)
377 } else {
378 self.parse_expression()?
379 };
380 Ok(Some(vec![f, list]))
381 }
382
383 fn alloc_rate_limit_slot(&mut self) -> u32 {
384 let s = self.next_rate_limit_slot;
385 self.next_rate_limit_slot = self.next_rate_limit_slot.saturating_add(1);
386 s
387 }
388
389 fn peek(&self) -> &Token {
390 self.tokens
391 .get(self.pos)
392 .map(|(t, _)| t)
393 .unwrap_or(&Token::Eof)
394 }
395
396 fn peek_line(&self) -> usize {
397 self.tokens.get(self.pos).map(|(_, l)| *l).unwrap_or(0)
398 }
399
400 fn peek_at(&self, offset: usize) -> &Token {
401 self.tokens
402 .get(self.pos + offset)
403 .map(|(t, _)| t)
404 .unwrap_or(&Token::Eof)
405 }
406
407 fn advance(&mut self) -> (Token, usize) {
408 let tok = self
409 .tokens
410 .get(self.pos)
411 .cloned()
412 .unwrap_or((Token::Eof, 0));
413 self.pos += 1;
414 tok
415 }
416
417 fn prev_line(&self) -> usize {
419 if self.pos > 0 {
420 self.tokens.get(self.pos - 1).map(|(_, l)| *l).unwrap_or(0)
421 } else {
422 0
423 }
424 }
425
426 fn looks_like_hashref(&self) -> bool {
435 debug_assert!(matches!(self.peek(), Token::LBrace));
436 let tok1 = self.peek_at(1);
437 let tok2 = self.peek_at(2);
438 match tok1 {
439 Token::RBrace => true,
440 Token::Ident(_)
441 | Token::SingleString(_)
442 | Token::DoubleString(_)
443 | Token::ScalarVar(_)
444 | Token::Integer(_) => matches!(tok2, Token::FatArrow),
445 Token::HashVar(_) => matches!(tok2, Token::RBrace | Token::Comma),
446 _ => false,
447 }
448 }
449
450 fn expect(&mut self, expected: &Token) -> StrykeResult<usize> {
451 let (tok, line) = self.advance();
452 if std::mem::discriminant(&tok) == std::mem::discriminant(expected) {
453 Ok(line)
454 } else {
455 Err(self.syntax_err(format!("Expected {:?}, got {:?}", expected, tok), line))
456 }
457 }
458
459 fn eat(&mut self, expected: &Token) -> bool {
460 if std::mem::discriminant(self.peek()) == std::mem::discriminant(expected) {
461 self.advance();
462 true
463 } else {
464 false
465 }
466 }
467
468 fn at_eof(&self) -> bool {
469 matches!(self.peek(), Token::Eof)
470 }
471
472 fn filetest_allows_implicit_topic(tok: &Token) -> bool {
474 matches!(
475 tok,
476 Token::RParen
477 | Token::Semicolon
478 | Token::Comma
479 | Token::RBrace
480 | Token::Eof
481 | Token::LogAnd
482 | Token::LogOr
483 | Token::LogAndWord
484 | Token::LogOrWord
485 | Token::PipeForward
486 )
487 }
488
489 fn next_is_new_stmt_keyword(&self, stmt_line: usize) -> bool {
493 if crate::compat_mode() {
495 return false;
496 }
497 if self.peek_line() == stmt_line {
498 return false;
499 }
500 matches!(
501 self.peek(),
502 Token::Ident(ref kw) if matches!(kw.as_str(),
503 "use" | "no" | "my" | "our" | "local" | "sub" | "struct" | "enum"
504 | "if" | "unless" | "while" | "until" | "for" | "foreach"
505 | "return" | "last" | "next" | "redo" | "package" | "require"
506 | "BEGIN" | "END" | "UNITCHECK" | "frozen" | "const" | "typed"
507 | "fn" | "class" | "abstract" | "final" | "trait"
512 | "state" | "mysync" | "oursync"
513 )
514 )
515 }
516
517 fn next_is_new_statement_start(&self, stmt_line: usize) -> bool {
521 if crate::compat_mode() {
522 return false;
523 }
524 if self.peek_line() == stmt_line {
525 return false;
526 }
527 matches!(
528 self.peek(),
529 Token::ScalarVar(_)
530 | Token::DerefScalarVar(_)
531 | Token::ArrayVar(_)
532 | Token::HashVar(_)
533 | Token::LBrace
534 ) || self.next_is_new_stmt_keyword(stmt_line)
535 }
536
537 pub fn parse_program(&mut self) -> StrykeResult<Program> {
540 let mut statements = self.parse_statements()?;
541 if !self.pending_synthetic_subs.is_empty() {
545 let synthetics = std::mem::take(&mut self.pending_synthetic_subs);
546 let mut combined = Vec::with_capacity(synthetics.len() + statements.len());
547 combined.extend(synthetics);
548 combined.append(&mut statements);
549 statements = combined;
550 }
551 Ok(Program { statements })
552 }
553
554 pub fn parse_statements(&mut self) -> StrykeResult<Vec<Statement>> {
556 let mut statements = Vec::new();
557 while !self.at_eof() {
558 if matches!(self.peek(), Token::Semicolon) {
559 let line = self.peek_line();
560 self.advance();
561 statements.push(Statement {
562 label: None,
563 kind: StmtKind::Empty,
564 line,
565 });
566 continue;
567 }
568 statements.push(self.parse_statement()?);
569 }
570 Ok(statements)
571 }
572
573 fn parse_statement(&mut self) -> StrykeResult<Statement> {
576 let line = self.peek_line();
577
578 let label = match self.peek().clone() {
581 Token::Ident(_) => {
582 if matches!(self.peek_at(1), Token::Colon)
583 && !matches!(self.peek_at(2), Token::Colon)
584 {
585 let (tok, _) = self.advance();
586 let l = match tok {
587 Token::Ident(l) => l,
588 _ => unreachable!(),
589 };
590 self.advance(); Some(l)
592 } else {
593 None
594 }
595 }
596 _ => None,
597 };
598
599 let mut stmt = match self.peek().clone() {
600 Token::FormatDecl { .. } => {
601 let tok_line = self.peek_line();
602 let (tok, _) = self.advance();
603 match tok {
604 Token::FormatDecl { name, lines } => Statement {
605 label: label.clone(),
606 kind: StmtKind::FormatDecl { name, lines },
607 line: tok_line,
608 },
609 _ => unreachable!(),
610 }
611 }
612 Token::Ident(ref kw) => match kw.as_str() {
613 "if" => self.parse_if()?,
614 "unless" => self.parse_unless()?,
615 "while" => {
616 let mut s = self.parse_while()?;
617 if let StmtKind::While {
618 label: ref mut lbl, ..
619 } = s.kind
620 {
621 *lbl = label.clone();
622 }
623 s
624 }
625 "until" => {
626 let mut s = self.parse_until()?;
627 if let StmtKind::Until {
628 label: ref mut lbl, ..
629 } = s.kind
630 {
631 *lbl = label.clone();
632 }
633 s
634 }
635 "for" => {
636 let mut s = self.parse_for_or_foreach()?;
637 match s.kind {
638 StmtKind::For {
639 label: ref mut lbl, ..
640 }
641 | StmtKind::Foreach {
642 label: ref mut lbl, ..
643 } => *lbl = label.clone(),
644 _ => {}
645 }
646 s
647 }
648 "foreach" => {
649 let mut s = self.parse_foreach()?;
650 if let StmtKind::Foreach {
651 label: ref mut lbl, ..
652 } = s.kind
653 {
654 *lbl = label.clone();
655 }
656 s
657 }
658 "sub" => {
659 if crate::no_interop_mode() {
660 return Err(self.syntax_err(
661 "stryke uses `fn` instead of `sub` (--no-interop is active)",
662 self.peek_line(),
663 ));
664 }
665 self.parse_sub_decl(true)?
666 }
667 "fn" => self.parse_sub_decl(false)?,
668 "struct" => {
669 if crate::compat_mode() {
670 return Err(self.syntax_err(
671 "`struct` is a stryke extension (disabled by --compat)",
672 self.peek_line(),
673 ));
674 }
675 self.parse_struct_decl()?
676 }
677 "enum" => {
678 if crate::compat_mode() {
679 return Err(self.syntax_err(
680 "`enum` is a stryke extension (disabled by --compat)",
681 self.peek_line(),
682 ));
683 }
684 self.parse_enum_decl()?
685 }
686 "class" => {
687 if crate::compat_mode() {
688 return Err(self.syntax_err(
690 "Perl 5.38 `class` syntax not yet implemented in --compat mode",
691 self.peek_line(),
692 ));
693 }
694 self.parse_class_decl(false, false)?
695 }
696 "abstract" => {
697 self.advance(); if !matches!(self.peek(), Token::Ident(ref s) if s == "class") {
699 return Err(self.syntax_err(
700 "`abstract` must be followed by `class`",
701 self.peek_line(),
702 ));
703 }
704 self.parse_class_decl(true, false)?
705 }
706 "final" => {
707 self.advance(); if !matches!(self.peek(), Token::Ident(ref s) if s == "class") {
709 return Err(self
710 .syntax_err("`final` must be followed by `class`", self.peek_line()));
711 }
712 self.parse_class_decl(false, true)?
713 }
714 "trait" => {
715 if crate::compat_mode() {
716 return Err(self.syntax_err(
717 "`trait` is a stryke extension (disabled by --compat)",
718 self.peek_line(),
719 ));
720 }
721 self.parse_trait_decl()?
722 }
723 "my" => self.parse_my_our_local("my", false)?,
724 "state" => self.parse_my_our_local("state", false)?,
725 "mysync" => {
726 if crate::compat_mode() {
727 return Err(self.syntax_err(
728 "`mysync` is a stryke extension (disabled by --compat)",
729 self.peek_line(),
730 ));
731 }
732 self.parse_my_our_local("mysync", false)?
733 }
734 "oursync" => {
735 if crate::compat_mode() {
736 return Err(self.syntax_err(
737 "`oursync` is a stryke extension (disabled by --compat)",
738 self.peek_line(),
739 ));
740 }
741 self.parse_my_our_local("oursync", false)?
742 }
743 "frozen" | "const" => {
744 let leading = kw.as_str().to_string();
745 if crate::compat_mode() {
746 return Err(self.syntax_err(
747 format!("`{leading}` is a stryke extension (disabled by --compat)"),
748 self.peek_line(),
749 ));
750 }
751 self.advance(); if let Token::Ident(ref kw) = self.peek().clone() {
757 if kw == "my" {
758 let mut stmt = self.parse_my_our_local("my", true)?;
763 if let StmtKind::My(ref mut decls) = stmt.kind {
764 for decl in decls.iter_mut() {
765 decl.frozen = true;
766 }
767 }
768 stmt
769 } else {
770 return Err(self.syntax_err(
771 format!("Expected 'my' after '{leading}'"),
772 self.peek_line(),
773 ));
774 }
775 } else {
776 return Err(self.syntax_err(
777 format!("Expected 'my' after '{leading}'"),
778 self.peek_line(),
779 ));
780 }
781 }
782 "typed" => {
783 if crate::compat_mode() {
784 return Err(self.syntax_err(
785 "`typed` is a stryke extension (disabled by --compat)",
786 self.peek_line(),
787 ));
788 }
789 self.advance();
790 if let Token::Ident(ref kw) = self.peek().clone() {
791 if kw == "my" {
792 self.parse_my_our_local("my", true)?
793 } else {
794 return Err(
795 self.syntax_err("Expected 'my' after 'typed'", self.peek_line())
796 );
797 }
798 } else {
799 return Err(
800 self.syntax_err("Expected 'my' after 'typed'", self.peek_line())
801 );
802 }
803 }
804 "our" => self.parse_my_our_local("our", false)?,
805 "local" => self.parse_my_our_local("local", false)?,
806 "package" => self.parse_package()?,
807 "use" => self.parse_use()?,
808 "no" => self.parse_no()?,
809 "return" => self.parse_return()?,
810 "last" => {
811 self.advance();
812 let lbl = self.try_take_loop_label();
813 let stmt = Statement {
814 label: None,
815 kind: StmtKind::Last(lbl.or(label.clone())),
816 line,
817 };
818 self.parse_stmt_postfix_modifier(stmt)?
819 }
820 "next" => {
821 self.advance();
822 let lbl = self.try_take_loop_label();
823 let stmt = Statement {
824 label: None,
825 kind: StmtKind::Next(lbl.or(label.clone())),
826 line,
827 };
828 self.parse_stmt_postfix_modifier(stmt)?
829 }
830 "redo" => {
831 self.advance();
832 let lbl = self.try_take_loop_label();
833 let stmt = Statement {
834 label: None,
835 kind: StmtKind::Redo(lbl.or(label.clone())),
836 line,
837 };
838 self.parse_stmt_postfix_modifier(stmt)?
839 }
840 "BEGIN" => {
841 self.advance();
842 let block = self.parse_block()?;
843 Statement {
844 label: None,
845 kind: StmtKind::Begin(block),
846 line,
847 }
848 }
849 "END" => {
850 self.advance();
851 let block = self.parse_block()?;
852 Statement {
853 label: None,
854 kind: StmtKind::End(block),
855 line,
856 }
857 }
858 "UNITCHECK" => {
859 self.advance();
860 let block = self.parse_block()?;
861 Statement {
862 label: None,
863 kind: StmtKind::UnitCheck(block),
864 line,
865 }
866 }
867 "CHECK" => {
868 self.advance();
869 let block = self.parse_block()?;
870 Statement {
871 label: None,
872 kind: StmtKind::Check(block),
873 line,
874 }
875 }
876 "INIT" => {
877 self.advance();
878 let block = self.parse_block()?;
879 Statement {
880 label: None,
881 kind: StmtKind::Init(block),
882 line,
883 }
884 }
885 "goto" => {
886 self.advance();
887 let target = self.parse_expression()?;
888 let stmt = Statement {
889 label: None,
890 kind: StmtKind::Goto {
891 target: Box::new(target),
892 },
893 line,
894 };
895 self.parse_stmt_postfix_modifier(stmt)?
897 }
898 "continue" => {
899 self.advance();
900 let block = self.parse_block()?;
901 Statement {
902 label: None,
903 kind: StmtKind::Continue(block),
904 line,
905 }
906 }
907 "before"
908 if matches!(
909 self.peek_at(1),
910 Token::SingleString(_) | Token::DoubleString(_)
911 ) =>
912 {
913 self.parse_advice_decl(crate::ast::AdviceKind::Before)?
914 }
915 "after"
916 if matches!(
917 self.peek_at(1),
918 Token::SingleString(_) | Token::DoubleString(_)
919 ) =>
920 {
921 self.parse_advice_decl(crate::ast::AdviceKind::After)?
922 }
923 "around"
924 if matches!(
925 self.peek_at(1),
926 Token::SingleString(_) | Token::DoubleString(_)
927 ) =>
928 {
929 self.parse_advice_decl(crate::ast::AdviceKind::Around)?
930 }
931 "try" => self.parse_try_catch()?,
932 "defer" => self.parse_defer_stmt()?,
933 "tie" => self.parse_tie_stmt()?,
934 "given" => self.parse_given()?,
935 "when" => self.parse_when_stmt()?,
936 "default" => self.parse_default_stmt()?,
937 "eval_timeout" => self.parse_eval_timeout()?,
938 "do" => {
939 if matches!(self.peek_at(1), Token::LBrace) {
940 self.advance();
941 let body = self.parse_block()?;
942 if let Token::Ident(ref w) = self.peek().clone() {
943 if w == "while" {
944 self.advance();
945 self.expect(&Token::LParen)?;
946 let mut condition = self.parse_expression()?;
947 Self::mark_match_scalar_g_for_boolean_condition(&mut condition);
948 self.expect(&Token::RParen)?;
949 self.eat(&Token::Semicolon);
950 Statement {
951 label: label.clone(),
952 kind: StmtKind::DoWhile { body, condition },
953 line,
954 }
955 } else {
956 let inner_line = body.first().map(|s| s.line).unwrap_or(line);
957 let inner = Expr {
958 kind: ExprKind::CodeRef {
959 params: vec![],
960 body,
961 },
962 line: inner_line,
963 };
964 let expr = Expr {
965 kind: ExprKind::Do(Box::new(inner)),
966 line,
967 };
968 let stmt = Statement {
969 label: label.clone(),
970 kind: StmtKind::Expression(expr),
971 line,
972 };
973 self.parse_stmt_postfix_modifier(stmt)?
975 }
976 } else {
977 let inner_line = body.first().map(|s| s.line).unwrap_or(line);
978 let inner = Expr {
979 kind: ExprKind::CodeRef {
980 params: vec![],
981 body,
982 },
983 line: inner_line,
984 };
985 let expr = Expr {
986 kind: ExprKind::Do(Box::new(inner)),
987 line,
988 };
989 let stmt = Statement {
990 label: label.clone(),
991 kind: StmtKind::Expression(expr),
992 line,
993 };
994 self.parse_stmt_postfix_modifier(stmt)?
995 }
996 } else {
997 if let Some(expr) = self.try_parse_bareword_stmt_call() {
998 let stmt = self.maybe_postfix_modifier(expr)?;
999 self.parse_stmt_postfix_modifier(stmt)?
1000 } else {
1001 let expr = self.parse_expression()?;
1002 let stmt = self.maybe_postfix_modifier(expr)?;
1003 self.parse_stmt_postfix_modifier(stmt)?
1004 }
1005 }
1006 }
1007 _ => {
1008 if let Some(expr) = self.try_parse_bareword_stmt_call() {
1010 let stmt = self.maybe_postfix_modifier(expr)?;
1011 self.parse_stmt_postfix_modifier(stmt)?
1012 } else {
1013 let expr = self.parse_expression()?;
1014 let stmt = self.maybe_postfix_modifier(expr)?;
1015 self.parse_stmt_postfix_modifier(stmt)?
1016 }
1017 }
1018 },
1019 Token::LBrace => {
1020 if self.looks_like_hashref() {
1023 let expr = self.parse_expression()?;
1024 let stmt = self.maybe_postfix_modifier(expr)?;
1025 self.parse_stmt_postfix_modifier(stmt)?
1026 } else {
1027 let block = self.parse_block()?;
1028 let stmt = Statement {
1029 label: None,
1030 kind: StmtKind::Block(block),
1031 line,
1032 };
1033 self.parse_stmt_postfix_modifier(stmt)?
1035 }
1036 }
1037 _ => {
1038 let expr = self.parse_expression()?;
1039 let stmt = self.maybe_postfix_modifier(expr)?;
1040 self.parse_stmt_postfix_modifier(stmt)?
1041 }
1042 };
1043
1044 stmt.label = label;
1045 Ok(stmt)
1046 }
1047
1048 fn try_take_loop_label(&mut self) -> Option<String> {
1054 let Token::Ident(s) = self.peek() else {
1055 return None;
1056 };
1057 let mut chars = s.chars();
1058 let first = chars.next()?;
1059 if !(first.is_ascii_uppercase() || first == '_') {
1060 return None;
1061 }
1062 let ok = s
1063 .chars()
1064 .all(|c| c.is_ascii_uppercase() || c.is_ascii_digit() || c == '_');
1065 if !ok {
1066 return None;
1067 }
1068 let (Token::Ident(l), _) = self.advance() else {
1069 unreachable!()
1070 };
1071 Some(l)
1072 }
1073
1074 fn parse_stmt_postfix_modifier(&mut self, stmt: Statement) -> StrykeResult<Statement> {
1076 let line = stmt.line;
1077 if self.peek_line() > self.prev_line() {
1082 self.eat(&Token::Semicolon);
1083 return Ok(stmt);
1084 }
1085 if let Token::Ident(ref kw) = self.peek().clone() {
1086 match kw.as_str() {
1087 "if" => {
1088 self.advance();
1089 let mut cond = self.parse_expression()?;
1090 Self::mark_match_scalar_g_for_boolean_condition(&mut cond);
1091 self.eat(&Token::Semicolon);
1092 return Ok(Statement {
1093 label: None,
1094 kind: StmtKind::If {
1095 condition: cond,
1096 body: vec![stmt],
1097 elsifs: vec![],
1098 else_block: None,
1099 },
1100 line,
1101 });
1102 }
1103 "unless" => {
1104 self.advance();
1105 let mut cond = self.parse_expression()?;
1106 Self::mark_match_scalar_g_for_boolean_condition(&mut cond);
1107 self.eat(&Token::Semicolon);
1108 return Ok(Statement {
1109 label: None,
1110 kind: StmtKind::Unless {
1111 condition: cond,
1112 body: vec![stmt],
1113 else_block: None,
1114 },
1115 line,
1116 });
1117 }
1118 "while" | "until" | "for" | "foreach" => {
1119 if let Some(expr) = Self::stmt_into_postfix_body_expr(stmt) {
1122 let out = self.maybe_postfix_modifier(expr)?;
1123 self.eat(&Token::Semicolon);
1124 return Ok(out);
1125 }
1126 return Err(self.syntax_err(
1127 format!("postfix `{}` is not supported on this statement form", kw),
1128 self.peek_line(),
1129 ));
1130 }
1131 "pmap" | "pflat_map" | "pgrep" | "pfor" | "preduce" | "pcache" | "par" => {
1133 let line = stmt.line;
1134 let block = self.stmt_into_parallel_block(stmt)?;
1135 let which = kw.as_str();
1136 self.advance();
1137 self.eat(&Token::Comma);
1138 let (list, progress) = self.parse_assign_expr_list_optional_progress()?;
1139 self.eat(&Token::Semicolon);
1140 let list = Box::new(list);
1141 let progress = progress.map(Box::new);
1142 let kind = match which {
1143 "pmap" => ExprKind::PMapExpr {
1144 block,
1145 list,
1146 progress,
1147 flat_outputs: false,
1148 on_cluster: None,
1149 stream: false,
1150 },
1151 "pflat_map" => ExprKind::PMapExpr {
1152 block,
1153 list,
1154 progress,
1155 flat_outputs: true,
1156 on_cluster: None,
1157 stream: false,
1158 },
1159 "pgrep" => ExprKind::PGrepExpr {
1160 block,
1161 list,
1162 progress,
1163 stream: false,
1164 },
1165 "pfor" => ExprKind::PForExpr {
1166 block,
1167 list,
1168 progress,
1169 },
1170 "preduce" => ExprKind::PReduceExpr {
1171 block,
1172 list,
1173 progress,
1174 },
1175 "pcache" => ExprKind::PcacheExpr {
1176 block,
1177 list,
1178 progress,
1179 },
1180 "par" => ExprKind::ParExpr { block, list },
1181 _ => unreachable!(),
1182 };
1183 return Ok(Statement {
1184 label: None,
1185 kind: StmtKind::Expression(Expr { kind, line }),
1186 line,
1187 });
1188 }
1189 _ => {}
1190 }
1191 }
1192 self.eat(&Token::Semicolon);
1193 Ok(stmt)
1194 }
1195
1196 fn stmt_into_parallel_block(&self, stmt: Statement) -> StrykeResult<Block> {
1199 let line = stmt.line;
1200 match stmt.kind {
1201 StmtKind::Block(block) => Ok(block),
1202 StmtKind::Expression(expr) => {
1203 if let ExprKind::Do(ref inner) = expr.kind {
1204 if let ExprKind::CodeRef { ref body, .. } = inner.kind {
1205 return Ok(body.clone());
1206 }
1207 }
1208 Ok(vec![Statement {
1209 label: None,
1210 kind: StmtKind::Expression(expr),
1211 line,
1212 }])
1213 }
1214 _ => Err(self.syntax_err(
1215 "postfix parallel op expects `do { }`, a bare `{ }` block, or an expression statement",
1216 line,
1217 )),
1218 }
1219 }
1220
1221 fn stmt_into_postfix_body_expr(stmt: Statement) -> Option<Expr> {
1224 match stmt.kind {
1225 StmtKind::Expression(expr) => Some(expr),
1226 StmtKind::Block(block) => {
1227 let line = stmt.line;
1228 let inner = Expr {
1229 kind: ExprKind::CodeRef {
1230 params: vec![],
1231 body: block,
1232 },
1233 line,
1234 };
1235 Some(Expr {
1236 kind: ExprKind::Do(Box::new(inner)),
1237 line,
1238 })
1239 }
1240 _ => None,
1241 }
1242 }
1243
1244 fn peek_is_postfix_stmt_modifier_keyword(&self) -> bool {
1247 matches!(
1248 self.peek(),
1249 Token::Ident(ref kw)
1250 if matches!(
1251 kw.as_str(),
1252 "if" | "unless" | "while" | "until" | "for" | "foreach"
1253 )
1254 )
1255 }
1256
1257 fn peek_is_named_unary_terminator(&self) -> bool {
1264 matches!(
1265 self.peek(),
1266 Token::Semicolon
1267 | Token::RBrace
1268 | Token::RParen
1269 | Token::RBracket
1270 | Token::Eof
1271 | Token::Comma
1272 | Token::FatArrow
1273 | Token::PipeForward
1274 | Token::Question
1275 | Token::Colon
1276 | Token::NumEq
1277 | Token::NumNe
1278 | Token::NumLt
1279 | Token::NumGt
1280 | Token::NumLe
1281 | Token::NumGe
1282 | Token::Spaceship
1283 | Token::StrEq
1284 | Token::StrNe
1285 | Token::StrLt
1286 | Token::StrGt
1287 | Token::StrLe
1288 | Token::StrGe
1289 | Token::StrCmp
1290 | Token::LogAnd
1291 | Token::LogOr
1292 | Token::LogAndWord
1293 | Token::LogOrWord
1294 | Token::DefinedOr
1295 | Token::Range
1296 | Token::RangeExclusive
1297 | Token::Assign
1298 | Token::PlusAssign
1299 | Token::MinusAssign
1300 | Token::MulAssign
1301 | Token::DivAssign
1302 | Token::ModAssign
1303 | Token::PowAssign
1304 | Token::DotAssign
1305 | Token::AndAssign
1306 | Token::OrAssign
1307 | Token::XorAssign
1308 | Token::DefinedOrAssign
1309 | Token::ShiftLeftAssign
1310 | Token::ShiftRightAssign
1311 | Token::BitAndAssign
1312 | Token::BitOrAssign
1313 )
1314 }
1315
1316 fn maybe_postfix_modifier(&mut self, expr: Expr) -> StrykeResult<Statement> {
1317 let line = expr.line;
1318 if self.peek_line() > self.prev_line() {
1320 return Ok(Statement {
1321 label: None,
1322 kind: StmtKind::Expression(expr),
1323 line,
1324 });
1325 }
1326 match self.peek() {
1327 Token::Ident(ref kw) => match kw.as_str() {
1328 "if" => {
1329 self.advance();
1330 let cond = self.parse_expression()?;
1331 Ok(Statement {
1332 label: None,
1333 kind: StmtKind::Expression(Expr {
1334 kind: ExprKind::PostfixIf {
1335 expr: Box::new(expr),
1336 condition: Box::new(cond),
1337 },
1338 line,
1339 }),
1340 line,
1341 })
1342 }
1343 "unless" => {
1344 self.advance();
1345 let cond = self.parse_expression()?;
1346 Ok(Statement {
1347 label: None,
1348 kind: StmtKind::Expression(Expr {
1349 kind: ExprKind::PostfixUnless {
1350 expr: Box::new(expr),
1351 condition: Box::new(cond),
1352 },
1353 line,
1354 }),
1355 line,
1356 })
1357 }
1358 "while" => {
1359 self.advance();
1360 let cond = self.parse_expression()?;
1361 Ok(Statement {
1362 label: None,
1363 kind: StmtKind::Expression(Expr {
1364 kind: ExprKind::PostfixWhile {
1365 expr: Box::new(expr),
1366 condition: Box::new(cond),
1367 },
1368 line,
1369 }),
1370 line,
1371 })
1372 }
1373 "until" => {
1374 self.advance();
1375 let cond = self.parse_expression()?;
1376 Ok(Statement {
1377 label: None,
1378 kind: StmtKind::Expression(Expr {
1379 kind: ExprKind::PostfixUntil {
1380 expr: Box::new(expr),
1381 condition: Box::new(cond),
1382 },
1383 line,
1384 }),
1385 line,
1386 })
1387 }
1388 "for" | "foreach" => {
1389 self.advance();
1390 let list = self.parse_expression()?;
1391 Ok(Statement {
1392 label: None,
1393 kind: StmtKind::Expression(Expr {
1394 kind: ExprKind::PostfixForeach {
1395 expr: Box::new(expr),
1396 list: Box::new(list),
1397 },
1398 line,
1399 }),
1400 line,
1401 })
1402 }
1403 _ => Ok(Statement {
1404 label: None,
1405 kind: StmtKind::Expression(expr),
1406 line,
1407 }),
1408 },
1409 _ => Ok(Statement {
1410 label: None,
1411 kind: StmtKind::Expression(expr),
1412 line,
1413 }),
1414 }
1415 }
1416
1417 fn try_parse_bareword_stmt_call(&mut self) -> Option<Expr> {
1419 let saved = self.pos;
1420 let line = self.peek_line();
1421 let mut name = match self.peek() {
1422 Token::Ident(n) => n.clone(),
1423 _ => return None,
1424 };
1425 if name.starts_with('\x00') || !Self::bareword_stmt_may_be_sub(&name) {
1427 return None;
1428 }
1429 self.advance();
1430 while self.eat(&Token::PackageSep) {
1431 match self.advance() {
1432 (Token::Ident(part), _) => {
1433 name = format!("{}::{}", name, part);
1434 }
1435 _ => {
1436 self.pos = saved;
1437 return None;
1438 }
1439 }
1440 }
1441 match self.peek() {
1442 Token::Semicolon | Token::RBrace => Some(Expr {
1443 kind: ExprKind::FuncCall { name, args: vec![] },
1444 line,
1445 }),
1446 _ => {
1447 self.pos = saved;
1448 None
1449 }
1450 }
1451 }
1452
1453 pub(crate) fn operator_keyword_to_ident_str(tok: &Token) -> Option<&'static str> {
1457 Some(match tok {
1458 Token::StrEq => "eq",
1459 Token::StrNe => "ne",
1460 Token::StrLt => "lt",
1461 Token::StrGt => "gt",
1462 Token::StrLe => "le",
1463 Token::StrGe => "ge",
1464 Token::StrCmp => "cmp",
1465 Token::LogAndWord => "and",
1466 Token::LogOrWord => "or",
1467 Token::LogNotWord => "not",
1468 Token::X => "x",
1469 _ => return None,
1470 })
1471 }
1472
1473 pub(crate) fn is_underscore_topic_slot(name: &str) -> bool {
1477 if name == "_" {
1478 return true;
1479 }
1480 if !name.starts_with('_') || name.len() < 2 {
1481 return false;
1482 }
1483 let bytes = name.as_bytes();
1484 let mut i = 1;
1485 while i < bytes.len() && bytes[i].is_ascii_digit() {
1487 i += 1;
1488 }
1489 let chevrons_start = i;
1491 while i < bytes.len() && bytes[i] == b'<' {
1492 i += 1;
1493 }
1494 i == bytes.len() && (i > 1 || chevrons_start > 1)
1496 }
1497
1498 pub(crate) fn is_reserved_special_var_name(name: &str) -> bool {
1508 matches!(
1509 name,
1510 "STDIN" | "STDOUT" | "STDERR" | "ARGV" | "ARGVOUT" | "DATA"
1512 | "ENV" | "INC" | "SIG" | "ISA"
1520 | "EXPORT" | "EXPORT_OK" | "EXPORT_TAGS"
1521 | "VERSION"
1522 | "__FILE__" | "__LINE__" | "__PACKAGE__" | "__SUB__"
1524 | "__DATA__" | "__END__"
1525 )
1526 }
1527
1528 fn bareword_stmt_may_be_sub(name: &str) -> bool {
1530 if Self::is_underscore_topic_slot(name) {
1535 return false;
1536 }
1537 !matches!(
1538 name,
1539 "__FILE__"
1540 | "__LINE__"
1541 | "__PACKAGE__"
1542 | "__SUB__"
1543 | "abs"
1544 | "async"
1545 | "spawn"
1546 | "atan2"
1547 | "await"
1548 | "barrier"
1549 | "bless"
1550 | "caller"
1551 | "capture"
1552 | "cat"
1553 | "chdir"
1554 | "chmod"
1555 | "chomp"
1556 | "chop"
1557 | "chr"
1558 | "chown"
1559 | "closedir"
1560 | "close"
1561 | "collect"
1562 | "cos"
1563 | "crypt"
1564 | "defined"
1565 | "dec"
1566 | "delete"
1567 | "die"
1568 | "deque"
1569 | "do"
1570 | "each"
1571 | "eof"
1572 | "fore"
1573 | "eval"
1574 | "exec"
1575 | "exists"
1576 | "exit"
1577 | "exp"
1578 | "fan"
1579 | "fan_cap"
1580 | "fc"
1581 | "fetch_url"
1582 | "d"
1583 | "dirs"
1584 | "dr"
1585 | "f"
1586 | "fi"
1587 | "files"
1588 | "filesf"
1589 | "filter"
1590 | "fr"
1591 | "getcwd"
1592 | "glob_par"
1593 | "par_sed"
1594 | "glob"
1595 | "grep"
1596 | "greps"
1597 | "heap"
1598 | "hex"
1599 | "inc"
1600 | "index"
1601 | "int"
1602 | "join"
1603 | "keys"
1604 | "lcfirst"
1605 | "lc"
1606 | "length"
1607 | "link"
1608 | "log"
1609 | "lstat"
1610 | "map"
1611 | "flat_map"
1612 | "maps"
1613 | "flat_maps"
1614 | "flatten"
1615 | "frequencies"
1616 | "freq"
1617 | "pfrequencies"
1618 | "pfreq"
1619 | "interleave"
1620 | "ddump"
1621 | "stringify"
1622 | "str"
1623 | "s"
1624 | "input"
1625 | "lines"
1626 | "words"
1627 | "chars"
1628 | "digits"
1629 | "letters"
1630 | "letters_uc"
1631 | "letters_lc"
1632 | "punctuation"
1633 | "sentences"
1634 | "paragraphs"
1635 | "sections"
1636 | "numbers"
1637 | "graphemes"
1638 | "columns"
1639 | "trim"
1640 | "avg"
1641 | "top"
1642 | "pager"
1643 | "pg"
1644 | "less"
1645 | "count_by"
1646 | "to_file"
1647 | "to_json"
1648 | "to_csv"
1649 | "grep_v"
1650 | "select_keys"
1651 | "pluck"
1652 | "clamp"
1653 | "normalize"
1654 | "stddev"
1655 | "squared"
1656 | "square"
1657 | "cubed"
1658 | "cube"
1659 | "expt"
1660 | "pow"
1661 | "pw"
1662 | "snake_case"
1663 | "camel_case"
1664 | "kebab_case"
1665 | "to_toml"
1666 | "to_yaml"
1667 | "to_xml"
1668 | "to_html"
1669 | "to_markdown"
1670 | "xopen"
1671 | "clip"
1672 | "paste"
1673 | "to_table"
1674 | "sparkline"
1675 | "bar_chart"
1676 | "flame"
1677 | "set"
1678 | "list_count"
1679 | "list_size"
1680 | "count"
1681 | "size"
1682 | "cnt"
1683 | "len"
1684 | "all"
1685 | "any"
1686 | "none"
1687 | "take_while"
1688 | "drop_while"
1689 | "skip_while"
1690 | "skip"
1691 | "first_or"
1692 | "tap"
1693 | "peek"
1694 | "partition"
1695 | "min_by"
1696 | "max_by"
1697 | "zip_with"
1698 | "group_by"
1699 | "chunk_by"
1700 | "with_index"
1701 | "puniq"
1702 | "pfirst"
1703 | "pany"
1704 | "uniq"
1705 | "distinct"
1706 | "shuffle"
1707 | "shuffled"
1708 | "chunked"
1709 | "windowed"
1710 | "match"
1711 | "mkdir"
1712 | "every"
1713 | "gen"
1714 | "oct"
1715 | "open"
1716 | "p"
1717 | "opendir"
1718 | "ord"
1719 | "par"
1720 | "par_lines"
1721 | "par_walk"
1722 | "pipe"
1723 | "pipes"
1724 | "block_devices"
1725 | "char_devices"
1726 | "exe"
1727 | "executables"
1728 | "rate_limit"
1729 | "retry"
1730 | "pcache"
1731 | "pchannel"
1732 | "pfor"
1733 | "pgrep"
1734 | "pgreps"
1735 | "pipeline"
1736 | "pmap_chunked"
1737 | "pmap_reduce"
1738 | "par_reduce"
1739 | "pmap_on"
1740 | "pflat_map_on"
1741 | "pmap"
1742 | "pmaps"
1743 | "pflat_map"
1744 | "pflat_maps"
1745 | "pop"
1746 | "pos"
1747 | "ppool"
1748 | "preduce_init"
1749 | "preduce"
1750 | "pselect"
1751 | "printf"
1752 | "print"
1753 | "pr"
1754 | "psort"
1755 | "push"
1756 | "pwatch"
1757 | "rand"
1758 | "readdir"
1759 | "readlink"
1760 | "reduce"
1761 | "fold"
1762 | "inject"
1763 | "first"
1764 | "detect"
1765 | "find"
1766 | "find_all"
1767 | "find_index"
1768 | "firstidx"
1769 | "first_index"
1770 | "ref"
1771 | "rename"
1772 | "require"
1773 | "rev"
1774 | "reverse"
1775 | "reversed"
1776 | "rewinddir"
1777 | "rindex"
1778 | "rmdir"
1779 | "rm"
1780 | "say"
1781 | "scalar"
1782 | "seekdir"
1783 | "shift"
1784 | "sin"
1785 | "slurp"
1786 | "sockets"
1787 | "sort"
1788 | "splice"
1789 | "splice_last"
1790 | "splice1"
1791 | "spl_last"
1792 | "split"
1793 | "sprintf"
1794 | "sqrt"
1795 | "srand"
1796 | "stat"
1797 | "study"
1798 | "substr"
1799 | "symlink"
1800 | "sym_links"
1801 | "system"
1802 | "telldir"
1803 | "timer"
1804 | "trace"
1805 | "ucfirst"
1806 | "uc"
1807 | "undef"
1808 | "umask"
1809 | "unlink"
1810 | "unshift"
1811 | "utime"
1812 | "values"
1813 | "wantarray"
1814 | "warn"
1815 | "watch"
1816 | "yield"
1817 | "sub"
1818 )
1819 }
1820
1821 fn parse_block(&mut self) -> StrykeResult<Block> {
1822 self.expect(&Token::LBrace)?;
1823 let saved_pipe_rhs_depth = self.pipe_rhs_depth;
1826 self.pipe_rhs_depth = 0;
1827 self.block_depth += 1;
1828 let mut stmts = Vec::new();
1829 if let Some(param_stmts) = self.try_parse_block_params()? {
1833 stmts.extend(param_stmts);
1834 }
1835 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
1836 if self.eat(&Token::Semicolon) {
1837 continue;
1838 }
1839 stmts.push(self.parse_statement()?);
1840 }
1841 self.expect(&Token::RBrace)?;
1842 self.pipe_rhs_depth = saved_pipe_rhs_depth;
1843 self.block_depth -= 1;
1844 Self::default_topic_for_sole_bareword(&mut stmts);
1845 Ok(stmts)
1846 }
1847
1848 fn try_parse_block_params(&mut self) -> StrykeResult<Option<Vec<Statement>>> {
1853 if !matches!(self.peek(), Token::BitOr) {
1854 return Ok(None);
1855 }
1856 let mut i = 1; loop {
1859 match self.peek_at(i) {
1860 Token::ScalarVar(_) => i += 1,
1861 _ => return Ok(None), }
1863 match self.peek_at(i) {
1864 Token::BitOr => break, Token::Comma => i += 1, _ => return Ok(None), }
1868 }
1869 let line = self.peek_line();
1871 self.advance(); let mut names = Vec::new();
1873 loop {
1874 if let Token::ScalarVar(ref name) = self.peek().clone() {
1875 names.push(name.clone());
1876 self.advance();
1877 }
1878 if self.eat(&Token::BitOr) {
1879 break;
1880 }
1881 self.expect(&Token::Comma)?;
1882 }
1883 let sources: Vec<&str> = match names.len() {
1888 1 => vec!["_"],
1889 2 => vec!["a", "b"],
1890 n => {
1891 let _ = n;
1893 vec![] }
1895 };
1896 let mut stmts = Vec::with_capacity(names.len());
1897 if !sources.is_empty() {
1898 for (name, src) in names.iter().zip(sources.iter()) {
1899 stmts.push(Statement {
1900 label: None,
1901 kind: StmtKind::My(vec![VarDecl {
1902 sigil: Sigil::Scalar,
1903 name: name.clone(),
1904 initializer: Some(Expr {
1905 kind: ExprKind::ScalarVar(src.to_string()),
1906 line,
1907 }),
1908 frozen: false,
1909 type_annotation: None,
1910 list_context: false,
1911 }]),
1912 line,
1913 });
1914 }
1915 } else {
1916 for (idx, name) in names.iter().enumerate() {
1918 let src = if idx == 0 {
1919 "_".to_string()
1920 } else {
1921 format!("_{idx}")
1922 };
1923 stmts.push(Statement {
1924 label: None,
1925 kind: StmtKind::My(vec![VarDecl {
1926 sigil: Sigil::Scalar,
1927 name: name.clone(),
1928 initializer: Some(Expr {
1929 kind: ExprKind::ScalarVar(src),
1930 line,
1931 }),
1932 frozen: false,
1933 type_annotation: None,
1934 list_context: false,
1935 }]),
1936 line,
1937 });
1938 }
1939 }
1940 Ok(Some(stmts))
1941 }
1942
1943 fn default_topic_for_sole_bareword(stmts: &mut [Statement]) {
1958 let [only] = stmts else { return };
1959 let StmtKind::Expression(ref mut expr) = only.kind else {
1960 return;
1961 };
1962 let topic_line = expr.line;
1963 let topic_arg = || Expr {
1964 kind: ExprKind::ScalarVar("_".to_string()),
1965 line: topic_line,
1966 };
1967 match expr.kind {
1968 ExprKind::FuncCall {
1970 ref name,
1971 ref mut args,
1972 } if args.is_empty()
1973 && (Self::is_known_bareword(name) || Self::is_try_builtin_name(name)) =>
1974 {
1975 args.push(topic_arg());
1976 }
1977 ExprKind::Bareword(ref name)
1981 if (Self::is_known_bareword(name) || Self::is_try_builtin_name(name)) =>
1982 {
1983 let n = name.clone();
1984 expr.kind = ExprKind::FuncCall {
1985 name: n,
1986 args: vec![topic_arg()],
1987 };
1988 }
1989 _ => {}
1990 }
1991 }
1992
1993 fn parse_defer_stmt(&mut self) -> StrykeResult<Statement> {
1997 let line = self.peek_line();
1998 self.advance(); let body = self.parse_block()?;
2000 self.eat(&Token::Semicolon);
2001 let coderef = Expr {
2003 kind: ExprKind::CodeRef {
2004 params: vec![],
2005 body,
2006 },
2007 line,
2008 };
2009 Ok(Statement {
2010 label: None,
2011 kind: StmtKind::Expression(Expr {
2012 kind: ExprKind::FuncCall {
2013 name: "defer__internal".to_string(),
2014 args: vec![coderef],
2015 },
2016 line,
2017 }),
2018 line,
2019 })
2020 }
2021
2022 fn parse_try_catch(&mut self) -> StrykeResult<Statement> {
2024 let line = self.peek_line();
2025 self.advance(); let try_block = self.parse_block()?;
2027 match self.peek() {
2028 Token::Ident(ref k) if k == "catch" => {
2029 self.advance();
2030 }
2031 _ => {
2032 return Err(self.syntax_err("expected 'catch' after try block", self.peek_line()));
2033 }
2034 }
2035 self.expect(&Token::LParen)?;
2036 let catch_var = self.parse_scalar_var_name()?;
2037 self.expect(&Token::RParen)?;
2038 let catch_block = self.parse_block()?;
2039 let finally_block = match self.peek() {
2040 Token::Ident(ref k) if k == "finally" => {
2041 self.advance();
2042 Some(self.parse_block()?)
2043 }
2044 _ => None,
2045 };
2046 self.eat(&Token::Semicolon);
2047 Ok(Statement {
2048 label: None,
2049 kind: StmtKind::TryCatch {
2050 try_block,
2051 catch_var,
2052 catch_block,
2053 finally_block,
2054 },
2055 line,
2056 })
2057 }
2058
2059 fn parse_thread_macro(&mut self, _line: usize, thread_last: bool) -> StrykeResult<Expr> {
2073 self.parse_thread_macro_inner(_line, thread_last, None)
2074 }
2075
2076 fn parse_thread_macro_inner(
2085 &mut self,
2086 _line: usize,
2087 thread_last: bool,
2088 mut parallel_collector: Option<&mut Vec<Expr>>,
2089 ) -> StrykeResult<Expr> {
2090 let saved_thread_last = self.thread_last_mode;
2092 self.thread_last_mode = thread_last;
2093
2094 let pipe_rhs_wrap = self.in_pipe_rhs();
2095 let mut result = if let Some(pre) = self.pending_thread_input.take() {
2099 pre
2100 } else if pipe_rhs_wrap {
2101 Expr {
2102 kind: ExprKind::ArrayElement {
2103 array: "_".to_string(),
2104 index: Box::new(Expr {
2105 kind: ExprKind::Integer(0),
2106 line: _line,
2107 }),
2108 },
2109 line: _line,
2110 }
2111 } else {
2112 self.suppress_parenless_call = self.suppress_parenless_call.saturating_add(1);
2115 let expr = self.parse_thread_input();
2116 self.suppress_parenless_call = self.suppress_parenless_call.saturating_sub(1);
2117 expr?
2118 };
2119 let source_for_par = if parallel_collector.is_some() {
2123 let src = std::mem::replace(
2124 &mut result,
2125 Expr {
2126 kind: ExprKind::ScalarVar("_".into()),
2127 line: _line,
2128 },
2129 );
2130 Some(src)
2131 } else {
2132 None
2133 };
2134
2135 let mut last_stage_end_line = self.prev_line();
2137
2138 loop {
2140 if self.peek_line() > last_stage_end_line {
2145 break;
2146 }
2147
2148 match self.peek() {
2152 Token::Semicolon
2153 | Token::RBrace
2154 | Token::RParen
2155 | Token::RBracket
2156 | Token::PipeForward
2157 | Token::Eof
2158 | Token::ScalarVar(_)
2159 | Token::ArrayVar(_)
2160 | Token::HashVar(_)
2161 | Token::Comma => break,
2162 Token::LogOr if matches!(self.peek_at(1), Token::NumGt) => break,
2167 Token::BitOr
2169 if matches!(self.peek_at(1), Token::Ident(ref n) if n == "then")
2170 && matches!(self.peek_at(2), Token::BitOr) =>
2171 {
2172 break
2173 }
2174 Token::Ident(ref kw)
2175 if matches!(
2176 kw.as_str(),
2177 "my" | "our"
2178 | "local"
2179 | "state"
2180 | "if"
2181 | "unless"
2182 | "while"
2183 | "until"
2184 | "for"
2185 | "foreach"
2186 | "return"
2187 | "last"
2188 | "next"
2189 | "redo"
2190 ) =>
2191 {
2192 break
2193 }
2194 _ => {}
2195 }
2196
2197 let stage_line = self.peek_line();
2198
2199 match self.peek().clone() {
2201 Token::ArrowBrace => {
2203 self.advance(); let mut stmts = Vec::new();
2205 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
2206 if self.eat(&Token::Semicolon) {
2207 continue;
2208 }
2209 stmts.push(self.parse_statement()?);
2210 }
2211 self.expect(&Token::RBrace)?;
2212 let code_ref = Expr {
2213 kind: ExprKind::CodeRef {
2214 params: vec![],
2215 body: stmts,
2216 },
2217 line: stage_line,
2218 };
2219 result = self.pipe_forward_apply(result, code_ref, stage_line)?;
2220 }
2221 Token::Ident(ref name) if name == "sub" => {
2223 if crate::no_interop_mode() {
2224 return Err(self.syntax_err(
2225 "stryke uses `fn {}` instead of `sub {}` (--no-interop)",
2226 stage_line,
2227 ));
2228 }
2229 self.advance(); let (params, _prototype) = self.parse_sub_sig_or_prototype_opt()?;
2231 let body = self.parse_block()?;
2232 let code_ref = Expr {
2233 kind: ExprKind::CodeRef { params, body },
2234 line: stage_line,
2235 };
2236 result = self.pipe_forward_apply(result, code_ref, stage_line)?;
2237 }
2238 Token::Ident(ref name) if name == "fn" => {
2240 self.advance(); let (params, _prototype) = self.parse_sub_sig_or_prototype_opt()?;
2242 self.parse_sub_attributes()?;
2243 let body = self.parse_fn_eq_body_or_block(false)?;
2244 let code_ref = Expr {
2245 kind: ExprKind::CodeRef { params, body },
2246 line: stage_line,
2247 };
2248 result = self.pipe_forward_apply(result, code_ref, stage_line)?;
2249 }
2250 Token::Ident(ref name) => {
2252 let mut func_name = name.clone();
2253 self.advance();
2254
2255 while matches!(self.peek(), Token::PackageSep) {
2257 self.advance(); if let Token::Ident(ref part) = self.peek().clone() {
2259 func_name.push_str("::");
2260 func_name.push_str(part);
2261 self.advance();
2262 } else {
2263 return Err(self.syntax_err(
2264 format!(
2265 "Expected identifier after `::` in thread stage, got {:?}",
2266 self.peek()
2267 ),
2268 stage_line,
2269 ));
2270 }
2271 }
2272
2273 if func_name.starts_with('\x00') {
2275 let parts: Vec<&str> = func_name.split('\x00').collect();
2276 if parts.len() >= 4 && parts[1] == "s" {
2277 let delim = parts.get(5).and_then(|s| s.chars().next()).unwrap_or('/');
2278 let stage = Expr {
2279 kind: ExprKind::Substitution {
2280 expr: Box::new(result.clone()),
2281 pattern: parts[2].to_string(),
2282 replacement: parts[3].to_string(),
2283 flags: format!("{}r", parts.get(4).unwrap_or(&"")),
2284 delim,
2285 },
2286 line: stage_line,
2287 };
2288 result = stage;
2289 last_stage_end_line = self.prev_line();
2290 continue;
2291 }
2292 if parts.len() >= 4 && parts[1] == "tr" {
2293 let delim = parts.get(5).and_then(|s| s.chars().next()).unwrap_or('/');
2294 let stage = Expr {
2295 kind: ExprKind::Transliterate {
2296 expr: Box::new(result.clone()),
2297 from: parts[2].to_string(),
2298 to: parts[3].to_string(),
2299 flags: format!("{}r", parts.get(4).unwrap_or(&"")),
2300 delim,
2301 },
2302 line: stage_line,
2303 };
2304 result = stage;
2305 last_stage_end_line = self.prev_line();
2306 continue;
2307 }
2308 return Err(
2309 self.syntax_err("Unexpected encoded token in thread", stage_line)
2310 );
2311 }
2312
2313 if matches!(self.peek(), Token::Plus)
2318 && matches!(self.peek_at(1), Token::LBrace)
2319 {
2320 self.advance(); self.expect(&Token::LBrace)?;
2322 let pairs = self.try_parse_hash_ref()?;
2324 let hashref_expr = Expr {
2325 kind: ExprKind::HashRef(pairs),
2326 line: stage_line,
2327 };
2328 let flatten_array_refs =
2329 matches!(func_name.as_str(), "flat_map" | "flat_maps");
2330 let stream = matches!(func_name.as_str(), "maps" | "flat_maps");
2331 let placeholder = Expr {
2333 kind: ExprKind::Undef,
2334 line: stage_line,
2335 };
2336 let map_node = Expr {
2337 kind: ExprKind::MapExprComma {
2338 expr: Box::new(hashref_expr),
2339 list: Box::new(placeholder),
2340 flatten_array_refs,
2341 stream,
2342 },
2343 line: stage_line,
2344 };
2345 result = self.pipe_forward_apply(result, map_node, stage_line)?;
2346 } else if func_name == "pmap_chunked" {
2348 let chunk_size = self.parse_assign_expr()?;
2349 let block = self.parse_block_or_bareword_block()?;
2350 let placeholder = self.pipe_placeholder_list(stage_line);
2351 let stage = Expr {
2352 kind: ExprKind::PMapChunkedExpr {
2353 chunk_size: Box::new(chunk_size),
2354 block,
2355 list: Box::new(placeholder),
2356 progress: None,
2357 },
2358 line: stage_line,
2359 };
2360 result = self.pipe_forward_apply(result, stage, stage_line)?;
2361 } else if func_name == "preduce_init" {
2363 let init = self.parse_assign_expr()?;
2364 let block = self.parse_block_or_bareword_block()?;
2365 let placeholder = self.pipe_placeholder_list(stage_line);
2366 let stage = Expr {
2367 kind: ExprKind::PReduceInitExpr {
2368 init: Box::new(init),
2369 block,
2370 list: Box::new(placeholder),
2371 progress: None,
2372 },
2373 line: stage_line,
2374 };
2375 result = self.pipe_forward_apply(result, stage, stage_line)?;
2376 } else if func_name == "pmap_reduce" {
2378 let map_block = self.parse_block_or_bareword_block()?;
2379 let reduce_block = if matches!(self.peek(), Token::LBrace) {
2380 self.parse_block()?
2381 } else {
2382 self.expect(&Token::Comma)?;
2383 self.parse_block_or_bareword_cmp_block()?
2384 };
2385 let placeholder = self.pipe_placeholder_list(stage_line);
2386 let stage = Expr {
2387 kind: ExprKind::PMapReduceExpr {
2388 map_block,
2389 reduce_block,
2390 list: Box::new(placeholder),
2391 progress: None,
2392 },
2393 line: stage_line,
2394 };
2395 result = self.pipe_forward_apply(result, stage, stage_line)?;
2396 } else if func_name == "par_reduce" {
2401 let extract_block = self.parse_block_or_bareword_block()?;
2402 let reduce_block = if matches!(self.peek(), Token::LBrace) {
2403 Some(self.parse_block()?)
2404 } else {
2405 None
2406 };
2407 let placeholder = self.pipe_placeholder_list(stage_line);
2408 let stage = Expr {
2409 kind: ExprKind::ParReduceExpr {
2410 extract_block,
2411 reduce_block,
2412 list: Box::new(placeholder),
2413 },
2414 line: stage_line,
2415 };
2416 result = self.pipe_forward_apply(result, stage, stage_line)?;
2417 } else if func_name == "pmap_on" || func_name == "pflat_map_on" {
2422 self.suppress_scalar_hash_brace =
2425 self.suppress_scalar_hash_brace.saturating_add(1);
2426 let cluster = self.parse_assign_expr();
2427 self.suppress_scalar_hash_brace =
2428 self.suppress_scalar_hash_brace.saturating_sub(1);
2429 let cluster = cluster?;
2430 self.eat(&Token::Comma);
2433 let block = self.parse_block_or_bareword_block()?;
2434 let placeholder = self.pipe_placeholder_list(stage_line);
2435 let stage = Expr {
2436 kind: ExprKind::PMapExpr {
2437 block,
2438 list: Box::new(placeholder),
2439 progress: None,
2440 flat_outputs: func_name == "pflat_map_on",
2441 on_cluster: Some(Box::new(cluster)),
2442 stream: false,
2443 },
2444 line: stage_line,
2445 };
2446 result = self.pipe_forward_apply(result, stage, stage_line)?;
2447 } else if matches!(self.peek(), Token::LBrace) {
2449 self.pipe_rhs_depth = self.pipe_rhs_depth.saturating_add(1);
2451 let stage = self.parse_thread_stage_with_block(&func_name, stage_line)?;
2452 self.pipe_rhs_depth = self.pipe_rhs_depth.saturating_sub(1);
2453 result = self.pipe_forward_apply(result, stage, stage_line)?;
2454 } else if matches!(self.peek(), Token::LParen) {
2455 if func_name == "join" {
2458 self.advance(); let separator = self.parse_assign_expr()?;
2460 self.expect(&Token::RParen)?;
2461 let placeholder = self.pipe_placeholder_list(stage_line);
2462 let stage = Expr {
2463 kind: ExprKind::JoinExpr {
2464 separator: Box::new(separator),
2465 list: Box::new(placeholder),
2466 },
2467 line: stage_line,
2468 };
2469 result = self.pipe_forward_apply(result, stage, stage_line)?;
2470 } else if func_name == "split" {
2471 self.advance(); let pattern = self.parse_assign_expr()?;
2473 let limit = if self.eat(&Token::Comma) {
2474 Some(Box::new(self.parse_assign_expr()?))
2475 } else {
2476 None
2477 };
2478 self.expect(&Token::RParen)?;
2479 let placeholder = Expr {
2480 kind: ExprKind::ScalarVar("_".to_string()),
2481 line: stage_line,
2482 };
2483 let stage = Expr {
2484 kind: ExprKind::SplitExpr {
2485 pattern: Box::new(pattern),
2486 string: Box::new(placeholder),
2487 limit,
2488 },
2489 line: stage_line,
2490 };
2491 result = self.pipe_forward_apply(result, stage, stage_line)?;
2492 } else {
2493 self.advance(); let mut call_args = Vec::new();
2504 while !matches!(self.peek(), Token::RParen | Token::Eof) {
2505 call_args.push(self.parse_assign_expr()?);
2506 if !self.eat(&Token::Comma) {
2507 break;
2508 }
2509 }
2510 self.expect(&Token::RParen)?;
2511 if !call_args.iter().any(Self::expr_contains_topic_var) {
2515 let topic = Expr {
2516 kind: ExprKind::ScalarVar("_".to_string()),
2517 line: stage_line,
2518 };
2519 if self.thread_last_mode {
2520 call_args.push(topic);
2521 } else {
2522 call_args.insert(0, topic);
2523 }
2524 }
2525 let call_expr = Expr {
2526 kind: ExprKind::FuncCall {
2527 name: func_name.clone(),
2528 args: call_args,
2529 },
2530 line: stage_line,
2531 };
2532 let code_ref = Expr {
2533 kind: ExprKind::CodeRef {
2534 params: vec![],
2535 body: vec![Statement {
2536 label: None,
2537 kind: StmtKind::Expression(call_expr),
2538 line: stage_line,
2539 }],
2540 },
2541 line: stage_line,
2542 };
2543 result = self.pipe_forward_apply(result, code_ref, stage_line)?;
2544 }
2545 } else {
2546 result = self.thread_apply_bare_func(&func_name, result, stage_line)?;
2548 }
2549 }
2550 Token::Regex(ref pattern, ref flags, delim) => {
2552 let pattern = pattern.clone();
2553 let flags = flags.clone();
2554 self.advance();
2555 result =
2556 self.thread_regex_grep_stage(result, pattern, flags, delim, stage_line);
2557 }
2558 Token::Slash => {
2561 self.advance(); if let Token::Ident(ref ident_s) = self.peek().clone() {
2567 if matches!(ident_s.as_str(), "m" | "s" | "tr" | "y" | "qr")
2568 && matches!(self.peek_at(1), Token::Regex(..))
2569 {
2570 self.advance(); if let Token::Regex(ref misparsed_pattern, ref misparsed_flags, _) =
2577 self.peek().clone()
2578 {
2579 let _ = (misparsed_pattern, misparsed_flags);
2589 }
2590 }
2591 }
2592
2593 let mut pattern = String::new();
2595 loop {
2596 match self.peek().clone() {
2597 Token::Slash => {
2598 self.advance(); break;
2600 }
2601 Token::Eof | Token::Semicolon | Token::Newline => {
2602 return Err(self
2603 .syntax_err("Unterminated regex in thread stage", stage_line));
2604 }
2605 Token::Regex(ref inner_pattern, ref inner_flags, delim) => {
2607 if pattern.is_empty()
2615 || matches!(pattern.as_str(), "m" | "s" | "tr" | "y" | "qr")
2616 {
2617 let _ = (inner_pattern, inner_flags, delim);
2623 }
2624 return Err(self.syntax_err(
2626 "Complex regex in thread stage - use m/pattern/ syntax instead",
2627 stage_line,
2628 ));
2629 }
2630 Token::Ident(ref s) => {
2631 pattern.push_str(s);
2632 self.advance();
2633 }
2634 Token::Integer(n) => {
2635 pattern.push_str(&n.to_string());
2636 self.advance();
2637 }
2638 Token::ScalarVar(ref v) => {
2639 pattern.push('$');
2640 pattern.push_str(v);
2641 self.advance();
2642 }
2643 Token::Dot => {
2644 pattern.push('.');
2645 self.advance();
2646 }
2647 Token::Star => {
2648 pattern.push('*');
2649 self.advance();
2650 }
2651 Token::Plus => {
2652 pattern.push('+');
2653 self.advance();
2654 }
2655 Token::Question => {
2656 pattern.push('?');
2657 self.advance();
2658 }
2659 Token::LParen => {
2660 pattern.push('(');
2661 self.advance();
2662 }
2663 Token::RParen => {
2664 pattern.push(')');
2665 self.advance();
2666 }
2667 Token::LBracket => {
2668 pattern.push('[');
2669 self.advance();
2670 }
2671 Token::RBracket => {
2672 pattern.push(']');
2673 self.advance();
2674 }
2675 Token::Backslash => {
2676 pattern.push('\\');
2677 self.advance();
2678 }
2679 Token::BitOr => {
2680 pattern.push('|');
2681 self.advance();
2682 }
2683 Token::Power => {
2684 pattern.push_str("**");
2685 self.advance();
2686 }
2687 Token::BitXor => {
2688 pattern.push('^');
2689 self.advance();
2690 }
2691 Token::Minus => {
2692 pattern.push('-');
2693 self.advance();
2694 }
2695 _ => {
2696 return Err(self.syntax_err(
2697 format!("Unexpected token in regex pattern: {:?}", self.peek()),
2698 stage_line,
2699 ));
2700 }
2701 }
2702 }
2703 let mut flags = String::new();
2707 if let Token::Ident(ref s) = self.peek().clone() {
2708 let is_flag_only =
2709 s.chars().all(|c| "gimsxecor".contains(c)) && s.len() <= 6;
2710 let followed_by_brace = matches!(self.peek_at(1), Token::LBrace);
2711 if is_flag_only && !followed_by_brace {
2712 flags.push_str(s);
2713 self.advance();
2714 }
2715 }
2716 result = self.thread_regex_grep_stage(result, pattern, flags, '/', stage_line);
2717 }
2718 tok => {
2719 return Err(self.syntax_err(
2720 format!(
2721 "thread: expected stage (ident, fn {{}}, s///, tr///, or /re/), got {:?}",
2722 tok
2723 ),
2724 stage_line,
2725 ));
2726 }
2727 };
2728 last_stage_end_line = self.prev_line();
2729 if let Some(stages) = parallel_collector.as_mut() {
2734 let stage_body = std::mem::replace(
2735 &mut result,
2736 Expr {
2737 kind: ExprKind::ScalarVar("_".into()),
2738 line: stage_line,
2739 },
2740 );
2741 stages.push(stage_body);
2742 }
2743 }
2744
2745 self.thread_last_mode = saved_thread_last;
2747
2748 if let Some(stages) = parallel_collector {
2754 let source_expr = source_for_par.unwrap_or(result);
2755 if stages.is_empty() {
2756 return Err(self.syntax_err(
2757 "~p> / ~p>> require at least one stage after the source",
2758 _line,
2759 ));
2760 }
2761 let stage_closures: Vec<Expr> = stages
2767 .drain(..)
2768 .map(|body| {
2769 let body_line = body.line;
2770 let wrapped = Expr {
2771 kind: ExprKind::ArrayRef(vec![body]),
2772 line: body_line,
2773 };
2774 Expr {
2775 kind: ExprKind::CodeRef {
2776 params: vec![],
2777 body: vec![Statement {
2778 label: None,
2779 kind: StmtKind::Expression(wrapped),
2780 line: body_line,
2781 }],
2782 },
2783 line: body_line,
2784 }
2785 })
2786 .collect();
2787 let stages_arr = Expr {
2788 kind: ExprKind::ArrayRef(stage_closures),
2789 line: _line,
2790 };
2791 let thread_last_flag = Expr {
2792 kind: ExprKind::Integer(if thread_last { 1 } else { 0 }),
2793 line: _line,
2794 };
2795 return Ok(Expr {
2802 kind: ExprKind::FuncCall {
2803 name: "_thread_par_run".into(),
2804 args: vec![stages_arr, thread_last_flag, source_expr],
2805 },
2806 line: _line,
2807 });
2808 }
2809
2810 if pipe_rhs_wrap {
2811 let body_line = result.line;
2814 return Ok(Expr {
2815 kind: ExprKind::CodeRef {
2816 params: vec![],
2817 body: vec![Statement {
2818 label: None,
2819 kind: StmtKind::Expression(result),
2820 line: body_line,
2821 }],
2822 },
2823 line: _line,
2824 });
2825 }
2826 Ok(result)
2827 }
2828
2829 fn thread_regex_grep_stage(
2831 &self,
2832 list: Expr,
2833 pattern: String,
2834 flags: String,
2835 delim: char,
2836 line: usize,
2837 ) -> Expr {
2838 let topic = Expr {
2839 kind: ExprKind::ScalarVar("_".to_string()),
2840 line,
2841 };
2842 let match_expr = Expr {
2843 kind: ExprKind::Match {
2844 expr: Box::new(topic),
2845 pattern,
2846 flags,
2847 scalar_g: false,
2848 delim,
2849 },
2850 line,
2851 };
2852 let block = vec![Statement {
2853 label: None,
2854 kind: StmtKind::Expression(match_expr),
2855 line,
2856 }];
2857 Expr {
2858 kind: ExprKind::GrepExpr {
2859 block,
2860 list: Box::new(list),
2861 keyword: crate::ast::GrepBuiltinKeyword::Grep,
2862 },
2863 line,
2864 }
2865 }
2866
2867 fn expr_contains_topic_var(e: &Expr) -> bool {
2878 format!("{:?}", e).contains("ScalarVar(\"_\")")
2879 }
2880
2881 fn rhs_has_free_bare_topic_slot(&self, rhs_start: usize, rhs_end: usize) -> bool {
2897 let end = rhs_end.min(self.tokens.len());
2898 if rhs_start < end && Self::is_thread_arrow(&self.tokens[rhs_start].0) {
2899 let input = rhs_start + 1;
2903 return input < end && self.bare_positional_indices.contains(&input);
2904 }
2905 let mut brace_depth = 0i32;
2906 for i in rhs_start..end {
2907 if brace_depth == 0 && self.bare_positional_indices.contains(&i) {
2908 return true;
2909 }
2910 match &self.tokens[i].0 {
2911 Token::LBrace | Token::ArrowBrace => brace_depth += 1,
2912 Token::RBrace => brace_depth -= 1,
2913 _ => {}
2914 }
2915 }
2916 false
2917 }
2918
2919 fn is_thread_arrow(tok: &Token) -> bool {
2920 matches!(
2921 tok,
2922 Token::ThreadArrow
2923 | Token::ThreadArrowLast
2924 | Token::ThreadArrowStream
2925 | Token::ThreadArrowStreamLast
2926 | Token::ThreadArrowPar
2927 | Token::ThreadArrowParLast
2928 | Token::ThreadArrowDist
2929 | Token::ThreadArrowDistLast
2930 )
2931 }
2932
2933 fn thread_apply_bare_func(&self, name: &str, arg: Expr, line: usize) -> StrykeResult<Expr> {
2935 let kind = match name {
2936 "uc" => ExprKind::Uc(Box::new(arg)),
2938 "lc" => ExprKind::Lc(Box::new(arg)),
2939 "ucfirst" | "ufc" => ExprKind::Ucfirst(Box::new(arg)),
2940 "lcfirst" | "lfc" => ExprKind::Lcfirst(Box::new(arg)),
2941 "fc" => ExprKind::Fc(Box::new(arg)),
2942 "chomp" => ExprKind::Chomp(Box::new(arg)),
2943 "chop" => ExprKind::Chop(Box::new(arg)),
2944 "length" => ExprKind::Length(Box::new(arg)),
2945 "len" | "cnt" => ExprKind::FuncCall {
2946 name: "count".to_string(),
2947 args: vec![arg],
2948 },
2949 "quotemeta" | "qm" => ExprKind::FuncCall {
2950 name: "quotemeta".to_string(),
2951 args: vec![arg],
2952 },
2953 "abs" => ExprKind::Abs(Box::new(arg)),
2955 "int" => ExprKind::Int(Box::new(arg)),
2956 "sqrt" | "sq" => ExprKind::Sqrt(Box::new(arg)),
2957 "sin" => ExprKind::Sin(Box::new(arg)),
2958 "cos" => ExprKind::Cos(Box::new(arg)),
2959 "exp" => ExprKind::Exp(Box::new(arg)),
2960 "log" => ExprKind::Log(Box::new(arg)),
2961 "hex" => ExprKind::Hex(Box::new(arg)),
2962 "oct" => ExprKind::Oct(Box::new(arg)),
2963 "chr" => ExprKind::Chr(Box::new(arg)),
2964 "ord" => ExprKind::Ord(Box::new(arg)),
2965 "rand" => ExprKind::Rand(Some(Box::new(arg))),
2966 "srand" => ExprKind::Srand(Some(Box::new(arg))),
2967 "defined" | "def" => ExprKind::Defined(Box::new(arg)),
2969 "ref" => ExprKind::Ref(Box::new(arg)),
2970 "scalar" => {
2971 if crate::no_interop_mode() {
2972 return Err(self.syntax_err(
2973 "stryke uses `len` (also `cnt` / `count`) instead of `scalar` (--no-interop)",
2974 line,
2975 ));
2976 }
2977 ExprKind::ScalarContext(Box::new(arg))
2978 }
2979 "keys" => ExprKind::Keys(Box::new(arg)),
2981 "values" => ExprKind::Values(Box::new(arg)),
2982 "each" => ExprKind::Each(Box::new(arg)),
2983 "pop" => ExprKind::Pop(Box::new(arg)),
2984 "shift" => ExprKind::Shift(Box::new(arg)),
2985 "reverse" => {
2986 if crate::no_interop_mode() {
2987 return Err(self.syntax_err(
2988 "stryke uses `rev` instead of `reverse` (--no-interop)",
2989 line,
2990 ));
2991 }
2992 ExprKind::ReverseExpr(Box::new(arg))
2993 }
2994 "reversed" | "rv" | "rev" => ExprKind::Rev(Box::new(arg)),
2995 "sort" | "so" => ExprKind::SortExpr {
2996 cmp: None,
2997 list: Box::new(arg),
2998 },
2999 "psort" => ExprKind::PSortExpr {
3000 cmp: None,
3001 list: Box::new(arg),
3002 progress: None,
3003 },
3004 "uniq" | "distinct" | "uq" => ExprKind::FuncCall {
3005 name: "uniq".to_string(),
3006 args: vec![arg],
3007 },
3008 "trim" | "tm" => ExprKind::FuncCall {
3009 name: "trim".to_string(),
3010 args: vec![arg],
3011 },
3012 "flatten" | "fl" => ExprKind::FuncCall {
3013 name: "flatten".to_string(),
3014 args: vec![arg],
3015 },
3016 "compact" | "cpt" => ExprKind::FuncCall {
3017 name: "compact".to_string(),
3018 args: vec![arg],
3019 },
3020 "shuffle" | "shuf" => ExprKind::FuncCall {
3021 name: "shuffle".to_string(),
3022 args: vec![arg],
3023 },
3024 "frequencies" | "freq" | "frq" => ExprKind::FuncCall {
3025 name: "frequencies".to_string(),
3026 args: vec![arg],
3027 },
3028 "pfrequencies" | "pfreq" | "pfrq" => ExprKind::FuncCall {
3029 name: "pfrequencies".to_string(),
3030 args: vec![arg],
3031 },
3032 "dedup" | "dup" => ExprKind::FuncCall {
3033 name: "dedup".to_string(),
3034 args: vec![arg],
3035 },
3036 "enumerate" | "en" => ExprKind::FuncCall {
3037 name: "enumerate".to_string(),
3038 args: vec![arg],
3039 },
3040 "lines" | "ln" => ExprKind::FuncCall {
3041 name: "lines".to_string(),
3042 args: vec![arg],
3043 },
3044 "words" | "wd" => ExprKind::FuncCall {
3045 name: "words".to_string(),
3046 args: vec![arg],
3047 },
3048 "chars" | "ch" => ExprKind::FuncCall {
3049 name: "chars".to_string(),
3050 args: vec![arg],
3051 },
3052 "digits" | "dg" => ExprKind::FuncCall {
3053 name: "digits".to_string(),
3054 args: vec![arg],
3055 },
3056 "letters" | "lts" => ExprKind::FuncCall {
3057 name: "letters".to_string(),
3058 args: vec![arg],
3059 },
3060 "letters_uc" => ExprKind::FuncCall {
3061 name: "letters_uc".to_string(),
3062 args: vec![arg],
3063 },
3064 "letters_lc" => ExprKind::FuncCall {
3065 name: "letters_lc".to_string(),
3066 args: vec![arg],
3067 },
3068 "punctuation" | "punct" => ExprKind::FuncCall {
3069 name: "punctuation".to_string(),
3070 args: vec![arg],
3071 },
3072 "sentences" | "sents" => ExprKind::FuncCall {
3073 name: "sentences".to_string(),
3074 args: vec![arg],
3075 },
3076 "paragraphs" | "paras" => ExprKind::FuncCall {
3077 name: "paragraphs".to_string(),
3078 args: vec![arg],
3079 },
3080 "sections" | "sects" => ExprKind::FuncCall {
3081 name: "sections".to_string(),
3082 args: vec![arg],
3083 },
3084 "numbers" | "nums" => ExprKind::FuncCall {
3085 name: "numbers".to_string(),
3086 args: vec![arg],
3087 },
3088 "graphemes" | "grs" => ExprKind::FuncCall {
3089 name: "graphemes".to_string(),
3090 args: vec![arg],
3091 },
3092 "columns" | "cols" => ExprKind::FuncCall {
3093 name: "columns".to_string(),
3094 args: vec![arg],
3095 },
3096 "slurp" | "sl" => ExprKind::Slurp(Box::new(arg)),
3098 "glob" => ExprKind::Glob(vec![arg]),
3099 "chdir" => ExprKind::Chdir(Box::new(arg)),
3100 "stat" => ExprKind::Stat(Box::new(arg)),
3101 "lstat" => ExprKind::Lstat(Box::new(arg)),
3102 "readlink" => ExprKind::Readlink(Box::new(arg)),
3103 "readdir" => ExprKind::Readdir(Box::new(arg)),
3104 "close" => ExprKind::Close(Box::new(arg)),
3105 "basename" | "bn" => ExprKind::FuncCall {
3106 name: "basename".to_string(),
3107 args: vec![arg],
3108 },
3109 "dirname" | "dn" => ExprKind::FuncCall {
3110 name: "dirname".to_string(),
3111 args: vec![arg],
3112 },
3113 "realpath" | "rp" => ExprKind::FuncCall {
3114 name: "realpath".to_string(),
3115 args: vec![arg],
3116 },
3117 "which" | "wh" => ExprKind::FuncCall {
3118 name: "which".to_string(),
3119 args: vec![arg],
3120 },
3121 "eval" => ExprKind::Eval(Box::new(arg)),
3123 "require" => ExprKind::Require(Box::new(arg)),
3124 "study" => ExprKind::Study(Box::new(arg)),
3125 "snake_case" | "sc" => ExprKind::FuncCall {
3127 name: "snake_case".to_string(),
3128 args: vec![arg],
3129 },
3130 "camel_case" | "cc" => ExprKind::FuncCall {
3131 name: "camel_case".to_string(),
3132 args: vec![arg],
3133 },
3134 "kebab_case" | "kc" => ExprKind::FuncCall {
3135 name: "kebab_case".to_string(),
3136 args: vec![arg],
3137 },
3138 "to_json" | "tj" => ExprKind::FuncCall {
3140 name: "to_json".to_string(),
3141 args: vec![arg],
3142 },
3143 "to_yaml" | "ty" => ExprKind::FuncCall {
3144 name: "to_yaml".to_string(),
3145 args: vec![arg],
3146 },
3147 "to_toml" | "tt" => ExprKind::FuncCall {
3148 name: "to_toml".to_string(),
3149 args: vec![arg],
3150 },
3151 "to_csv" | "tc" => ExprKind::FuncCall {
3152 name: "to_csv".to_string(),
3153 args: vec![arg],
3154 },
3155 "to_xml" | "tx" => ExprKind::FuncCall {
3156 name: "to_xml".to_string(),
3157 args: vec![arg],
3158 },
3159 "to_html" | "th" => ExprKind::FuncCall {
3160 name: "to_html".to_string(),
3161 args: vec![arg],
3162 },
3163 "to_markdown" | "to_md" | "tmd" => ExprKind::FuncCall {
3164 name: "to_markdown".to_string(),
3165 args: vec![arg],
3166 },
3167 "xopen" | "xo" => ExprKind::FuncCall {
3168 name: "xopen".to_string(),
3169 args: vec![arg],
3170 },
3171 "clip" | "clipboard" | "pbcopy" => ExprKind::FuncCall {
3172 name: "clip".to_string(),
3173 args: vec![arg],
3174 },
3175 "to_table" | "table" | "tbl" => ExprKind::FuncCall {
3176 name: "to_table".to_string(),
3177 args: vec![arg],
3178 },
3179 "sparkline" | "spark" => ExprKind::FuncCall {
3180 name: "sparkline".to_string(),
3181 args: vec![arg],
3182 },
3183 "bar_chart" | "bars" => ExprKind::FuncCall {
3184 name: "bar_chart".to_string(),
3185 args: vec![arg],
3186 },
3187 "flame" | "flamechart" => ExprKind::FuncCall {
3188 name: "flame".to_string(),
3189 args: vec![arg],
3190 },
3191 "ddump" | "dd" => ExprKind::FuncCall {
3192 name: "ddump".to_string(),
3193 args: vec![arg],
3194 },
3195 "say" => {
3196 if crate::no_interop_mode() {
3197 return Err(
3198 self.syntax_err("stryke uses `p` instead of `say` (--no-interop)", line)
3199 );
3200 }
3201 ExprKind::Say {
3202 handle: None,
3203 args: vec![arg],
3204 }
3205 }
3206 "p" => ExprKind::Say {
3207 handle: None,
3208 args: vec![arg],
3209 },
3210 "print" => ExprKind::Print {
3211 handle: None,
3212 args: vec![arg],
3213 },
3214 "warn" => ExprKind::Warn(vec![arg]),
3215 "die" => ExprKind::Die(vec![arg]),
3216 "stringify" | "str" => ExprKind::FuncCall {
3217 name: "stringify".to_string(),
3218 args: vec![arg],
3219 },
3220 "json_decode" | "jd" => ExprKind::FuncCall {
3221 name: "json_decode".to_string(),
3222 args: vec![arg],
3223 },
3224 "yaml_decode" | "yd" => ExprKind::FuncCall {
3225 name: "yaml_decode".to_string(),
3226 args: vec![arg],
3227 },
3228 "toml_decode" | "td" => ExprKind::FuncCall {
3229 name: "toml_decode".to_string(),
3230 args: vec![arg],
3231 },
3232 "xml_decode" | "xd" => ExprKind::FuncCall {
3233 name: "xml_decode".to_string(),
3234 args: vec![arg],
3235 },
3236 "json_encode" | "je" => ExprKind::FuncCall {
3237 name: "json_encode".to_string(),
3238 args: vec![arg],
3239 },
3240 "yaml_encode" | "ye" => ExprKind::FuncCall {
3241 name: "yaml_encode".to_string(),
3242 args: vec![arg],
3243 },
3244 "toml_encode" | "te" => ExprKind::FuncCall {
3245 name: "toml_encode".to_string(),
3246 args: vec![arg],
3247 },
3248 "xml_encode" | "xe" => ExprKind::FuncCall {
3249 name: "xml_encode".to_string(),
3250 args: vec![arg],
3251 },
3252 "base64_encode" | "b64e" => ExprKind::FuncCall {
3254 name: "base64_encode".to_string(),
3255 args: vec![arg],
3256 },
3257 "base64_decode" | "b64d" => ExprKind::FuncCall {
3258 name: "base64_decode".to_string(),
3259 args: vec![arg],
3260 },
3261 "hex_encode" | "hxe" => ExprKind::FuncCall {
3262 name: "hex_encode".to_string(),
3263 args: vec![arg],
3264 },
3265 "hex_decode" | "hxd" => ExprKind::FuncCall {
3266 name: "hex_decode".to_string(),
3267 args: vec![arg],
3268 },
3269 "url_encode" | "uri_escape" | "ue" => ExprKind::FuncCall {
3270 name: "url_encode".to_string(),
3271 args: vec![arg],
3272 },
3273 "url_decode" | "uri_unescape" | "ud" => ExprKind::FuncCall {
3274 name: "url_decode".to_string(),
3275 args: vec![arg],
3276 },
3277 "gzip" | "gz" => ExprKind::FuncCall {
3278 name: "gzip".to_string(),
3279 args: vec![arg],
3280 },
3281 "gunzip" | "ugz" => ExprKind::FuncCall {
3282 name: "gunzip".to_string(),
3283 args: vec![arg],
3284 },
3285 "zstd" | "zst" => ExprKind::FuncCall {
3286 name: "zstd".to_string(),
3287 args: vec![arg],
3288 },
3289 "zstd_decode" | "uzst" => ExprKind::FuncCall {
3290 name: "zstd_decode".to_string(),
3291 args: vec![arg],
3292 },
3293 "sha256" | "s256" => ExprKind::FuncCall {
3295 name: "sha256".to_string(),
3296 args: vec![arg],
3297 },
3298 "sha1" | "s1" => ExprKind::FuncCall {
3299 name: "sha1".to_string(),
3300 args: vec![arg],
3301 },
3302 "md5" | "m5" => ExprKind::FuncCall {
3303 name: "md5".to_string(),
3304 args: vec![arg],
3305 },
3306 "uuid" | "uid" => ExprKind::FuncCall {
3307 name: "uuid".to_string(),
3308 args: vec![arg],
3309 },
3310 "datetime_utc" | "utc" => ExprKind::FuncCall {
3312 name: "datetime_utc".to_string(),
3313 args: vec![arg],
3314 },
3315 "e" | "fore" | "ep" => ExprKind::ForEachExpr {
3318 block: vec![Statement {
3319 label: None,
3320 kind: StmtKind::Expression(Expr {
3321 kind: ExprKind::Say {
3322 handle: None,
3323 args: vec![Expr {
3324 kind: ExprKind::ScalarVar("_".into()),
3325 line,
3326 }],
3327 },
3328 line,
3329 }),
3330 line,
3331 }],
3332 list: Box::new(arg),
3333 },
3334 _ => ExprKind::FuncCall {
3336 name: name.to_string(),
3337 args: vec![arg],
3338 },
3339 };
3340 Ok(Expr { kind, line })
3341 }
3342
3343 fn parse_thread_stage_with_block(&mut self, name: &str, line: usize) -> StrykeResult<Expr> {
3346 let block = self.parse_block()?;
3347 let placeholder = self.pipe_placeholder_list(line);
3349
3350 match name {
3351 "map" | "flat_map" | "maps" | "flat_maps" => {
3352 let flatten_array_refs = matches!(name, "flat_map" | "flat_maps");
3353 let stream = matches!(name, "maps" | "flat_maps");
3354 Ok(Expr {
3355 kind: ExprKind::MapExpr {
3356 block,
3357 list: Box::new(placeholder),
3358 flatten_array_refs,
3359 stream,
3360 },
3361 line,
3362 })
3363 }
3364 "grep" | "greps" | "filter" | "fi" | "find_all" | "gr" => {
3365 let keyword = match name {
3366 "grep" | "gr" => crate::ast::GrepBuiltinKeyword::Grep,
3367 "greps" => crate::ast::GrepBuiltinKeyword::Greps,
3368 "filter" | "fi" => crate::ast::GrepBuiltinKeyword::Filter,
3369 "find_all" => crate::ast::GrepBuiltinKeyword::FindAll,
3370 _ => unreachable!(),
3371 };
3372 Ok(Expr {
3373 kind: ExprKind::GrepExpr {
3374 block,
3375 list: Box::new(placeholder),
3376 keyword,
3377 },
3378 line,
3379 })
3380 }
3381 "sort" | "so" => Ok(Expr {
3382 kind: ExprKind::SortExpr {
3383 cmp: Some(SortComparator::Block(block)),
3384 list: Box::new(placeholder),
3385 },
3386 line,
3387 }),
3388 "reduce" | "rd" => Ok(Expr {
3389 kind: ExprKind::ReduceExpr {
3390 block,
3391 list: Box::new(placeholder),
3392 },
3393 line,
3394 }),
3395 "fore" | "e" | "ep" => Ok(Expr {
3396 kind: ExprKind::ForEachExpr {
3397 block,
3398 list: Box::new(placeholder),
3399 },
3400 line,
3401 }),
3402 "par" => Ok(Expr {
3403 kind: ExprKind::ParExpr {
3404 block,
3405 list: Box::new(placeholder),
3406 },
3407 line,
3408 }),
3409 "pmap" | "pflat_map" | "pmaps" | "pflat_maps" => Ok(Expr {
3410 kind: ExprKind::PMapExpr {
3411 block,
3412 list: Box::new(placeholder),
3413 progress: None,
3414 flat_outputs: name == "pflat_map" || name == "pflat_maps",
3415 on_cluster: None,
3416 stream: name == "pmaps" || name == "pflat_maps",
3417 },
3418 line,
3419 }),
3420 "pgrep" | "pgreps" => Ok(Expr {
3421 kind: ExprKind::PGrepExpr {
3422 block,
3423 list: Box::new(placeholder),
3424 progress: None,
3425 stream: name == "pgreps",
3426 },
3427 line,
3428 }),
3429 "pfor" => Ok(Expr {
3430 kind: ExprKind::PForExpr {
3431 block,
3432 list: Box::new(placeholder),
3433 progress: None,
3434 },
3435 line,
3436 }),
3437 "preduce" => Ok(Expr {
3438 kind: ExprKind::PReduceExpr {
3439 block,
3440 list: Box::new(placeholder),
3441 progress: None,
3442 },
3443 line,
3444 }),
3445 "pcache" => Ok(Expr {
3446 kind: ExprKind::PcacheExpr {
3447 block,
3448 list: Box::new(placeholder),
3449 progress: None,
3450 },
3451 line,
3452 }),
3453 "psort" => Ok(Expr {
3454 kind: ExprKind::PSortExpr {
3455 cmp: Some(block),
3456 list: Box::new(placeholder),
3457 progress: None,
3458 },
3459 line,
3460 }),
3461 _ => {
3462 let code_ref = Expr {
3469 kind: ExprKind::CodeRef {
3470 params: vec![],
3471 body: block,
3472 },
3473 line,
3474 };
3475 let args = if Self::is_block_then_list_pipe_builtin(name) {
3476 vec![code_ref, placeholder]
3477 } else {
3478 vec![code_ref]
3479 };
3480 Ok(Expr {
3481 kind: ExprKind::FuncCall {
3482 name: name.to_string(),
3483 args,
3484 },
3485 line,
3486 })
3487 }
3488 }
3489 }
3490
3491 fn parse_tie_stmt(&mut self) -> StrykeResult<Statement> {
3493 let line = self.peek_line();
3494 self.advance(); let mut implicit_decl: Option<Statement> = None;
3501 if let Token::Ident(kw) = self.peek().clone() {
3502 if matches!(kw.as_str(), "my" | "our") {
3503 let kw_line = self.peek_line();
3504 self.advance(); let (decl_sigil, decl_name) = match self.peek().clone() {
3507 Token::ScalarVar(s) => (Sigil::Scalar, s),
3508 Token::ArrayVar(a) => (Sigil::Array, a),
3509 Token::HashVar(h) => (Sigil::Hash, h),
3510 tok => {
3511 return Err(self.syntax_err(
3512 format!("expected variable after `tie {}`, got {:?}", kw, tok),
3513 self.peek_line(),
3514 ));
3515 }
3516 };
3517 let decls = vec![VarDecl {
3518 sigil: decl_sigil,
3519 name: decl_name.clone(),
3520 initializer: None,
3521 frozen: false,
3522 type_annotation: None,
3523 list_context: false,
3524 }];
3525 implicit_decl = Some(Statement {
3526 label: None,
3527 kind: if kw == "my" {
3528 StmtKind::My(decls)
3529 } else {
3530 StmtKind::Our(decls)
3531 },
3532 line: kw_line,
3533 });
3534 }
3539 }
3540 let target = match self.peek().clone() {
3541 Token::HashVar(h) => {
3542 self.advance();
3543 TieTarget::Hash(h)
3544 }
3545 Token::ArrayVar(a) => {
3546 self.advance();
3547 TieTarget::Array(a)
3548 }
3549 Token::ScalarVar(s) => {
3550 self.advance();
3551 TieTarget::Scalar(s)
3552 }
3553 tok => {
3554 return Err(self.syntax_err(
3555 format!("tie expects $scalar, @array, or %hash, got {:?}", tok),
3556 self.peek_line(),
3557 ));
3558 }
3559 };
3560 self.expect(&Token::Comma)?;
3561 let class = self.parse_assign_expr()?;
3562 let mut args = Vec::new();
3563 while self.eat(&Token::Comma) {
3564 if matches!(self.peek(), Token::Semicolon | Token::RBrace | Token::Eof) {
3565 break;
3566 }
3567 args.push(self.parse_assign_expr()?);
3568 }
3569 self.eat(&Token::Semicolon);
3570 let tie_stmt = Statement {
3571 label: None,
3572 kind: StmtKind::Tie {
3573 target,
3574 class,
3575 args,
3576 },
3577 line,
3578 };
3579 if let Some(decl) = implicit_decl {
3580 Ok(Statement {
3585 label: None,
3586 kind: StmtKind::StmtGroup(vec![decl, tie_stmt]),
3587 line,
3588 })
3589 } else {
3590 Ok(tie_stmt)
3591 }
3592 }
3593
3594 fn parse_given(&mut self) -> StrykeResult<Statement> {
3596 let line = self.peek_line();
3597 self.advance();
3598 self.expect(&Token::LParen)?;
3599 let topic = self.parse_expression()?;
3600 self.expect(&Token::RParen)?;
3601 let body = self.parse_block()?;
3602 self.eat(&Token::Semicolon);
3603 Ok(Statement {
3604 label: None,
3605 kind: StmtKind::Given { topic, body },
3606 line,
3607 })
3608 }
3609
3610 fn parse_when_stmt(&mut self) -> StrykeResult<Statement> {
3612 let line = self.peek_line();
3613 self.advance();
3614 self.expect(&Token::LParen)?;
3615 let cond = self.parse_expression()?;
3616 self.expect(&Token::RParen)?;
3617 let body = self.parse_block()?;
3618 self.eat(&Token::Semicolon);
3619 Ok(Statement {
3620 label: None,
3621 kind: StmtKind::When { cond, body },
3622 line,
3623 })
3624 }
3625
3626 fn parse_default_stmt(&mut self) -> StrykeResult<Statement> {
3628 let line = self.peek_line();
3629 self.advance();
3630 let body = self.parse_block()?;
3631 self.eat(&Token::Semicolon);
3632 Ok(Statement {
3633 label: None,
3634 kind: StmtKind::DefaultCase { body },
3635 line,
3636 })
3637 }
3638
3639 fn parse_cond_expr(&mut self, line: usize) -> StrykeResult<Expr> {
3645 self.expect(&Token::LBrace)?;
3646
3647 let mut arms: Vec<(Expr, Block)> = Vec::new();
3648 let mut else_block: Option<Block> = None;
3649
3650 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
3651 let arm_line = self.peek_line();
3652
3653 let is_default = matches!(self.peek(), Token::Ident(ref s) if s == "default")
3655 && matches!(self.peek_at(1), Token::FatArrow);
3656
3657 if is_default {
3658 self.advance(); self.advance(); let body = if matches!(self.peek(), Token::LBrace) {
3661 self.parse_block()?
3662 } else {
3663 let expr = self.parse_assign_expr()?;
3664 vec![Statement {
3665 label: None,
3666 kind: StmtKind::Expression(expr),
3667 line: arm_line,
3668 }]
3669 };
3670 else_block = Some(body);
3671 self.eat(&Token::Comma);
3672 break; }
3674
3675 let condition = self.parse_assign_expr()?;
3677 self.expect(&Token::FatArrow)?;
3678
3679 let body = if matches!(self.peek(), Token::LBrace) {
3680 self.parse_block()?
3681 } else {
3682 let expr = self.parse_assign_expr()?;
3683 vec![Statement {
3684 label: None,
3685 kind: StmtKind::Expression(expr),
3686 line: arm_line,
3687 }]
3688 };
3689
3690 arms.push((condition, body));
3691 self.eat(&Token::Comma);
3692 }
3693
3694 self.expect(&Token::RBrace)?;
3695
3696 if arms.is_empty() {
3697 return Err(self.syntax_err("cond requires at least one condition arm", line));
3698 }
3699
3700 let (first_cond, first_body) = arms.remove(0);
3702 let elsifs: Vec<(Expr, Block)> = arms;
3703
3704 let if_stmt = Statement {
3706 label: None,
3707 kind: StmtKind::If {
3708 condition: first_cond,
3709 body: first_body,
3710 elsifs,
3711 else_block,
3712 },
3713 line,
3714 };
3715 let inner = Expr {
3716 kind: ExprKind::CodeRef {
3717 params: vec![],
3718 body: vec![if_stmt],
3719 },
3720 line,
3721 };
3722 Ok(Expr {
3723 kind: ExprKind::Do(Box::new(inner)),
3724 line,
3725 })
3726 }
3727
3728 fn parse_algebraic_match_expr(&mut self, line: usize) -> StrykeResult<Expr> {
3730 self.expect(&Token::LParen)?;
3731 let subject = self.parse_expression()?;
3732 self.expect(&Token::RParen)?;
3733 self.expect(&Token::LBrace)?;
3734 let mut arms = Vec::new();
3735 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
3736 if self.eat(&Token::Semicolon) {
3737 continue;
3738 }
3739 let pattern = self.parse_match_pattern()?;
3740 let guard = if matches!(self.peek(), Token::Ident(ref s) if s == "if") {
3741 self.advance();
3742 Some(Box::new(self.parse_assign_expr()?))
3745 } else {
3746 None
3747 };
3748 self.expect(&Token::FatArrow)?;
3749 let body = self.parse_assign_expr()?;
3751 arms.push(MatchArm {
3752 pattern,
3753 guard,
3754 body,
3755 });
3756 self.eat(&Token::Comma);
3757 }
3758 self.expect(&Token::RBrace)?;
3759 Ok(Expr {
3760 kind: ExprKind::AlgebraicMatch {
3761 subject: Box::new(subject),
3762 arms,
3763 },
3764 line,
3765 })
3766 }
3767
3768 fn parse_match_pattern(&mut self) -> StrykeResult<MatchPattern> {
3769 match self.peek().clone() {
3770 Token::Regex(pattern, flags, _delim) => {
3771 self.advance();
3772 Ok(MatchPattern::Regex { pattern, flags })
3773 }
3774 Token::Ident(ref s) if s == "_" => {
3775 self.advance();
3776 Ok(MatchPattern::Any)
3777 }
3778 Token::Ident(ref s) if s == "Some" => {
3779 self.advance();
3780 self.expect(&Token::LParen)?;
3781 let name = self.parse_scalar_var_name()?;
3782 self.expect(&Token::RParen)?;
3783 Ok(MatchPattern::OptionSome(name))
3784 }
3785 Token::LBracket => self.parse_match_array_pattern(),
3786 Token::LBrace => self.parse_match_hash_pattern(),
3787 Token::LParen => {
3788 self.advance();
3789 let e = self.parse_expression()?;
3790 self.expect(&Token::RParen)?;
3791 Ok(MatchPattern::Value(Box::new(e)))
3792 }
3793 _ => {
3794 let e = self.parse_assign_expr()?;
3795 Ok(MatchPattern::Value(Box::new(e)))
3796 }
3797 }
3798 }
3799
3800 fn parse_match_array_elems_until_rbracket(&mut self) -> StrykeResult<Vec<MatchArrayElem>> {
3802 let mut elems = Vec::new();
3803 if self.eat(&Token::RBracket) {
3804 return Ok(vec![]);
3805 }
3806 loop {
3807 if matches!(self.peek(), Token::Star) {
3808 self.advance();
3809 elems.push(MatchArrayElem::Rest);
3810 self.eat(&Token::Comma);
3811 if !matches!(self.peek(), Token::RBracket) {
3812 return Err(self.syntax_err(
3813 "`*` must be the last element in an array match pattern",
3814 self.peek_line(),
3815 ));
3816 }
3817 self.expect(&Token::RBracket)?;
3818 return Ok(elems);
3819 }
3820 if let Token::ArrayVar(name) = self.peek().clone() {
3821 self.advance();
3822 elems.push(MatchArrayElem::RestBind(name));
3823 self.eat(&Token::Comma);
3824 if !matches!(self.peek(), Token::RBracket) {
3825 return Err(self.syntax_err(
3826 "`@name` rest bind must be the last element in an array match pattern",
3827 self.peek_line(),
3828 ));
3829 }
3830 self.expect(&Token::RBracket)?;
3831 return Ok(elems);
3832 }
3833 if let Token::ScalarVar(name) = self.peek().clone() {
3834 self.advance();
3835 elems.push(MatchArrayElem::CaptureScalar(name));
3836 if self.eat(&Token::Comma) {
3837 if matches!(self.peek(), Token::RBracket) {
3838 break;
3839 }
3840 continue;
3841 }
3842 break;
3843 }
3844 let e = self.parse_assign_expr()?;
3845 elems.push(MatchArrayElem::Expr(e));
3846 if self.eat(&Token::Comma) {
3847 if matches!(self.peek(), Token::RBracket) {
3848 break;
3849 }
3850 continue;
3851 }
3852 break;
3853 }
3854 self.expect(&Token::RBracket)?;
3855 Ok(elems)
3856 }
3857
3858 fn parse_match_array_pattern(&mut self) -> StrykeResult<MatchPattern> {
3859 self.expect(&Token::LBracket)?;
3860 let elems = self.parse_match_array_elems_until_rbracket()?;
3861 Ok(MatchPattern::Array(elems))
3862 }
3863
3864 fn parse_match_hash_pattern(&mut self) -> StrykeResult<MatchPattern> {
3865 self.expect(&Token::LBrace)?;
3866 let mut pairs = Vec::new();
3867 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
3868 if self.eat(&Token::Semicolon) {
3869 continue;
3870 }
3871 let key = self.parse_assign_expr()?;
3872 self.expect(&Token::FatArrow)?;
3873 match self.advance().0 {
3874 Token::Ident(ref s) if s == "_" => {
3875 pairs.push(MatchHashPair::KeyOnly { key });
3876 }
3877 Token::ScalarVar(name) => {
3878 pairs.push(MatchHashPair::Capture { key, name });
3879 }
3880 tok => {
3881 return Err(self.syntax_err(
3882 format!(
3883 "hash match pattern must bind with `=> $name` or `=> _`, got {:?}",
3884 tok
3885 ),
3886 self.peek_line(),
3887 ));
3888 }
3889 }
3890 self.eat(&Token::Comma);
3891 }
3892 self.expect(&Token::RBrace)?;
3893 Ok(MatchPattern::Hash(pairs))
3894 }
3895
3896 fn parse_eval_timeout(&mut self) -> StrykeResult<Statement> {
3898 let line = self.peek_line();
3899 self.advance();
3900 let timeout = self.parse_postfix()?;
3901 let body = self.parse_block_or_bareword_block_no_args()?;
3902 self.eat(&Token::Semicolon);
3903 Ok(Statement {
3904 label: None,
3905 kind: StmtKind::EvalTimeout { timeout, body },
3906 line,
3907 })
3908 }
3909
3910 fn mark_match_scalar_g_for_boolean_condition(cond: &mut Expr) {
3911 match &mut cond.kind {
3912 ExprKind::Match {
3913 flags, scalar_g, ..
3914 } if flags.contains('g') => {
3915 *scalar_g = true;
3916 }
3917 ExprKind::UnaryOp {
3918 op: UnaryOp::LogNot,
3919 expr,
3920 } => {
3921 if let ExprKind::Match {
3922 flags, scalar_g, ..
3923 } = &mut expr.kind
3924 {
3925 if flags.contains('g') {
3926 *scalar_g = true;
3927 }
3928 }
3929 }
3930 _ => {}
3931 }
3932 }
3933
3934 fn parse_if(&mut self) -> StrykeResult<Statement> {
3935 let line = self.peek_line();
3936 self.advance(); if matches!(self.peek(), Token::Ident(ref s) if s == "let") {
3938 if crate::compat_mode() {
3939 return Err(self.syntax_err(
3940 "`if let` is a stryke extension (disabled by --compat)",
3941 line,
3942 ));
3943 }
3944 return self.parse_if_let(line);
3945 }
3946 self.expect(&Token::LParen)?;
3947 let mut cond = self.parse_expression()?;
3948 Self::mark_match_scalar_g_for_boolean_condition(&mut cond);
3949 self.expect(&Token::RParen)?;
3950 let body = self.parse_block()?;
3951
3952 let mut elsifs = Vec::new();
3953 let mut else_block = None;
3954
3955 loop {
3956 if let Token::Ident(ref kw) = self.peek().clone() {
3957 if kw == "elsif" {
3958 self.advance();
3959 self.expect(&Token::LParen)?;
3960 let mut c = self.parse_expression()?;
3961 Self::mark_match_scalar_g_for_boolean_condition(&mut c);
3962 self.expect(&Token::RParen)?;
3963 let b = self.parse_block()?;
3964 elsifs.push((c, b));
3965 continue;
3966 }
3967 if kw == "else" {
3968 self.advance();
3969 else_block = Some(self.parse_block()?);
3970 }
3971 }
3972 break;
3973 }
3974
3975 Ok(Statement {
3976 label: None,
3977 kind: StmtKind::If {
3978 condition: cond,
3979 body,
3980 elsifs,
3981 else_block,
3982 },
3983 line,
3984 })
3985 }
3986
3987 fn parse_if_let(&mut self, line: usize) -> StrykeResult<Statement> {
3989 self.advance(); let pattern = self.parse_match_pattern()?;
3991 self.expect(&Token::Assign)?;
3992 self.suppress_scalar_hash_brace = self.suppress_scalar_hash_brace.saturating_add(1);
3994 let rhs = self.parse_assign_expr();
3995 self.suppress_scalar_hash_brace = self.suppress_scalar_hash_brace.saturating_sub(1);
3996 let rhs = rhs?;
3997 let then_block = self.parse_block()?;
3998 let else_block_opt = match self.peek().clone() {
3999 Token::Ident(ref kw) if kw == "else" => {
4000 self.advance();
4001 Some(self.parse_block()?)
4002 }
4003 Token::Ident(ref kw) if kw == "elsif" => {
4004 return Err(self.syntax_err(
4005 "`if let` does not support `elsif`; use `else { }` or a full `match`",
4006 self.peek_line(),
4007 ));
4008 }
4009 _ => None,
4010 };
4011 let then_expr = Self::expr_do_anon_block(then_block, line);
4012 let else_expr = if let Some(eb) = else_block_opt {
4013 Self::expr_do_anon_block(eb, line)
4014 } else {
4015 Expr {
4016 kind: ExprKind::Undef,
4017 line,
4018 }
4019 };
4020 let arms = vec![
4021 MatchArm {
4022 pattern,
4023 guard: None,
4024 body: then_expr,
4025 },
4026 MatchArm {
4027 pattern: MatchPattern::Any,
4028 guard: None,
4029 body: else_expr,
4030 },
4031 ];
4032 Ok(Statement {
4033 label: None,
4034 kind: StmtKind::Expression(Expr {
4035 kind: ExprKind::AlgebraicMatch {
4036 subject: Box::new(rhs),
4037 arms,
4038 },
4039 line,
4040 }),
4041 line,
4042 })
4043 }
4044
4045 fn expr_do_anon_block(block: Block, outer_line: usize) -> Expr {
4046 let inner_line = block.first().map(|s| s.line).unwrap_or(outer_line);
4047 Expr {
4048 kind: ExprKind::Do(Box::new(Expr {
4049 kind: ExprKind::CodeRef {
4050 params: vec![],
4051 body: block,
4052 },
4053 line: inner_line,
4054 })),
4055 line: outer_line,
4056 }
4057 }
4058
4059 fn parse_unless(&mut self) -> StrykeResult<Statement> {
4060 let line = self.peek_line();
4061 self.advance(); self.expect(&Token::LParen)?;
4063 let mut cond = self.parse_expression()?;
4064 Self::mark_match_scalar_g_for_boolean_condition(&mut cond);
4065 self.expect(&Token::RParen)?;
4066 let body = self.parse_block()?;
4067 let else_block = if let Token::Ident(ref kw) = self.peek().clone() {
4068 if kw == "else" {
4069 self.advance();
4070 Some(self.parse_block()?)
4071 } else {
4072 None
4073 }
4074 } else {
4075 None
4076 };
4077 Ok(Statement {
4078 label: None,
4079 kind: StmtKind::Unless {
4080 condition: cond,
4081 body,
4082 else_block,
4083 },
4084 line,
4085 })
4086 }
4087
4088 fn parse_while(&mut self) -> StrykeResult<Statement> {
4089 let line = self.peek_line();
4090 self.advance(); if matches!(self.peek(), Token::Ident(ref s) if s == "let") {
4092 if crate::compat_mode() {
4093 return Err(self.syntax_err(
4094 "`while let` is a stryke extension (disabled by --compat)",
4095 line,
4096 ));
4097 }
4098 return self.parse_while_let(line);
4099 }
4100 self.expect(&Token::LParen)?;
4101 let mut cond = self.parse_expression()?;
4102 Self::mark_match_scalar_g_for_boolean_condition(&mut cond);
4103 self.expect(&Token::RParen)?;
4104 let body = self.parse_block()?;
4105 let continue_block = self.parse_optional_continue_block()?;
4106 Ok(Statement {
4107 label: None,
4108 kind: StmtKind::While {
4109 condition: cond,
4110 body,
4111 label: None,
4112 continue_block,
4113 },
4114 line,
4115 })
4116 }
4117
4118 fn parse_while_let(&mut self, line: usize) -> StrykeResult<Statement> {
4121 self.advance(); let pattern = self.parse_match_pattern()?;
4123 self.expect(&Token::Assign)?;
4124 self.suppress_scalar_hash_brace = self.suppress_scalar_hash_brace.saturating_add(1);
4125 let rhs = self.parse_assign_expr();
4126 self.suppress_scalar_hash_brace = self.suppress_scalar_hash_brace.saturating_sub(1);
4127 let rhs = rhs?;
4128 let mut user_body = self.parse_block()?;
4129 let continue_block = self.parse_optional_continue_block()?;
4130 user_body.push(Statement::new(
4131 StmtKind::Expression(Expr {
4132 kind: ExprKind::Integer(1),
4133 line,
4134 }),
4135 line,
4136 ));
4137 let tmp = format!("__while_let_{}", self.alloc_desugar_tmp());
4138 let match_expr = Expr {
4139 kind: ExprKind::AlgebraicMatch {
4140 subject: Box::new(rhs),
4141 arms: vec![
4142 MatchArm {
4143 pattern,
4144 guard: None,
4145 body: Self::expr_do_anon_block(user_body, line),
4146 },
4147 MatchArm {
4148 pattern: MatchPattern::Any,
4149 guard: None,
4150 body: Expr {
4151 kind: ExprKind::Integer(0),
4152 line,
4153 },
4154 },
4155 ],
4156 },
4157 line,
4158 };
4159 let my_stmt = Statement::new(
4160 StmtKind::My(vec![VarDecl {
4161 sigil: Sigil::Scalar,
4162 name: tmp.clone(),
4163 initializer: Some(match_expr),
4164 frozen: false,
4165 type_annotation: None,
4166 list_context: false,
4167 }]),
4168 line,
4169 );
4170 let unless_last = Statement::new(
4171 StmtKind::Unless {
4172 condition: Expr {
4173 kind: ExprKind::ScalarVar(tmp),
4174 line,
4175 },
4176 body: vec![Statement::new(StmtKind::Last(None), line)],
4177 else_block: None,
4178 },
4179 line,
4180 );
4181 Ok(Statement::new(
4182 StmtKind::While {
4183 condition: Expr {
4184 kind: ExprKind::Integer(1),
4185 line,
4186 },
4187 body: vec![my_stmt, unless_last],
4188 label: None,
4189 continue_block,
4190 },
4191 line,
4192 ))
4193 }
4194
4195 fn parse_until(&mut self) -> StrykeResult<Statement> {
4196 let line = self.peek_line();
4197 self.advance(); self.expect(&Token::LParen)?;
4199 let mut cond = self.parse_expression()?;
4200 Self::mark_match_scalar_g_for_boolean_condition(&mut cond);
4201 self.expect(&Token::RParen)?;
4202 let body = self.parse_block()?;
4203 let continue_block = self.parse_optional_continue_block()?;
4204 Ok(Statement {
4205 label: None,
4206 kind: StmtKind::Until {
4207 condition: cond,
4208 body,
4209 label: None,
4210 continue_block,
4211 },
4212 line,
4213 })
4214 }
4215
4216 fn parse_optional_continue_block(&mut self) -> StrykeResult<Option<Block>> {
4218 if let Token::Ident(ref kw) = self.peek().clone() {
4219 if kw == "continue" {
4220 self.advance();
4221 return Ok(Some(self.parse_block()?));
4222 }
4223 }
4224 Ok(None)
4225 }
4226
4227 fn parse_for_or_foreach(&mut self) -> StrykeResult<Statement> {
4228 let line = self.peek_line();
4229 self.advance(); match self.peek() {
4235 Token::LParen => {
4236 let saved = self.pos;
4241 self.advance(); let mut depth = 1;
4244 let mut has_semi = false;
4245 let mut scan = self.pos;
4246 while scan < self.tokens.len() {
4247 match &self.tokens[scan].0 {
4248 Token::LParen => depth += 1,
4249 Token::RParen => {
4250 depth -= 1;
4251 if depth == 0 {
4252 break;
4253 }
4254 }
4255 Token::Semicolon if depth == 1 => {
4256 has_semi = true;
4257 break;
4258 }
4259 _ => {}
4260 }
4261 scan += 1;
4262 }
4263 self.pos = saved;
4264
4265 if has_semi {
4266 self.parse_c_style_for(line)
4267 } else {
4268 self.expect(&Token::LParen)?;
4270 let list = self.parse_expression()?;
4271 self.expect(&Token::RParen)?;
4272 let body = self.parse_block()?;
4273 let continue_block = self.parse_optional_continue_block()?;
4274 Ok(Statement {
4275 label: None,
4276 kind: StmtKind::Foreach {
4277 var: "_".to_string(),
4278 list,
4279 body,
4280 label: None,
4281 continue_block,
4282 },
4283 line,
4284 })
4285 }
4286 }
4287 Token::Ident(ref kw) if kw == "my" => {
4288 self.advance(); let var = self.parse_scalar_var_name()?;
4290 self.expect(&Token::LParen)?;
4291 let list = self.parse_expression()?;
4292 self.expect(&Token::RParen)?;
4293 let body = self.parse_block()?;
4294 let continue_block = self.parse_optional_continue_block()?;
4295 Ok(Statement {
4296 label: None,
4297 kind: StmtKind::Foreach {
4298 var,
4299 list,
4300 body,
4301 label: None,
4302 continue_block,
4303 },
4304 line,
4305 })
4306 }
4307 Token::ScalarVar(_) => {
4308 let var = self.parse_scalar_var_name()?;
4309 self.expect(&Token::LParen)?;
4310 let list = self.parse_expression()?;
4311 self.expect(&Token::RParen)?;
4312 let body = self.parse_block()?;
4313 let continue_block = self.parse_optional_continue_block()?;
4314 Ok(Statement {
4315 label: None,
4316 kind: StmtKind::Foreach {
4317 var,
4318 list,
4319 body,
4320 label: None,
4321 continue_block,
4322 },
4323 line,
4324 })
4325 }
4326 _ => self.parse_c_style_for(line),
4327 }
4328 }
4329
4330 fn parse_c_style_for(&mut self, line: usize) -> StrykeResult<Statement> {
4331 self.expect(&Token::LParen)?;
4332 let init = if self.eat(&Token::Semicolon) {
4333 None
4334 } else {
4335 let s = self.parse_statement()?;
4336 self.eat(&Token::Semicolon);
4337 Some(Box::new(s))
4338 };
4339 let mut condition = if matches!(self.peek(), Token::Semicolon) {
4340 None
4341 } else {
4342 Some(self.parse_expression()?)
4343 };
4344 if let Some(ref mut c) = condition {
4345 Self::mark_match_scalar_g_for_boolean_condition(c);
4346 }
4347 self.expect(&Token::Semicolon)?;
4348 let step = if matches!(self.peek(), Token::RParen) {
4349 None
4350 } else {
4351 Some(self.parse_expression()?)
4352 };
4353 self.expect(&Token::RParen)?;
4354 let body = self.parse_block()?;
4355 let continue_block = self.parse_optional_continue_block()?;
4356 Ok(Statement {
4357 label: None,
4358 kind: StmtKind::For {
4359 init,
4360 condition,
4361 step,
4362 body,
4363 label: None,
4364 continue_block,
4365 },
4366 line,
4367 })
4368 }
4369
4370 fn parse_foreach(&mut self) -> StrykeResult<Statement> {
4371 let line = self.peek_line();
4372 self.advance(); let var = match self.peek() {
4374 Token::Ident(ref kw) if kw == "my" => {
4375 self.advance();
4376 self.parse_scalar_var_name()?
4377 }
4378 Token::ScalarVar(_) => self.parse_scalar_var_name()?,
4379 _ => "_".to_string(),
4380 };
4381 self.expect(&Token::LParen)?;
4382 let list = self.parse_expression()?;
4383 self.expect(&Token::RParen)?;
4384 let body = self.parse_block()?;
4385 let continue_block = self.parse_optional_continue_block()?;
4386 Ok(Statement {
4387 label: None,
4388 kind: StmtKind::Foreach {
4389 var,
4390 list,
4391 body,
4392 label: None,
4393 continue_block,
4394 },
4395 line,
4396 })
4397 }
4398
4399 fn parse_scalar_var_name(&mut self) -> StrykeResult<String> {
4400 match self.advance() {
4401 (Token::ScalarVar(name), _) => Ok(name),
4402 (tok, line) => {
4403 Err(self.syntax_err(format!("Expected scalar variable, got {:?}", tok), line))
4404 }
4405 }
4406 }
4407
4408 fn parse_legacy_sub_prototype_tail(&mut self) -> StrykeResult<String> {
4410 let mut s = String::new();
4411 loop {
4412 match self.peek().clone() {
4413 Token::RParen => {
4414 self.advance();
4415 break;
4416 }
4417 Token::Eof => {
4418 return Err(self.syntax_err(
4419 "Unterminated sub prototype (expected ')' before end of input)",
4420 self.peek_line(),
4421 ));
4422 }
4423 Token::ScalarVar(v) if v == ")" => {
4424 self.advance();
4427 s.push('$');
4428 if matches!(self.peek(), Token::LBrace) {
4429 break;
4430 }
4431 }
4432 Token::Ident(i) => {
4433 let i = i.clone();
4434 self.advance();
4435 s.push_str(&i);
4436 }
4437 Token::Semicolon => {
4438 self.advance();
4439 s.push(';');
4440 }
4441 Token::LParen => {
4442 self.advance();
4443 s.push('(');
4444 }
4445 Token::LBracket => {
4446 self.advance();
4447 s.push('[');
4448 }
4449 Token::RBracket => {
4450 self.advance();
4451 s.push(']');
4452 }
4453 Token::Backslash => {
4454 self.advance();
4455 s.push('\\');
4456 }
4457 Token::Comma => {
4458 self.advance();
4459 s.push(',');
4460 }
4461 Token::ScalarVar(v) => {
4462 let v = v.clone();
4463 self.advance();
4464 s.push('$');
4465 s.push_str(&v);
4466 }
4467 Token::ArrayVar(v) => {
4468 let v = v.clone();
4469 self.advance();
4470 s.push('@');
4471 s.push_str(&v);
4472 }
4473 Token::ArrayAt => {
4475 self.advance();
4476 s.push('@');
4477 }
4478 Token::HashVar(v) => {
4479 let v = v.clone();
4480 self.advance();
4481 s.push('%');
4482 s.push_str(&v);
4483 }
4484 Token::HashPercent => {
4485 self.advance();
4486 s.push('%');
4487 }
4488 Token::Plus => {
4489 self.advance();
4490 s.push('+');
4491 }
4492 Token::Minus => {
4493 self.advance();
4494 s.push('-');
4495 }
4496 Token::BitAnd => {
4497 self.advance();
4498 s.push('&');
4499 }
4500 tok => {
4501 return Err(self.syntax_err(
4502 format!("Unexpected token in sub prototype: {:?}", tok),
4503 self.peek_line(),
4504 ));
4505 }
4506 }
4507 }
4508 Ok(s)
4509 }
4510
4511 fn sub_signature_list_starts_here(&self) -> bool {
4512 match self.peek() {
4513 Token::LBrace | Token::LBracket => true,
4514 Token::ScalarVar(name) if name != "$$" && name != ")" => true,
4515 Token::ArrayVar(_) | Token::HashVar(_) => true,
4516 _ => false,
4517 }
4518 }
4519
4520 fn parse_sub_signature_hash_key(&mut self) -> StrykeResult<String> {
4521 let (tok, line) = self.advance();
4522 match tok {
4523 Token::Ident(i) => Ok(i),
4524 Token::SingleString(s) | Token::DoubleString(s) => Ok(s),
4525 tok => Err(self.syntax_err(
4526 format!(
4527 "sub signature: expected hash key (identifier or string), got {:?}",
4528 tok
4529 ),
4530 line,
4531 )),
4532 }
4533 }
4534
4535 fn parse_sub_signature_param_list(&mut self) -> StrykeResult<Vec<SubSigParam>> {
4536 let mut params = Vec::new();
4537 loop {
4538 if matches!(self.peek(), Token::RParen) {
4539 break;
4540 }
4541 match self.peek().clone() {
4542 Token::ScalarVar(name) => {
4543 if name == "$$" || name == ")" {
4544 return Err(self.syntax_err(
4545 format!(
4546 "`{name}` cannot start a stryke sub signature (use legacy prototype `($$)` etc.)"
4547 ),
4548 self.peek_line(),
4549 ));
4550 }
4551 self.advance();
4552 let ty = if self.eat(&Token::Colon) {
4553 match self.peek() {
4554 Token::Ident(ref tname) => {
4555 let tname = tname.clone();
4556 self.advance();
4557 Some(match tname.as_str() {
4558 "Int" => PerlTypeName::Int,
4559 "Str" => PerlTypeName::Str,
4560 "Float" => PerlTypeName::Float,
4561 "Bool" => PerlTypeName::Bool,
4562 "Array" => PerlTypeName::Array,
4563 "Hash" => PerlTypeName::Hash,
4564 "Ref" => PerlTypeName::Ref,
4565 "Any" => PerlTypeName::Any,
4566 _ => PerlTypeName::Struct(tname),
4567 })
4568 }
4569 _ => {
4570 return Err(self.syntax_err(
4571 "expected type name after `:` in sub signature",
4572 self.peek_line(),
4573 ));
4574 }
4575 }
4576 } else {
4577 None
4578 };
4579 let default = if self.eat(&Token::Assign) {
4581 Some(Box::new(self.parse_ternary()?))
4582 } else {
4583 None
4584 };
4585 params.push(SubSigParam::Scalar(name, ty, default));
4586 }
4587 Token::ArrayVar(name) => {
4588 self.advance();
4589 let default = if self.eat(&Token::Assign) {
4590 Some(Box::new(self.parse_ternary()?))
4591 } else {
4592 None
4593 };
4594 params.push(SubSigParam::Array(name, default));
4595 }
4596 Token::HashVar(name) => {
4597 self.advance();
4598 let default = if self.eat(&Token::Assign) {
4599 Some(Box::new(self.parse_ternary()?))
4600 } else {
4601 None
4602 };
4603 params.push(SubSigParam::Hash(name, default));
4604 }
4605 Token::LBracket => {
4606 self.advance();
4607 let elems = self.parse_match_array_elems_until_rbracket()?;
4608 params.push(SubSigParam::ArrayDestruct(elems));
4609 }
4610 Token::LBrace => {
4611 self.advance();
4612 let mut pairs = Vec::new();
4613 loop {
4614 if matches!(self.peek(), Token::RBrace | Token::Eof) {
4615 break;
4616 }
4617 if self.eat(&Token::Comma) {
4618 continue;
4619 }
4620 let key = self.parse_sub_signature_hash_key()?;
4621 self.expect(&Token::FatArrow)?;
4622 let bind = self.parse_scalar_var_name()?;
4623 pairs.push((key, bind));
4624 self.eat(&Token::Comma);
4625 }
4626 self.expect(&Token::RBrace)?;
4627 params.push(SubSigParam::HashDestruct(pairs));
4628 }
4629 tok => {
4630 return Err(self.syntax_err(
4631 format!(
4632 "expected `$name`, `[ ... ]`, or `{{ ... }}` in sub signature, got {:?}",
4633 tok
4634 ),
4635 self.peek_line(),
4636 ));
4637 }
4638 }
4639 match self.peek() {
4640 Token::Comma => {
4641 self.advance();
4642 if matches!(self.peek(), Token::RParen) {
4643 return Err(self.syntax_err(
4644 "trailing `,` before `)` in sub signature",
4645 self.peek_line(),
4646 ));
4647 }
4648 }
4649 Token::RParen => break,
4650 _ => {
4651 return Err(self.syntax_err(
4652 format!(
4653 "expected `,` or `)` after sub signature parameter, got {:?}",
4654 self.peek()
4655 ),
4656 self.peek_line(),
4657 ));
4658 }
4659 }
4660 }
4661 Ok(params)
4662 }
4663
4664 fn parse_sub_sig_or_prototype_opt(
4666 &mut self,
4667 ) -> StrykeResult<(Vec<SubSigParam>, Option<String>)> {
4668 if !matches!(self.peek(), Token::LParen) {
4669 return Ok((vec![], None));
4670 }
4671 self.advance();
4672 if matches!(self.peek(), Token::RParen) {
4673 self.advance();
4674 return Ok((vec![], Some(String::new())));
4675 }
4676 if self.sub_signature_list_starts_here() {
4677 let params = self.parse_sub_signature_param_list()?;
4678 self.expect(&Token::RParen)?;
4679 return Ok((params, None));
4680 }
4681 let proto = self.parse_legacy_sub_prototype_tail()?;
4682 Ok((vec![], Some(proto)))
4683 }
4684
4685 fn parse_sub_attributes(&mut self) -> StrykeResult<()> {
4687 while self.eat(&Token::Colon) {
4688 match self.advance() {
4689 (Token::Ident(_), _) => {}
4690 (tok, line) => {
4691 return Err(self.syntax_err(
4692 format!("Expected attribute name after `:`, got {:?}", tok),
4693 line,
4694 ));
4695 }
4696 }
4697 if self.eat(&Token::LParen) {
4698 let mut depth = 1usize;
4699 while depth > 0 {
4700 match self.advance().0 {
4701 Token::LParen => depth += 1,
4702 Token::RParen => {
4703 depth -= 1;
4704 }
4705 Token::Eof => {
4706 return Err(self.syntax_err(
4707 "Unterminated sub attribute argument list",
4708 self.peek_line(),
4709 ));
4710 }
4711 _ => {}
4712 }
4713 }
4714 }
4715 }
4716 Ok(())
4717 }
4718
4719 fn try_parse_fn_assign_shorthand_body(&mut self) -> StrykeResult<Option<Block>> {
4722 if !self.eat(&Token::Assign) {
4723 return Ok(None);
4724 }
4725 let expr = self.parse_assign_expr()?;
4726 if matches!(self.peek(), Token::Comma) {
4727 return Err(self.syntax_err(
4728 "`fn ... =` allows only a single expression; use `fn ... { ... }` for multiple statements",
4729 self.peek_line(),
4730 ));
4731 }
4732 let eline = expr.line;
4733 self.eat(&Token::Semicolon);
4734 let mut body = vec![Statement {
4735 label: None,
4736 kind: StmtKind::Expression(expr),
4737 line: eline,
4738 }];
4739 Self::default_topic_for_sole_bareword(&mut body);
4740 Ok(Some(body))
4741 }
4742
4743 fn parse_fn_eq_body_or_block(&mut self, is_sub_keyword: bool) -> StrykeResult<Block> {
4746 if !is_sub_keyword {
4747 if let Some(block) = self.try_parse_fn_assign_shorthand_body()? {
4748 return Ok(block);
4749 }
4750 }
4751 self.parse_block()
4752 }
4753
4754 fn parse_sub_decl(&mut self, is_sub_keyword: bool) -> StrykeResult<Statement> {
4755 let line = self.peek_line();
4756 self.advance(); match self.peek().clone() {
4758 Token::Ident(_) => {
4759 let name = self.parse_package_qualified_identifier()?;
4760 let bare = name.rsplit("::").next().unwrap_or(&name);
4769 if Self::is_underscore_topic_slot(bare) {
4770 return Err(self.syntax_err(
4771 format!(
4772 "`fn {}` would shadow the topic-slot scalar; pick a different name",
4773 name
4774 ),
4775 line,
4776 ));
4777 }
4778 if Self::is_reserved_special_var_name(bare) {
4779 return Err(self.syntax_err(
4780 format!(
4781 "`fn {}` would shadow a Perl special variable / filehandle / compile-time token; pick a different name",
4782 name
4783 ),
4784 line,
4785 ));
4786 }
4787 let allow_shadow =
4794 crate::compat_mode() || (self.parsing_module && !crate::no_interop_mode());
4795 if !allow_shadow {
4796 self.check_udf_shadows_builtin(&name, line)?;
4797 }
4798 self.declared_subs.insert(name.clone());
4799 let (params, prototype) = self.parse_sub_sig_or_prototype_opt()?;
4800 self.parse_sub_attributes()?;
4801 let body = self.parse_fn_eq_body_or_block(is_sub_keyword)?;
4802 Ok(Statement {
4803 label: None,
4804 kind: StmtKind::SubDecl {
4805 name,
4806 params,
4807 body,
4808 prototype,
4809 },
4810 line,
4811 })
4812 }
4813 Token::LParen | Token::LBrace | Token::Colon => {
4814 if is_sub_keyword && crate::no_interop_mode() {
4816 return Err(self.syntax_err(
4817 "stryke uses `fn {}` instead of `sub {}` (--no-interop)",
4818 line,
4819 ));
4820 }
4821 let (params, _prototype) = self.parse_sub_sig_or_prototype_opt()?;
4823 self.parse_sub_attributes()?;
4824 let body = self.parse_fn_eq_body_or_block(is_sub_keyword)?;
4825 Ok(Statement {
4826 label: None,
4827 kind: StmtKind::Expression(Expr {
4828 kind: ExprKind::CodeRef { params, body },
4829 line,
4830 }),
4831 line,
4832 })
4833 }
4834 tok => {
4835 let topic_name = match &tok {
4840 Token::ScalarVar(n) | Token::ArrayVar(n) | Token::HashVar(n)
4841 if Self::is_underscore_topic_slot(n) =>
4842 {
4843 Some((
4844 match &tok {
4845 Token::ScalarVar(_) => '$',
4846 Token::ArrayVar(_) => '@',
4847 Token::HashVar(_) => '%',
4848 _ => unreachable!(),
4849 },
4850 n.clone(),
4851 ))
4852 }
4853 _ => None,
4854 };
4855 if let Some((sigil, n)) = topic_name {
4856 return Err(self.syntax_err(
4857 format!(
4858 "`fn {}{}` would shadow the topic-slot scalar; pick a different name",
4859 sigil, n
4860 ),
4861 self.peek_line(),
4862 ));
4863 }
4864 let special_var = match &tok {
4869 Token::ScalarVar(n) | Token::ArrayVar(n) | Token::HashVar(n) => Some((
4870 match &tok {
4871 Token::ScalarVar(_) => '$',
4872 Token::ArrayVar(_) => '@',
4873 Token::HashVar(_) => '%',
4874 _ => unreachable!(),
4875 },
4876 n.clone(),
4877 )),
4878 _ => None,
4879 };
4880 if let Some((sigil, n)) = special_var {
4881 return Err(self.syntax_err(
4882 format!(
4883 "`fn {}{}` would shadow a Perl special variable / global; pick a different name",
4884 sigil, n
4885 ),
4886 self.peek_line(),
4887 ));
4888 }
4889 if matches!(tok, Token::Percent) {
4894 return Err(self.syntax_err(
4895 "`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 { ... }`",
4896 self.peek_line(),
4897 ));
4898 }
4899 Err(self.syntax_err(
4900 format!("Expected sub name, `(`, `{{`, or `:`, got {:?}", tok),
4901 self.peek_line(),
4902 ))
4903 }
4904 }
4905 }
4906
4907 fn parse_advice_decl(&mut self, kind: crate::ast::AdviceKind) -> StrykeResult<Statement> {
4910 let line = self.peek_line();
4911 self.advance(); let pattern = match self.advance() {
4913 (Token::SingleString(s), _) | (Token::DoubleString(s), _) => s,
4914 (tok, err_line) => {
4915 return Err(self.syntax_err(
4916 format!(
4917 "Expected string-literal pattern after `{}`, got {:?}",
4918 match kind {
4919 crate::ast::AdviceKind::Before => "before",
4920 crate::ast::AdviceKind::After => "after",
4921 crate::ast::AdviceKind::Around => "around",
4922 },
4923 tok
4924 ),
4925 err_line,
4926 ));
4927 }
4928 };
4929 let body = self.parse_block()?;
4930 Ok(Statement {
4931 label: None,
4932 kind: StmtKind::AdviceDecl {
4933 kind,
4934 pattern,
4935 body,
4936 },
4937 line,
4938 })
4939 }
4940
4941 fn parse_struct_decl(&mut self) -> StrykeResult<Statement> {
4943 let line = self.peek_line();
4944 self.advance(); let raw_name = self.parse_package_qualified_identifier().map_err(|_| {
4946 self.syntax_err(
4947 format!("Expected struct name, got {:?}", self.peek()),
4948 self.peek_line(),
4949 )
4950 })?;
4951 let name = if raw_name.contains("::") || self.current_package == "main" {
4952 raw_name
4953 } else {
4954 format!("{}::{}", self.current_package, raw_name)
4955 };
4956 self.expect(&Token::LBrace)?;
4957 let mut fields = Vec::new();
4958 let mut methods = Vec::new();
4959 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
4960 let is_method = match self.peek() {
4962 Token::Ident(s) => s == "fn" || s == "sub",
4963 _ => false,
4964 };
4965 if is_method {
4966 let is_sub_keyword = matches!(self.peek(), Token::Ident(ref s) if s == "sub");
4967 self.advance(); let method_name = match self.advance() {
4969 (Token::Ident(n), _) => n,
4970 (tok, err_line) => {
4971 return Err(self
4972 .syntax_err(format!("Expected method name, got {:?}", tok), err_line))
4973 }
4974 };
4975 let params = if self.eat(&Token::LParen) {
4977 let p = self.parse_sub_signature_param_list()?;
4978 self.expect(&Token::RParen)?;
4979 p
4980 } else {
4981 Vec::new()
4982 };
4983 let body = if is_sub_keyword {
4984 self.parse_block()?
4985 } else {
4986 self.parse_fn_eq_body_or_block(false)?
4987 };
4988 methods.push(crate::ast::StructMethod {
4989 name: method_name,
4990 params,
4991 body,
4992 });
4993 self.eat(&Token::Comma);
4995 self.eat(&Token::Semicolon);
4996 continue;
4997 }
4998
4999 let field_name = match self.advance() {
5000 (Token::Ident(n), _) => n,
5001 (tok, err_line) => {
5002 return Err(
5003 self.syntax_err(format!("Expected field name, got {:?}", tok), err_line)
5004 )
5005 }
5006 };
5007 let ty = if self.eat(&Token::FatArrow) || self.eat(&Token::Colon) {
5012 self.parse_type_name()?
5013 } else {
5014 crate::ast::PerlTypeName::Any
5015 };
5016 let default = if self.eat(&Token::Assign) {
5017 Some(self.parse_ternary()?)
5019 } else {
5020 None
5021 };
5022 fields.push(StructField {
5023 name: field_name,
5024 ty,
5025 default,
5026 });
5027 if !self.eat(&Token::Comma) {
5028 self.eat(&Token::Semicolon);
5030 }
5031 }
5032 self.expect(&Token::RBrace)?;
5033 self.eat(&Token::Semicolon);
5034 Ok(Statement {
5035 label: None,
5036 kind: StmtKind::StructDecl {
5037 def: StructDef {
5038 name,
5039 fields,
5040 methods,
5041 },
5042 },
5043 line,
5044 })
5045 }
5046
5047 fn parse_enum_decl(&mut self) -> StrykeResult<Statement> {
5049 let line = self.peek_line();
5050 self.advance(); let raw_name = self.parse_package_qualified_identifier().map_err(|_| {
5052 self.syntax_err(
5053 format!("Expected enum name, got {:?}", self.peek()),
5054 self.peek_line(),
5055 )
5056 })?;
5057 let name = if raw_name.contains("::") || self.current_package == "main" {
5058 raw_name
5059 } else {
5060 format!("{}::{}", self.current_package, raw_name)
5061 };
5062 self.expect(&Token::LBrace)?;
5063 let mut variants = Vec::new();
5064 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
5065 let variant_name = match self.advance() {
5066 (Token::Ident(n), _) => n,
5067 (tok, err_line) => {
5068 return Err(
5069 self.syntax_err(format!("Expected variant name, got {:?}", tok), err_line)
5070 )
5071 }
5072 };
5073 let ty = if self.eat(&Token::FatArrow) {
5074 Some(self.parse_type_name()?)
5075 } else {
5076 None
5077 };
5078 variants.push(EnumVariant {
5079 name: variant_name,
5080 ty,
5081 });
5082 if !self.eat(&Token::Comma) {
5083 self.eat(&Token::Semicolon);
5084 }
5085 }
5086 self.expect(&Token::RBrace)?;
5087 self.eat(&Token::Semicolon);
5088 Ok(Statement {
5089 label: None,
5090 kind: StmtKind::EnumDecl {
5091 def: EnumDef { name, variants },
5092 },
5093 line,
5094 })
5095 }
5096
5097 fn parse_class_decl(&mut self, is_abstract: bool, is_final: bool) -> StrykeResult<Statement> {
5099 use crate::ast::{ClassDef, ClassField, ClassMethod, ClassStaticField, Visibility};
5100 let line = self.peek_line();
5101 self.advance(); let raw_name = self.parse_package_qualified_identifier().map_err(|_| {
5103 self.syntax_err(
5104 format!("Expected class name, got {:?}", self.peek()),
5105 self.peek_line(),
5106 )
5107 })?;
5108 let name = if raw_name.contains("::") || self.current_package == "main" {
5113 raw_name
5114 } else {
5115 format!("{}::{}", self.current_package, raw_name)
5116 };
5117
5118 let mut extends = Vec::new();
5120 if matches!(self.peek(), Token::Ident(ref s) if s == "extends") {
5121 self.advance(); loop {
5123 let parent = self.parse_package_qualified_identifier().map_err(|_| {
5124 self.syntax_err(
5125 format!(
5126 "Expected parent class name after `extends`, got {:?}",
5127 self.peek()
5128 ),
5129 self.peek_line(),
5130 )
5131 })?;
5132 extends.push(parent);
5133 if !self.eat(&Token::Comma) {
5134 break;
5135 }
5136 }
5137 }
5138
5139 let mut implements = Vec::new();
5141 if matches!(self.peek(), Token::Ident(ref s) if s == "impl") {
5142 self.advance(); loop {
5144 let trait_name = self.parse_package_qualified_identifier().map_err(|_| {
5145 self.syntax_err(
5146 format!("Expected trait name after `impl`, got {:?}", self.peek()),
5147 self.peek_line(),
5148 )
5149 })?;
5150 implements.push(trait_name);
5151 if !self.eat(&Token::Comma) {
5152 break;
5153 }
5154 }
5155 }
5156
5157 self.expect(&Token::LBrace)?;
5158 let mut fields = Vec::new();
5159 let mut methods = Vec::new();
5160 let mut static_fields = Vec::new();
5161
5162 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
5163 let visibility = match self.peek() {
5165 Token::Ident(ref s) if s == "pub" => {
5166 self.advance();
5167 Visibility::Public
5168 }
5169 Token::Ident(ref s) if s == "priv" => {
5170 self.advance();
5171 Visibility::Private
5172 }
5173 Token::Ident(ref s) if s == "prot" => {
5174 self.advance();
5175 Visibility::Protected
5176 }
5177 _ => Visibility::Public, };
5179
5180 if matches!(self.peek(), Token::Ident(ref s) if s == "static") {
5182 self.advance(); if matches!(self.peek(), Token::Ident(ref s) if s == "fn" || s == "sub") {
5186 return Err(self.syntax_err(
5188 "use `fn Self.name` for static methods, not `static fn`",
5189 self.peek_line(),
5190 ));
5191 }
5192
5193 let field_name = match self.advance() {
5194 (Token::Ident(n), _) => n,
5195 (tok, err_line) => {
5196 return Err(self.syntax_err(
5197 format!("Expected static field name, got {:?}", tok),
5198 err_line,
5199 ))
5200 }
5201 };
5202
5203 let ty = if self.eat(&Token::Colon) {
5204 self.parse_type_name()?
5205 } else {
5206 crate::ast::PerlTypeName::Any
5207 };
5208
5209 let default = if self.eat(&Token::Assign) {
5210 Some(self.parse_ternary()?)
5211 } else {
5212 None
5213 };
5214
5215 static_fields.push(ClassStaticField {
5216 name: field_name,
5217 ty,
5218 visibility,
5219 default,
5220 });
5221
5222 if !self.eat(&Token::Comma) {
5223 self.eat(&Token::Semicolon);
5224 }
5225 continue;
5226 }
5227
5228 let method_is_final = matches!(self.peek(), Token::Ident(ref s) if s == "final");
5230 if method_is_final {
5231 self.advance(); }
5233
5234 let is_method = matches!(self.peek(), Token::Ident(ref s) if s == "fn" || s == "sub");
5236 if is_method {
5237 self.advance(); let is_static = matches!(self.peek(), Token::Ident(ref s) if s == "Self");
5241 if is_static {
5242 self.advance(); self.expect(&Token::Dot)?;
5244 }
5245
5246 let method_name = match self.advance() {
5247 (Token::Ident(n), _) => n,
5248 (tok, err_line) => {
5249 return Err(self
5250 .syntax_err(format!("Expected method name, got {:?}", tok), err_line))
5251 }
5252 };
5253
5254 let params = if self.eat(&Token::LParen) {
5256 let p = self.parse_sub_signature_param_list()?;
5257 self.expect(&Token::RParen)?;
5258 p
5259 } else {
5260 Vec::new()
5261 };
5262
5263 let body = if let Some(b) = self.try_parse_fn_assign_shorthand_body()? {
5265 Some(b)
5266 } else if matches!(self.peek(), Token::LBrace) {
5267 Some(self.parse_block()?)
5268 } else {
5269 None
5270 };
5271
5272 methods.push(ClassMethod {
5273 name: method_name,
5274 params,
5275 body,
5276 visibility,
5277 is_static,
5278 is_final: method_is_final,
5279 });
5280 self.eat(&Token::Comma);
5281 self.eat(&Token::Semicolon);
5282 continue;
5283 } else if method_is_final {
5284 return Err(self.syntax_err("`final` must be followed by `fn`", self.peek_line()));
5285 }
5286
5287 let field_name = match self.advance() {
5289 (Token::Ident(n), _) => n,
5290 (tok, err_line) => {
5291 return Err(
5292 self.syntax_err(format!("Expected field name, got {:?}", tok), err_line)
5293 )
5294 }
5295 };
5296
5297 let ty = if self.eat(&Token::Colon) || self.eat(&Token::FatArrow) {
5301 self.parse_type_name()?
5302 } else {
5303 crate::ast::PerlTypeName::Any
5304 };
5305
5306 let default = if self.eat(&Token::Assign) {
5308 Some(self.parse_ternary()?)
5309 } else {
5310 None
5311 };
5312
5313 fields.push(ClassField {
5314 name: field_name,
5315 ty,
5316 visibility,
5317 default,
5318 });
5319
5320 if !self.eat(&Token::Comma) {
5321 self.eat(&Token::Semicolon);
5322 }
5323 }
5324
5325 self.expect(&Token::RBrace)?;
5326 self.eat(&Token::Semicolon);
5327
5328 Ok(Statement {
5329 label: None,
5330 kind: StmtKind::ClassDecl {
5331 def: ClassDef {
5332 name,
5333 is_abstract,
5334 is_final,
5335 extends,
5336 implements,
5337 fields,
5338 methods,
5339 static_fields,
5340 },
5341 },
5342 line,
5343 })
5344 }
5345
5346 fn parse_trait_decl(&mut self) -> StrykeResult<Statement> {
5348 use crate::ast::{ClassMethod, TraitDef, Visibility};
5349 let line = self.peek_line();
5350 self.advance(); let raw_name = self.parse_package_qualified_identifier().map_err(|_| {
5352 self.syntax_err(
5353 format!("Expected trait name, got {:?}", self.peek()),
5354 self.peek_line(),
5355 )
5356 })?;
5357 let name = if raw_name.contains("::") || self.current_package == "main" {
5358 raw_name
5359 } else {
5360 format!("{}::{}", self.current_package, raw_name)
5361 };
5362
5363 self.expect(&Token::LBrace)?;
5364 let mut methods = Vec::new();
5365
5366 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
5367 let visibility = match self.peek() {
5369 Token::Ident(ref s) if s == "pub" => {
5370 self.advance();
5371 Visibility::Public
5372 }
5373 Token::Ident(ref s) if s == "priv" => {
5374 self.advance();
5375 Visibility::Private
5376 }
5377 Token::Ident(ref s) if s == "prot" => {
5378 self.advance();
5379 Visibility::Protected
5380 }
5381 _ => Visibility::Public,
5382 };
5383
5384 if !matches!(self.peek(), Token::Ident(ref s) if s == "fn" || s == "sub") {
5386 return Err(self.syntax_err("Expected `fn` in trait definition", self.peek_line()));
5387 }
5388 self.advance(); let method_name = match self.advance() {
5391 (Token::Ident(n), _) => n,
5392 (tok, err_line) => {
5393 return Err(
5394 self.syntax_err(format!("Expected method name, got {:?}", tok), err_line)
5395 )
5396 }
5397 };
5398
5399 let params = if self.eat(&Token::LParen) {
5401 let p = self.parse_sub_signature_param_list()?;
5402 self.expect(&Token::RParen)?;
5403 p
5404 } else {
5405 Vec::new()
5406 };
5407
5408 let body = if let Some(b) = self.try_parse_fn_assign_shorthand_body()? {
5410 Some(b)
5411 } else if matches!(self.peek(), Token::LBrace) {
5412 Some(self.parse_block()?)
5413 } else {
5414 None
5415 };
5416
5417 methods.push(ClassMethod {
5418 name: method_name,
5419 params,
5420 body,
5421 visibility,
5422 is_static: false,
5423 is_final: false,
5424 });
5425
5426 self.eat(&Token::Comma);
5427 self.eat(&Token::Semicolon);
5428 }
5429
5430 self.expect(&Token::RBrace)?;
5431 self.eat(&Token::Semicolon);
5432
5433 Ok(Statement {
5434 label: None,
5435 kind: StmtKind::TraitDecl {
5436 def: TraitDef { name, methods },
5437 },
5438 line,
5439 })
5440 }
5441
5442 fn local_simple_target_to_var_decl(target: &Expr) -> Option<VarDecl> {
5443 match &target.kind {
5444 ExprKind::ScalarVar(name) => Some(VarDecl {
5445 sigil: Sigil::Scalar,
5446 name: name.clone(),
5447 initializer: None,
5448 frozen: false,
5449 type_annotation: None,
5450 list_context: false,
5451 }),
5452 ExprKind::ArrayVar(name) => Some(VarDecl {
5453 sigil: Sigil::Array,
5454 name: name.clone(),
5455 initializer: None,
5456 frozen: false,
5457 type_annotation: None,
5458 list_context: false,
5459 }),
5460 ExprKind::HashVar(name) => Some(VarDecl {
5461 sigil: Sigil::Hash,
5462 name: name.clone(),
5463 initializer: None,
5464 frozen: false,
5465 type_annotation: None,
5466 list_context: false,
5467 }),
5468 ExprKind::Typeglob(name) => Some(VarDecl {
5469 sigil: Sigil::Typeglob,
5470 name: name.clone(),
5471 initializer: None,
5472 frozen: false,
5473 type_annotation: None,
5474 list_context: false,
5475 }),
5476 _ => None,
5477 }
5478 }
5479
5480 fn parse_decl_array_destructure(
5481 &mut self,
5482 keyword: &str,
5483 line: usize,
5484 ) -> StrykeResult<Statement> {
5485 self.expect(&Token::LBracket)?;
5486 let elems = self.parse_match_array_elems_until_rbracket()?;
5487 self.expect(&Token::Assign)?;
5488 self.suppress_scalar_hash_brace += 1;
5489 let rhs = self.parse_expression()?;
5490 self.suppress_scalar_hash_brace -= 1;
5491 let stmt = self.desugar_array_destructure(keyword, line, elems, rhs)?;
5492 self.parse_stmt_postfix_modifier(stmt)
5493 }
5494
5495 fn parse_decl_hash_destructure(
5496 &mut self,
5497 keyword: &str,
5498 line: usize,
5499 ) -> StrykeResult<Statement> {
5500 let MatchPattern::Hash(pairs) = self.parse_match_hash_pattern()? else {
5501 unreachable!("parse_match_hash_pattern returns Hash");
5502 };
5503 self.expect(&Token::Assign)?;
5504 self.suppress_scalar_hash_brace += 1;
5505 let rhs = self.parse_expression()?;
5506 self.suppress_scalar_hash_brace -= 1;
5507 let stmt = self.desugar_hash_destructure(keyword, line, pairs, rhs)?;
5508 self.parse_stmt_postfix_modifier(stmt)
5509 }
5510
5511 fn desugar_array_destructure(
5512 &mut self,
5513 keyword: &str,
5514 line: usize,
5515 elems: Vec<MatchArrayElem>,
5516 rhs: Expr,
5517 ) -> StrykeResult<Statement> {
5518 let tmp = format!("__stryke_ds_{}", self.alloc_desugar_tmp());
5519 let mut stmts: Vec<Statement> = Vec::new();
5520 stmts.push(destructure_stmt_from_var_decls(
5521 keyword,
5522 vec![VarDecl {
5523 sigil: Sigil::Scalar,
5524 name: tmp.clone(),
5525 initializer: Some(rhs),
5526 frozen: false,
5527 type_annotation: None,
5528 list_context: false,
5529 }],
5530 line,
5531 ));
5532
5533 let has_rest = elems
5534 .iter()
5535 .any(|e| matches!(e, MatchArrayElem::Rest | MatchArrayElem::RestBind(_)));
5536 let fixed_slots = elems
5537 .iter()
5538 .filter(|e| {
5539 matches!(
5540 e,
5541 MatchArrayElem::CaptureScalar(_) | MatchArrayElem::Expr(_)
5542 )
5543 })
5544 .count();
5545 if !has_rest {
5546 let cond = Expr {
5547 kind: ExprKind::BinOp {
5548 left: Box::new(destructure_expr_array_len(&tmp, line)),
5549 op: BinOp::NumEq,
5550 right: Box::new(Expr {
5551 kind: ExprKind::Integer(fixed_slots as i64),
5552 line,
5553 }),
5554 },
5555 line,
5556 };
5557 stmts.push(destructure_stmt_unless_die(
5558 line,
5559 cond,
5560 "array destructure: length mismatch",
5561 ));
5562 }
5563
5564 let mut idx: i64 = 0;
5565 for elem in elems {
5566 match elem {
5567 MatchArrayElem::Rest => break,
5568 MatchArrayElem::RestBind(name) => {
5569 let list_source = Expr {
5570 kind: ExprKind::Deref {
5571 expr: Box::new(destructure_expr_scalar_tmp(&tmp, line)),
5572 kind: Sigil::Array,
5573 },
5574 line,
5575 };
5576 let last_ix = Expr {
5577 kind: ExprKind::BinOp {
5578 left: Box::new(destructure_expr_array_len(&tmp, line)),
5579 op: BinOp::Sub,
5580 right: Box::new(Expr {
5581 kind: ExprKind::Integer(1),
5582 line,
5583 }),
5584 },
5585 line,
5586 };
5587 let range = Expr {
5588 kind: ExprKind::Range {
5589 from: Box::new(Expr {
5590 kind: ExprKind::Integer(idx),
5591 line,
5592 }),
5593 to: Box::new(last_ix),
5594 exclusive: false,
5595 step: None,
5596 },
5597 line,
5598 };
5599 let slice = Expr {
5600 kind: ExprKind::AnonymousListSlice {
5601 source: Box::new(list_source),
5602 indices: vec![range],
5603 },
5604 line,
5605 };
5606 stmts.push(destructure_stmt_from_var_decls(
5607 keyword,
5608 vec![VarDecl {
5609 sigil: Sigil::Array,
5610 name,
5611 initializer: Some(slice),
5612 frozen: false,
5613 type_annotation: None,
5614 list_context: false,
5615 }],
5616 line,
5617 ));
5618 break;
5619 }
5620 MatchArrayElem::CaptureScalar(name) => {
5621 let arrow = Expr {
5622 kind: ExprKind::ArrowDeref {
5623 expr: Box::new(destructure_expr_scalar_tmp(&tmp, line)),
5624 index: Box::new(Expr {
5625 kind: ExprKind::Integer(idx),
5626 line,
5627 }),
5628 kind: DerefKind::Array,
5629 },
5630 line,
5631 };
5632 stmts.push(destructure_stmt_from_var_decls(
5633 keyword,
5634 vec![VarDecl {
5635 sigil: Sigil::Scalar,
5636 name,
5637 initializer: Some(arrow),
5638 frozen: false,
5639 list_context: false,
5640 type_annotation: None,
5641 }],
5642 line,
5643 ));
5644 idx += 1;
5645 }
5646 MatchArrayElem::Expr(e) => {
5647 let elem_subj = Expr {
5648 kind: ExprKind::ArrowDeref {
5649 expr: Box::new(destructure_expr_scalar_tmp(&tmp, line)),
5650 index: Box::new(Expr {
5651 kind: ExprKind::Integer(idx),
5652 line,
5653 }),
5654 kind: DerefKind::Array,
5655 },
5656 line,
5657 };
5658 let match_expr = Expr {
5659 kind: ExprKind::AlgebraicMatch {
5660 subject: Box::new(elem_subj),
5661 arms: vec![
5662 MatchArm {
5663 pattern: MatchPattern::Value(Box::new(e.clone())),
5664 guard: None,
5665 body: Expr {
5666 kind: ExprKind::Integer(0),
5667 line,
5668 },
5669 },
5670 MatchArm {
5671 pattern: MatchPattern::Any,
5672 guard: None,
5673 body: Expr {
5674 kind: ExprKind::Die(vec![Expr {
5675 kind: ExprKind::String(
5676 "array destructure: element pattern mismatch"
5677 .to_string(),
5678 ),
5679 line,
5680 }]),
5681 line,
5682 },
5683 },
5684 ],
5685 },
5686 line,
5687 };
5688 stmts.push(Statement {
5689 label: None,
5690 kind: StmtKind::Expression(match_expr),
5691 line,
5692 });
5693 idx += 1;
5694 }
5695 }
5696 }
5697
5698 Ok(Statement {
5699 label: None,
5700 kind: StmtKind::StmtGroup(stmts),
5701 line,
5702 })
5703 }
5704
5705 fn desugar_hash_destructure(
5706 &mut self,
5707 keyword: &str,
5708 line: usize,
5709 pairs: Vec<MatchHashPair>,
5710 rhs: Expr,
5711 ) -> StrykeResult<Statement> {
5712 let tmp = format!("__stryke_ds_{}", self.alloc_desugar_tmp());
5713 let mut stmts: Vec<Statement> = Vec::new();
5714 stmts.push(destructure_stmt_from_var_decls(
5715 keyword,
5716 vec![VarDecl {
5717 sigil: Sigil::Scalar,
5718 name: tmp.clone(),
5719 initializer: Some(rhs),
5720 frozen: false,
5721 type_annotation: None,
5722 list_context: false,
5723 }],
5724 line,
5725 ));
5726
5727 for pair in pairs {
5728 match pair {
5729 MatchHashPair::KeyOnly { key } => {
5730 let exists_op = Expr {
5731 kind: ExprKind::Exists(Box::new(Expr {
5732 kind: ExprKind::ArrowDeref {
5733 expr: Box::new(destructure_expr_scalar_tmp(&tmp, line)),
5734 index: Box::new(key),
5735 kind: DerefKind::Hash,
5736 },
5737 line,
5738 })),
5739 line,
5740 };
5741 stmts.push(destructure_stmt_unless_die(
5742 line,
5743 exists_op,
5744 "hash destructure: missing required key",
5745 ));
5746 }
5747 MatchHashPair::Capture { key, name } => {
5748 let init = Expr {
5749 kind: ExprKind::ArrowDeref {
5750 expr: Box::new(destructure_expr_scalar_tmp(&tmp, line)),
5751 index: Box::new(key),
5752 kind: DerefKind::Hash,
5753 },
5754 line,
5755 };
5756 stmts.push(destructure_stmt_from_var_decls(
5757 keyword,
5758 vec![VarDecl {
5759 sigil: Sigil::Scalar,
5760 name,
5761 initializer: Some(init),
5762 frozen: false,
5763 type_annotation: None,
5764 list_context: false,
5765 }],
5766 line,
5767 ));
5768 }
5769 }
5770 }
5771
5772 Ok(Statement {
5773 label: None,
5774 kind: StmtKind::StmtGroup(stmts),
5775 line,
5776 })
5777 }
5778
5779 fn parse_my_our_local(
5780 &mut self,
5781 keyword: &str,
5782 allow_type_annotation: bool,
5783 ) -> StrykeResult<Statement> {
5784 let line = self.peek_line();
5785 self.advance(); if keyword == "local"
5788 && !matches!(self.peek(), Token::LParen | Token::LBracket | Token::LBrace)
5789 {
5790 let target = self.parse_postfix()?;
5791 let mut initializer: Option<Expr> = None;
5792 if self.eat(&Token::Assign) {
5793 initializer = Some(self.parse_expression()?);
5794 } else if matches!(
5795 self.peek(),
5796 Token::OrAssign | Token::DefinedOrAssign | Token::AndAssign
5797 ) {
5798 if matches!(&target.kind, ExprKind::Typeglob(_)) {
5799 return Err(self.syntax_err(
5800 "compound assignment on typeglob declaration is not supported",
5801 self.peek_line(),
5802 ));
5803 }
5804 let op = match self.peek().clone() {
5805 Token::OrAssign => BinOp::LogOr,
5806 Token::DefinedOrAssign => BinOp::DefinedOr,
5807 Token::AndAssign => BinOp::LogAnd,
5808 _ => unreachable!(),
5809 };
5810 self.advance();
5811 let rhs = self.parse_assign_expr()?;
5812 let tgt_line = target.line;
5813 initializer = Some(Expr {
5814 kind: ExprKind::CompoundAssign {
5815 target: Box::new(target.clone()),
5816 op,
5817 value: Box::new(rhs),
5818 },
5819 line: tgt_line,
5820 });
5821 }
5822
5823 let kind = if let Some(mut decl) = Self::local_simple_target_to_var_decl(&target) {
5824 decl.initializer = initializer;
5825 StmtKind::Local(vec![decl])
5826 } else {
5827 StmtKind::LocalExpr {
5828 target,
5829 initializer,
5830 }
5831 };
5832 let stmt = Statement {
5833 label: None,
5834 kind,
5835 line,
5836 };
5837 return self.parse_stmt_postfix_modifier(stmt);
5838 }
5839
5840 if matches!(self.peek(), Token::LBracket) {
5841 return self.parse_decl_array_destructure(keyword, line);
5842 }
5843 if matches!(self.peek(), Token::LBrace) {
5844 return self.parse_decl_hash_destructure(keyword, line);
5845 }
5846
5847 let mut decls = Vec::new();
5848 let used_parens = self.eat(&Token::LParen);
5849
5850 if used_parens {
5851 while !matches!(self.peek(), Token::RParen | Token::Eof) {
5853 let decl = self.parse_var_decl(allow_type_annotation)?;
5854 decls.push(decl);
5855 if !self.eat(&Token::Comma) {
5856 break;
5857 }
5858 }
5859 self.expect(&Token::RParen)?;
5860 } else {
5861 decls.push(self.parse_var_decl(allow_type_annotation)?);
5862 }
5863 if used_parens {
5865 for decl in &mut decls {
5866 decl.list_context = true;
5867 }
5868 }
5869
5870 if self.eat(&Token::Assign) {
5872 if keyword == "our" && decls.len() == 1 {
5873 while matches!(self.peek(), Token::Ident(ref i) if i == "our") {
5874 self.advance();
5875 decls.push(self.parse_var_decl(allow_type_annotation)?);
5876 if !self.eat(&Token::Assign) {
5877 return Err(self.syntax_err(
5878 "expected `=` after `our` in chained our-declaration",
5879 self.peek_line(),
5880 ));
5881 }
5882 }
5883 }
5884 let rhs_start_pos = self.pos;
5885 let mut val = self.parse_expression()?;
5886 let rhs_end_pos = self.pos;
5887 if !crate::compat_mode()
5908 && self.block_depth == 0
5909 && decls.len() == 1
5910 && matches!(decls[0].sigil, Sigil::Scalar)
5911 && !matches!(
5912 val.kind,
5913 ExprKind::CodeRef { .. }
5914 | ExprKind::SubroutineRef(_)
5915 | ExprKind::SubroutineCodeRef(_)
5916 | ExprKind::DynamicSubCodeRef(_)
5917 )
5918 && self.rhs_has_free_bare_topic_slot(rhs_start_pos, rhs_end_pos)
5919 {
5920 let val_line = val.line;
5921 val = Expr {
5922 kind: ExprKind::CodeRef {
5923 params: Vec::new(),
5924 body: vec![Statement {
5925 label: None,
5926 kind: StmtKind::Expression(val),
5927 line: val_line,
5928 }],
5929 },
5930 line: val_line,
5931 };
5932 }
5933 if !crate::compat_mode() && decls.len() == 1 {
5936 let decl = &decls[0];
5937 let target_kind = match decl.sigil {
5938 Sigil::Scalar => ExprKind::ScalarVar(decl.name.clone()),
5939 Sigil::Array => ExprKind::ArrayVar(decl.name.clone()),
5940 Sigil::Hash => ExprKind::HashVar(decl.name.clone()),
5941 Sigil::Typeglob => {
5942 if decls.len() == 1 {
5944 decls[0].initializer = Some(val);
5945 } else {
5946 for d in &mut decls {
5947 d.initializer = Some(val.clone());
5948 }
5949 }
5950 return Ok(Statement {
5951 label: None,
5952 kind: match keyword {
5953 "my" => StmtKind::My(decls),
5954 "mysync" => StmtKind::MySync(decls),
5955 "our" => StmtKind::Our(decls),
5956 "oursync" => StmtKind::OurSync(decls),
5957 "local" => StmtKind::Local(decls),
5958 "state" => StmtKind::State(decls),
5959 _ => unreachable!(),
5960 },
5961 line,
5962 });
5963 }
5964 };
5965 let target = Expr {
5966 kind: target_kind,
5967 line,
5968 };
5969 self.validate_assignment(&target, &val, line)?;
5970 }
5971 if decls.len() == 1 {
5972 decls[0].initializer = Some(val);
5973 } else {
5974 for decl in &mut decls {
5975 decl.initializer = Some(val.clone());
5976 }
5977 }
5978 } else if decls.len() == 1 {
5979 let op = match self.peek().clone() {
5981 Token::OrAssign => Some(BinOp::LogOr),
5982 Token::DefinedOrAssign => Some(BinOp::DefinedOr),
5983 Token::AndAssign => Some(BinOp::LogAnd),
5984 _ => None,
5985 };
5986 if let Some(op) = op {
5987 let d = &decls[0];
5988 if matches!(d.sigil, Sigil::Typeglob) {
5989 return Err(self.syntax_err(
5990 "compound assignment on typeglob declaration is not supported",
5991 self.peek_line(),
5992 ));
5993 }
5994 self.advance();
5995 let rhs = self.parse_assign_expr()?;
5996 let target = Expr {
5997 kind: match d.sigil {
5998 Sigil::Scalar => ExprKind::ScalarVar(d.name.clone()),
5999 Sigil::Array => ExprKind::ArrayVar(d.name.clone()),
6000 Sigil::Hash => ExprKind::HashVar(d.name.clone()),
6001 Sigil::Typeglob => unreachable!(),
6002 },
6003 line,
6004 };
6005 decls[0].initializer = Some(Expr {
6006 kind: ExprKind::CompoundAssign {
6007 target: Box::new(target),
6008 op,
6009 value: Box::new(rhs),
6010 },
6011 line,
6012 });
6013 }
6014 }
6015
6016 let kind = match keyword {
6017 "my" => StmtKind::My(decls),
6018 "mysync" => StmtKind::MySync(decls),
6019 "our" => StmtKind::Our(decls),
6020 "oursync" => StmtKind::OurSync(decls),
6021 "local" => StmtKind::Local(decls),
6022 "state" => StmtKind::State(decls),
6023 _ => unreachable!(),
6024 };
6025 let stmt = Statement {
6026 label: None,
6027 kind,
6028 line,
6029 };
6030 self.parse_stmt_postfix_modifier(stmt)
6032 }
6033
6034 fn parse_var_decl(&mut self, allow_type_annotation: bool) -> StrykeResult<VarDecl> {
6035 let mut decl = match self.advance() {
6036 (Token::ScalarVar(name), _) => VarDecl {
6037 sigil: Sigil::Scalar,
6038 name,
6039 initializer: None,
6040 frozen: false,
6041 type_annotation: None,
6042 list_context: false,
6043 },
6044 (Token::ArrayVar(name), _) => VarDecl {
6045 sigil: Sigil::Array,
6046 name,
6047 initializer: None,
6048 frozen: false,
6049 type_annotation: None,
6050 list_context: false,
6051 },
6052 (Token::HashVar(name), line) => {
6053 if !crate::compat_mode() {
6054 self.check_hash_shadows_reserved(&name, line)?;
6055 }
6056 VarDecl {
6057 sigil: Sigil::Hash,
6058 name,
6059 initializer: None,
6060 frozen: false,
6061 type_annotation: None,
6062 list_context: false,
6063 }
6064 }
6065 (Token::Star, _line) => {
6066 let name = match self.advance() {
6067 (Token::Ident(n), _) => n,
6068 (tok, l) => {
6069 return Err(self
6070 .syntax_err(format!("Expected identifier after *, got {:?}", tok), l));
6071 }
6072 };
6073 VarDecl {
6074 sigil: Sigil::Typeglob,
6075 name,
6076 initializer: None,
6077 frozen: false,
6078 type_annotation: None,
6079 list_context: false,
6080 }
6081 }
6082 (Token::Ident(ref kw), _) if kw == "undef" => VarDecl {
6087 sigil: Sigil::Scalar,
6088 name: format!("__undef_sink_{}", self.pos),
6092 initializer: None,
6093 frozen: false,
6094 type_annotation: None,
6095 list_context: false,
6096 },
6097 (tok, line) => {
6098 return Err(self.syntax_err(
6099 format!("Expected variable in declaration, got {:?}", tok),
6100 line,
6101 ));
6102 }
6103 };
6104 if allow_type_annotation && self.eat(&Token::Colon) {
6105 let ty = self.parse_type_name()?;
6106 if decl.sigil != Sigil::Scalar {
6107 return Err(self.syntax_err(
6108 "`: Type` is only valid for scalar declarations (typed my $name : Int)",
6109 self.peek_line(),
6110 ));
6111 }
6112 decl.type_annotation = Some(ty);
6113 }
6114 Ok(decl)
6115 }
6116
6117 fn parse_type_name(&mut self) -> StrykeResult<PerlTypeName> {
6118 match self.advance() {
6119 (Token::Ident(name), _) => match name.as_str() {
6120 "Int" => Ok(PerlTypeName::Int),
6121 "Str" => Ok(PerlTypeName::Str),
6122 "Float" => Ok(PerlTypeName::Float),
6123 "Bool" => Ok(PerlTypeName::Bool),
6124 "Array" => Ok(PerlTypeName::Array),
6125 "Hash" => Ok(PerlTypeName::Hash),
6126 "Ref" => Ok(PerlTypeName::Ref),
6127 "Any" => Ok(PerlTypeName::Any),
6128 _ => Ok(PerlTypeName::Struct(name)),
6129 },
6130 (tok, err_line) => Err(self.syntax_err(
6131 format!("Expected type name after `:`, got {:?}", tok),
6132 err_line,
6133 )),
6134 }
6135 }
6136
6137 fn parse_package(&mut self) -> StrykeResult<Statement> {
6138 let line = self.peek_line();
6139 self.advance(); let name = match self.advance() {
6141 (Token::Ident(n), _) => n,
6142 (tok, line) => {
6143 return Err(self.syntax_err(format!("Expected package name, got {:?}", tok), line))
6144 }
6145 };
6146 let mut full_name = name;
6148 while self.eat(&Token::PackageSep) {
6149 if let (Token::Ident(part), _) = self.advance() {
6150 full_name = format!("{}::{}", full_name, part);
6151 }
6152 }
6153 self.eat(&Token::Semicolon);
6154 self.current_package = full_name.clone();
6157 Ok(Statement {
6158 label: None,
6159 kind: StmtKind::Package { name: full_name },
6160 line,
6161 })
6162 }
6163
6164 fn parse_use(&mut self) -> StrykeResult<Statement> {
6165 let line = self.peek_line();
6166 self.advance(); let (tok, tok_line) = self.advance();
6168 match tok {
6169 Token::Float(v) => {
6170 self.eat(&Token::Semicolon);
6171 Ok(Statement {
6172 label: None,
6173 kind: StmtKind::UsePerlVersion { version: v },
6174 line,
6175 })
6176 }
6177 Token::Integer(n) => {
6178 if matches!(self.peek(), Token::Semicolon | Token::Eof) {
6179 self.eat(&Token::Semicolon);
6180 Ok(Statement {
6181 label: None,
6182 kind: StmtKind::UsePerlVersion { version: n as f64 },
6183 line,
6184 })
6185 } else {
6186 Err(self.syntax_err(
6187 format!("Expected ';' after use VERSION (got {:?})", self.peek()),
6188 line,
6189 ))
6190 }
6191 }
6192 Token::Ident(n) => {
6193 let mut full_name = n;
6194 while self.eat(&Token::PackageSep) {
6195 if let (Token::Ident(part), _) = self.advance() {
6196 full_name = format!("{}::{}", full_name, part);
6197 }
6198 }
6199 if full_name == "overload" {
6200 let mut pairs = Vec::new();
6201 let mut parse_overload_pairs = |this: &mut Self| -> StrykeResult<()> {
6202 loop {
6203 if matches!(this.peek(), Token::RParen | Token::Semicolon | Token::Eof)
6204 {
6205 break;
6206 }
6207 let key_e = this.parse_assign_expr()?;
6208 this.expect(&Token::FatArrow)?;
6209 let val_e = this.parse_assign_expr()?;
6210 let key = this.expr_to_overload_key(&key_e)?;
6211 let val = this.expr_to_overload_sub(&val_e)?;
6212 pairs.push((key, val));
6213 if !this.eat(&Token::Comma) {
6214 break;
6215 }
6216 }
6217 Ok(())
6218 };
6219 if self.eat(&Token::LParen) {
6220 parse_overload_pairs(self)?;
6222 self.expect(&Token::RParen)?;
6223 } else if !matches!(self.peek(), Token::Semicolon | Token::Eof) {
6224 parse_overload_pairs(self)?;
6225 }
6226 self.eat(&Token::Semicolon);
6227 return Ok(Statement {
6228 label: None,
6229 kind: StmtKind::UseOverload { pairs },
6230 line,
6231 });
6232 }
6233 let mut imports = Vec::new();
6234 let on_same_line = self.peek_line() == tok_line;
6242 if on_same_line
6243 && !matches!(self.peek(), Token::Semicolon | Token::Eof)
6244 && !self.next_is_new_statement_start(tok_line)
6245 {
6246 loop {
6247 if matches!(self.peek(), Token::Semicolon | Token::Eof) {
6248 break;
6249 }
6250 imports.push(self.parse_expression()?);
6251 if !self.eat(&Token::Comma) {
6252 break;
6253 }
6254 }
6255 }
6256 self.eat(&Token::Semicolon);
6257 Ok(Statement {
6258 label: None,
6259 kind: StmtKind::Use {
6260 module: full_name,
6261 imports,
6262 },
6263 line,
6264 })
6265 }
6266 other => Err(self.syntax_err(
6267 format!("Expected module name or version after use, got {:?}", other),
6268 tok_line,
6269 )),
6270 }
6271 }
6272
6273 fn parse_no(&mut self) -> StrykeResult<Statement> {
6274 let line = self.peek_line();
6275 self.advance(); let module = match self.advance() {
6277 (Token::Ident(n), tok_line) => (n, tok_line),
6278 (tok, line) => {
6279 return Err(self.syntax_err(
6280 format!("Expected module name after no, got {:?}", tok),
6281 line,
6282 ))
6283 }
6284 };
6285 let (module_name, tok_line) = module;
6286 let mut full_name = module_name;
6287 while self.eat(&Token::PackageSep) {
6288 if let (Token::Ident(part), _) = self.advance() {
6289 full_name = format!("{}::{}", full_name, part);
6290 }
6291 }
6292 let mut imports = Vec::new();
6293 if !matches!(self.peek(), Token::Semicolon | Token::Eof)
6294 && !self.next_is_new_statement_start(tok_line)
6295 {
6296 loop {
6297 if matches!(self.peek(), Token::Semicolon | Token::Eof) {
6298 break;
6299 }
6300 imports.push(self.parse_expression()?);
6301 if !self.eat(&Token::Comma) {
6302 break;
6303 }
6304 }
6305 }
6306 self.eat(&Token::Semicolon);
6307 Ok(Statement {
6308 label: None,
6309 kind: StmtKind::No {
6310 module: full_name,
6311 imports,
6312 },
6313 line,
6314 })
6315 }
6316
6317 fn parse_return(&mut self) -> StrykeResult<Statement> {
6318 let line = self.peek_line();
6319 self.advance(); let val = if matches!(self.peek(), Token::Semicolon | Token::RBrace | Token::Eof)
6326 || self.peek_is_postfix_stmt_modifier_keyword()
6327 {
6328 None
6329 } else {
6330 let first = self.parse_assign_expr()?;
6335 if matches!(self.peek(), Token::Comma | Token::FatArrow) {
6336 let mut items = vec![first];
6337 while self.eat(&Token::Comma) || self.eat(&Token::FatArrow) {
6338 if matches!(self.peek(), Token::Semicolon | Token::RBrace | Token::Eof)
6339 || self.peek_is_postfix_stmt_modifier_keyword()
6340 {
6341 break;
6342 }
6343 items.push(self.parse_assign_expr()?);
6344 }
6345 let line = items.first().map(|e| e.line).unwrap_or(line);
6346 Some(Expr {
6347 kind: ExprKind::List(items),
6348 line,
6349 })
6350 } else {
6351 Some(first)
6352 }
6353 };
6354 let stmt = Statement {
6356 label: None,
6357 kind: StmtKind::Return(val),
6358 line,
6359 };
6360 if let Token::Ident(ref kw) = self.peek().clone() {
6361 match kw.as_str() {
6362 "if" => {
6363 self.advance();
6364 let cond = self.parse_expression()?;
6365 self.eat(&Token::Semicolon);
6366 return Ok(Statement {
6367 label: None,
6368 kind: StmtKind::If {
6369 condition: cond,
6370 body: vec![stmt],
6371 elsifs: vec![],
6372 else_block: None,
6373 },
6374 line,
6375 });
6376 }
6377 "unless" => {
6378 self.advance();
6379 let cond = self.parse_expression()?;
6380 self.eat(&Token::Semicolon);
6381 return Ok(Statement {
6382 label: None,
6383 kind: StmtKind::Unless {
6384 condition: cond,
6385 body: vec![stmt],
6386 else_block: None,
6387 },
6388 line,
6389 });
6390 }
6391 _ => {}
6392 }
6393 }
6394 self.eat(&Token::Semicolon);
6395 Ok(stmt)
6396 }
6397
6398 fn parse_expression(&mut self) -> StrykeResult<Expr> {
6401 self.parse_comma_expr()
6402 }
6403
6404 fn parse_comma_expr(&mut self) -> StrykeResult<Expr> {
6405 let expr = self.parse_or_word()?;
6413 let mut exprs = vec![expr];
6414 while self.eat(&Token::Comma) || self.eat(&Token::FatArrow) {
6415 if matches!(
6416 self.peek(),
6417 Token::RParen | Token::RBracket | Token::RBrace | Token::Semicolon | Token::Eof
6418 ) {
6419 break; }
6421 exprs.push(self.parse_or_word()?);
6422 }
6423 if exprs.len() == 1 {
6424 return Ok(exprs.pop().unwrap());
6425 }
6426 let line = exprs[0].line;
6427 Ok(Expr {
6428 kind: ExprKind::List(exprs),
6429 line,
6430 })
6431 }
6432
6433 fn parse_assign_expr(&mut self) -> StrykeResult<Expr> {
6434 let expr = self.parse_ternary()?;
6435 let line = expr.line;
6436
6437 match self.peek().clone() {
6438 Token::Assign => {
6439 self.advance();
6440 let right = self.parse_assign_expr()?;
6441 if let ExprKind::MethodCall { ref args, .. } = expr.kind {
6443 if args.is_empty() {
6444 let ExprKind::MethodCall {
6446 object,
6447 method,
6448 super_call,
6449 ..
6450 } = expr.kind
6451 else {
6452 unreachable!()
6453 };
6454 return Ok(Expr {
6455 kind: ExprKind::MethodCall {
6456 object,
6457 method,
6458 args: vec![right],
6459 super_call,
6460 },
6461 line,
6462 });
6463 }
6464 }
6465 self.validate_assignment(&expr, &right, line)?;
6466 Ok(Expr {
6467 kind: ExprKind::Assign {
6468 target: Box::new(expr),
6469 value: Box::new(right),
6470 },
6471 line,
6472 })
6473 }
6474 Token::PlusAssign => {
6475 self.advance();
6476 let r = self.parse_assign_expr()?;
6477 Ok(Expr {
6478 kind: ExprKind::CompoundAssign {
6479 target: Box::new(expr),
6480 op: BinOp::Add,
6481 value: Box::new(r),
6482 },
6483 line,
6484 })
6485 }
6486 Token::MinusAssign => {
6487 self.advance();
6488 let r = self.parse_assign_expr()?;
6489 Ok(Expr {
6490 kind: ExprKind::CompoundAssign {
6491 target: Box::new(expr),
6492 op: BinOp::Sub,
6493 value: Box::new(r),
6494 },
6495 line,
6496 })
6497 }
6498 Token::MulAssign => {
6499 self.advance();
6500 let r = self.parse_assign_expr()?;
6501 Ok(Expr {
6502 kind: ExprKind::CompoundAssign {
6503 target: Box::new(expr),
6504 op: BinOp::Mul,
6505 value: Box::new(r),
6506 },
6507 line,
6508 })
6509 }
6510 Token::DivAssign => {
6511 self.advance();
6512 let r = self.parse_assign_expr()?;
6513 Ok(Expr {
6514 kind: ExprKind::CompoundAssign {
6515 target: Box::new(expr),
6516 op: BinOp::Div,
6517 value: Box::new(r),
6518 },
6519 line,
6520 })
6521 }
6522 Token::ModAssign => {
6523 self.advance();
6524 let r = self.parse_assign_expr()?;
6525 Ok(Expr {
6526 kind: ExprKind::CompoundAssign {
6527 target: Box::new(expr),
6528 op: BinOp::Mod,
6529 value: Box::new(r),
6530 },
6531 line,
6532 })
6533 }
6534 Token::PowAssign => {
6535 self.advance();
6536 let r = self.parse_assign_expr()?;
6537 Ok(Expr {
6538 kind: ExprKind::CompoundAssign {
6539 target: Box::new(expr),
6540 op: BinOp::Pow,
6541 value: Box::new(r),
6542 },
6543 line,
6544 })
6545 }
6546 Token::XAssign => {
6547 self.advance();
6552 let r = self.parse_assign_expr()?;
6553 let lhs_for_repeat = expr.clone();
6554 Ok(Expr {
6555 kind: ExprKind::Assign {
6556 target: Box::new(expr),
6557 value: Box::new(Expr {
6558 kind: ExprKind::Repeat {
6559 expr: Box::new(lhs_for_repeat),
6560 count: Box::new(r),
6561 list_repeat: false,
6562 },
6563 line,
6564 }),
6565 },
6566 line,
6567 })
6568 }
6569 Token::DotAssign => {
6570 self.advance();
6571 let r = self.parse_assign_expr()?;
6572 Ok(Expr {
6573 kind: ExprKind::CompoundAssign {
6574 target: Box::new(expr),
6575 op: BinOp::Concat,
6576 value: Box::new(r),
6577 },
6578 line,
6579 })
6580 }
6581 Token::BitAndAssign => {
6582 self.advance();
6583 let r = self.parse_assign_expr()?;
6584 Ok(Expr {
6585 kind: ExprKind::CompoundAssign {
6586 target: Box::new(expr),
6587 op: BinOp::BitAnd,
6588 value: Box::new(r),
6589 },
6590 line,
6591 })
6592 }
6593 Token::BitOrAssign => {
6594 self.advance();
6595 let r = self.parse_assign_expr()?;
6596 Ok(Expr {
6597 kind: ExprKind::CompoundAssign {
6598 target: Box::new(expr),
6599 op: BinOp::BitOr,
6600 value: Box::new(r),
6601 },
6602 line,
6603 })
6604 }
6605 Token::XorAssign => {
6606 self.advance();
6607 let r = self.parse_assign_expr()?;
6608 Ok(Expr {
6609 kind: ExprKind::CompoundAssign {
6610 target: Box::new(expr),
6611 op: BinOp::BitXor,
6612 value: Box::new(r),
6613 },
6614 line,
6615 })
6616 }
6617 Token::ShiftLeftAssign => {
6618 self.advance();
6619 let r = self.parse_assign_expr()?;
6620 Ok(Expr {
6621 kind: ExprKind::CompoundAssign {
6622 target: Box::new(expr),
6623 op: BinOp::ShiftLeft,
6624 value: Box::new(r),
6625 },
6626 line,
6627 })
6628 }
6629 Token::ShiftRightAssign => {
6630 self.advance();
6631 let r = self.parse_assign_expr()?;
6632 Ok(Expr {
6633 kind: ExprKind::CompoundAssign {
6634 target: Box::new(expr),
6635 op: BinOp::ShiftRight,
6636 value: Box::new(r),
6637 },
6638 line,
6639 })
6640 }
6641 Token::OrAssign => {
6642 self.advance();
6643 let r = self.parse_assign_expr()?;
6644 Ok(Expr {
6645 kind: ExprKind::CompoundAssign {
6646 target: Box::new(expr),
6647 op: BinOp::LogOr,
6648 value: Box::new(r),
6649 },
6650 line,
6651 })
6652 }
6653 Token::DefinedOrAssign => {
6654 self.advance();
6655 let r = self.parse_assign_expr()?;
6656 Ok(Expr {
6657 kind: ExprKind::CompoundAssign {
6658 target: Box::new(expr),
6659 op: BinOp::DefinedOr,
6660 value: Box::new(r),
6661 },
6662 line,
6663 })
6664 }
6665 Token::AndAssign => {
6666 self.advance();
6667 let r = self.parse_assign_expr()?;
6668 Ok(Expr {
6669 kind: ExprKind::CompoundAssign {
6670 target: Box::new(expr),
6671 op: BinOp::LogAnd,
6672 value: Box::new(r),
6673 },
6674 line,
6675 })
6676 }
6677 _ => Ok(expr),
6678 }
6679 }
6680
6681 fn parse_ternary(&mut self) -> StrykeResult<Expr> {
6682 let expr = self.parse_pipe_forward()?;
6683 if self.eat(&Token::Question) {
6684 let line = expr.line;
6685 self.suppress_colon_range = self.suppress_colon_range.saturating_add(1);
6686 let then_expr = self.parse_assign_expr();
6687 self.suppress_colon_range = self.suppress_colon_range.saturating_sub(1);
6688 let then_expr = then_expr?;
6689 self.expect(&Token::Colon)?;
6690 let else_expr = self.parse_assign_expr()?;
6691 return Ok(Expr {
6692 kind: ExprKind::Ternary {
6693 condition: Box::new(expr),
6694 then_expr: Box::new(then_expr),
6695 else_expr: Box::new(else_expr),
6696 },
6697 line,
6698 });
6699 }
6700 Ok(expr)
6701 }
6702
6703 fn parse_pipe_forward(&mut self) -> StrykeResult<Expr> {
6709 let mut left = self.parse_range()?;
6716 if self.no_pipe_forward_depth > 0 {
6722 return Ok(left);
6723 }
6724 while matches!(self.peek(), Token::PipeForward) {
6725 if crate::compat_mode() {
6726 return Err(self.syntax_err(
6727 "pipe-forward operator `|>` is a stryke extension (disabled by --compat)",
6728 left.line,
6729 ));
6730 }
6731 let line = left.line;
6732 self.advance();
6733 self.pipe_rhs_depth = self.pipe_rhs_depth.saturating_add(1);
6736 let right_result = self.parse_range();
6740 self.pipe_rhs_depth = self.pipe_rhs_depth.saturating_sub(1);
6741 let right = right_result?;
6742 left = self.pipe_forward_apply(left, right, line)?;
6743 }
6744 Ok(left)
6745 }
6746
6747 fn pipe_forward_apply(&self, lhs: Expr, rhs: Expr, line: usize) -> StrykeResult<Expr> {
6769 let Expr { kind, line: rline } = rhs;
6770 let new_kind = match kind {
6771 ExprKind::FuncCall { name, mut args } => {
6773 let dispatch_name: &str = name.strip_prefix("CORE::").unwrap_or(name.as_str());
6776 match dispatch_name {
6777 "puniq" | "uniq" | "distinct" | "flatten" | "set" | "list_count"
6778 | "list_size" | "count" | "size" | "cnt" | "len" | "with_index" | "shuffle"
6779 | "shuffled" | "frequencies" | "freq" | "pfrequencies" | "pfreq"
6780 | "interleave" | "ddump" | "stringify" | "str" | "lines" | "words"
6781 | "chars" | "digits" | "letters" | "letters_uc" | "letters_lc"
6782 | "punctuation" | "numbers" | "graphemes" | "columns" | "sentences"
6783 | "paragraphs" | "sections" | "trim" | "avg" | "to_json" | "to_csv"
6784 | "to_toml" | "to_yaml" | "to_xml" | "to_html" | "from_json" | "from_csv"
6785 | "from_toml" | "from_yaml" | "from_xml" | "to_markdown" | "to_table"
6786 | "xopen" | "clip" | "sparkline" | "bar_chart" | "flame" | "stddev"
6787 | "squared" | "sq" | "square" | "cubed" | "cb" | "cube" | "normalize"
6788 | "snake_case" | "camel_case" | "kebab_case" => {
6789 if args.is_empty() {
6790 args.push(lhs);
6791 } else {
6792 args[0] = lhs;
6793 }
6794 }
6795 "chunked" | "windowed" => {
6796 if args.is_empty() {
6797 return Err(self.syntax_err(
6798 "|>: chunked(N) / windowed(N) needs size — e.g. `@a |> windowed(2)`",
6799 line,
6800 ));
6801 }
6802 args.insert(0, lhs);
6803 }
6804 "reduce" | "fold" => {
6805 args.push(lhs);
6806 }
6807 "grep_v" | "pluck" | "tee" | "nth" | "chunk" => {
6808 args.push(lhs);
6814 }
6815 "enumerate" | "dedup" => {
6816 args.insert(0, lhs);
6819 }
6820 "clamp" => {
6821 args.push(lhs);
6823 }
6824 n if Self::is_block_then_list_pipe_builtin(n) => {
6825 if args.len() < 2 {
6826 return Err(self.syntax_err(
6827 format!(
6828 "|>: `{name}` needs {{ BLOCK }}, LIST so the list can receive the pipe"
6829 ),
6830 line,
6831 ));
6832 }
6833 args[1] = lhs;
6834 }
6835 "take" | "head" | "tail" | "drop" => {
6836 if args.is_empty() {
6837 return Err(self.syntax_err(
6838 "|>: `{name}` needs N last — e.g. `@a |> take(3)` for `take(@a, 3)`",
6839 line,
6840 ));
6841 }
6842 args.insert(0, lhs);
6844 }
6845 _ => {
6846 if self.thread_last_mode {
6847 args.push(lhs);
6848 } else {
6849 args.insert(0, lhs);
6850 }
6851 }
6852 }
6853 ExprKind::FuncCall { name, args }
6854 }
6855 ExprKind::MethodCall {
6856 object,
6857 method,
6858 mut args,
6859 super_call,
6860 } => {
6861 if self.thread_last_mode {
6862 args.push(lhs);
6863 } else {
6864 args.insert(0, lhs);
6865 }
6866 ExprKind::MethodCall {
6867 object,
6868 method,
6869 args,
6870 super_call,
6871 }
6872 }
6873 ExprKind::IndirectCall {
6874 target,
6875 mut args,
6876 ampersand,
6877 pass_caller_arglist: _,
6878 } => {
6879 if self.thread_last_mode {
6880 args.push(lhs);
6881 } else {
6882 args.insert(0, lhs);
6883 }
6884 ExprKind::IndirectCall {
6885 target,
6886 args,
6887 ampersand,
6888 pass_caller_arglist: false,
6891 }
6892 }
6893
6894 ExprKind::Print { handle, mut args } => {
6896 if self.thread_last_mode {
6897 args.push(lhs);
6898 } else {
6899 args.insert(0, lhs);
6900 }
6901 ExprKind::Print { handle, args }
6902 }
6903 ExprKind::Say { handle, mut args } => {
6904 if self.thread_last_mode {
6905 args.push(lhs);
6906 } else {
6907 args.insert(0, lhs);
6908 }
6909 ExprKind::Say { handle, args }
6910 }
6911 ExprKind::Printf { handle, mut args } => {
6912 if self.thread_last_mode {
6913 args.push(lhs);
6914 } else {
6915 args.insert(0, lhs);
6916 }
6917 ExprKind::Printf { handle, args }
6918 }
6919 ExprKind::Die(mut args) => {
6920 if self.thread_last_mode {
6921 args.push(lhs);
6922 } else {
6923 args.insert(0, lhs);
6924 }
6925 ExprKind::Die(args)
6926 }
6927 ExprKind::Warn(mut args) => {
6928 if self.thread_last_mode {
6929 args.push(lhs);
6930 } else {
6931 args.insert(0, lhs);
6932 }
6933 ExprKind::Warn(args)
6934 }
6935
6936 ExprKind::Sprintf { format, mut args } => {
6942 if self.thread_last_mode {
6943 args.push(lhs);
6944 } else {
6945 args.insert(0, lhs);
6946 }
6947 ExprKind::Sprintf { format, args }
6948 }
6949
6950 ExprKind::System(mut args) => {
6952 if self.thread_last_mode {
6953 args.push(lhs);
6954 } else {
6955 args.insert(0, lhs);
6956 }
6957 ExprKind::System(args)
6958 }
6959 ExprKind::Exec(mut args) => {
6960 if self.thread_last_mode {
6961 args.push(lhs);
6962 } else {
6963 args.insert(0, lhs);
6964 }
6965 ExprKind::Exec(args)
6966 }
6967 ExprKind::Unlink(mut args) => {
6968 if self.thread_last_mode {
6969 args.push(lhs);
6970 } else {
6971 args.insert(0, lhs);
6972 }
6973 ExprKind::Unlink(args)
6974 }
6975 ExprKind::Chmod(mut args) => {
6976 if self.thread_last_mode {
6977 args.push(lhs);
6978 } else {
6979 args.insert(0, lhs);
6980 }
6981 ExprKind::Chmod(args)
6982 }
6983 ExprKind::Chown(mut args) => {
6984 if self.thread_last_mode {
6985 args.push(lhs);
6986 } else {
6987 args.insert(0, lhs);
6988 }
6989 ExprKind::Chown(args)
6990 }
6991 ExprKind::Glob(mut args) => {
6992 if self.thread_last_mode {
6993 args.push(lhs);
6994 } else {
6995 args.insert(0, lhs);
6996 }
6997 ExprKind::Glob(args)
6998 }
6999 ExprKind::Files(mut args) => {
7000 if self.thread_last_mode {
7001 args.push(lhs);
7002 } else {
7003 args.insert(0, lhs);
7004 }
7005 ExprKind::Files(args)
7006 }
7007 ExprKind::Filesf(mut args) => {
7008 if self.thread_last_mode {
7009 args.push(lhs);
7010 } else {
7011 args.insert(0, lhs);
7012 }
7013 ExprKind::Filesf(args)
7014 }
7015 ExprKind::FilesfRecursive(mut args) => {
7016 if self.thread_last_mode {
7017 args.push(lhs);
7018 } else {
7019 args.insert(0, lhs);
7020 }
7021 ExprKind::FilesfRecursive(args)
7022 }
7023 ExprKind::Dirs(mut args) => {
7024 if self.thread_last_mode {
7025 args.push(lhs);
7026 } else {
7027 args.insert(0, lhs);
7028 }
7029 ExprKind::Dirs(args)
7030 }
7031 ExprKind::DirsRecursive(mut args) => {
7032 if self.thread_last_mode {
7033 args.push(lhs);
7034 } else {
7035 args.insert(0, lhs);
7036 }
7037 ExprKind::DirsRecursive(args)
7038 }
7039 ExprKind::SymLinks(mut args) => {
7040 if self.thread_last_mode {
7041 args.push(lhs);
7042 } else {
7043 args.insert(0, lhs);
7044 }
7045 ExprKind::SymLinks(args)
7046 }
7047 ExprKind::Sockets(mut args) => {
7048 if self.thread_last_mode {
7049 args.push(lhs);
7050 } else {
7051 args.insert(0, lhs);
7052 }
7053 ExprKind::Sockets(args)
7054 }
7055 ExprKind::Pipes(mut args) => {
7056 if self.thread_last_mode {
7057 args.push(lhs);
7058 } else {
7059 args.insert(0, lhs);
7060 }
7061 ExprKind::Pipes(args)
7062 }
7063 ExprKind::BlockDevices(mut args) => {
7064 if self.thread_last_mode {
7065 args.push(lhs);
7066 } else {
7067 args.insert(0, lhs);
7068 }
7069 ExprKind::BlockDevices(args)
7070 }
7071 ExprKind::CharDevices(mut args) => {
7072 if self.thread_last_mode {
7073 args.push(lhs);
7074 } else {
7075 args.insert(0, lhs);
7076 }
7077 ExprKind::CharDevices(args)
7078 }
7079 ExprKind::GlobPar { mut args, progress } => {
7080 if self.thread_last_mode {
7081 args.push(lhs);
7082 } else {
7083 args.insert(0, lhs);
7084 }
7085 ExprKind::GlobPar { args, progress }
7086 }
7087 ExprKind::ParSed { mut args, progress } => {
7088 if self.thread_last_mode {
7089 args.push(lhs);
7090 } else {
7091 args.insert(0, lhs);
7092 }
7093 ExprKind::ParSed { args, progress }
7094 }
7095
7096 ExprKind::Length(_) => ExprKind::Length(Box::new(lhs)),
7098 ExprKind::Abs(_) => ExprKind::Abs(Box::new(lhs)),
7099 ExprKind::Int(_) => ExprKind::Int(Box::new(lhs)),
7100 ExprKind::Sqrt(_) => ExprKind::Sqrt(Box::new(lhs)),
7101 ExprKind::Sin(_) => ExprKind::Sin(Box::new(lhs)),
7102 ExprKind::Cos(_) => ExprKind::Cos(Box::new(lhs)),
7103 ExprKind::Exp(_) => ExprKind::Exp(Box::new(lhs)),
7104 ExprKind::Log(_) => ExprKind::Log(Box::new(lhs)),
7105 ExprKind::Hex(_) => ExprKind::Hex(Box::new(lhs)),
7106 ExprKind::Oct(_) => ExprKind::Oct(Box::new(lhs)),
7107 ExprKind::Lc(_) => ExprKind::Lc(Box::new(lhs)),
7108 ExprKind::Uc(_) => ExprKind::Uc(Box::new(lhs)),
7109 ExprKind::Lcfirst(_) => ExprKind::Lcfirst(Box::new(lhs)),
7110 ExprKind::Ucfirst(_) => ExprKind::Ucfirst(Box::new(lhs)),
7111 ExprKind::Fc(_) => ExprKind::Fc(Box::new(lhs)),
7112 ExprKind::Chr(_) => ExprKind::Chr(Box::new(lhs)),
7113 ExprKind::Ord(_) => ExprKind::Ord(Box::new(lhs)),
7114 ExprKind::Chomp(_) => ExprKind::Chomp(Box::new(lhs)),
7115 ExprKind::Chop(_) => ExprKind::Chop(Box::new(lhs)),
7116 ExprKind::Defined(_) => ExprKind::Defined(Box::new(lhs)),
7117 ExprKind::Ref(_) => ExprKind::Ref(Box::new(lhs)),
7118 ExprKind::ScalarContext(_) => ExprKind::ScalarContext(Box::new(lhs)),
7119 ExprKind::Keys(_) => ExprKind::Keys(Box::new(lhs)),
7120 ExprKind::Values(_) => ExprKind::Values(Box::new(lhs)),
7121 ExprKind::Each(_) => ExprKind::Each(Box::new(lhs)),
7122 ExprKind::Pop(_) => ExprKind::Pop(Box::new(lhs)),
7123 ExprKind::Shift(_) => ExprKind::Shift(Box::new(lhs)),
7124 ExprKind::Delete(_) => ExprKind::Delete(Box::new(lhs)),
7125 ExprKind::Exists(_) => ExprKind::Exists(Box::new(lhs)),
7126 ExprKind::ReverseExpr(_) => ExprKind::ReverseExpr(Box::new(lhs)),
7127 ExprKind::Rev(_) => ExprKind::Rev(Box::new(lhs)),
7128 ExprKind::Slurp(_) => ExprKind::Slurp(Box::new(lhs)),
7129 ExprKind::Capture(_) => ExprKind::Capture(Box::new(lhs)),
7130 ExprKind::Qx(_) => ExprKind::Qx(Box::new(lhs)),
7131 ExprKind::FetchUrl(_) => ExprKind::FetchUrl(Box::new(lhs)),
7132 ExprKind::Close(_) => ExprKind::Close(Box::new(lhs)),
7133 ExprKind::Chdir(_) => ExprKind::Chdir(Box::new(lhs)),
7134 ExprKind::Readdir(_) => ExprKind::Readdir(Box::new(lhs)),
7135 ExprKind::Closedir(_) => ExprKind::Closedir(Box::new(lhs)),
7136 ExprKind::Rewinddir(_) => ExprKind::Rewinddir(Box::new(lhs)),
7137 ExprKind::Telldir(_) => ExprKind::Telldir(Box::new(lhs)),
7138 ExprKind::Stat(_) => ExprKind::Stat(Box::new(lhs)),
7139 ExprKind::Lstat(_) => ExprKind::Lstat(Box::new(lhs)),
7140 ExprKind::Readlink(_) => ExprKind::Readlink(Box::new(lhs)),
7141 ExprKind::Study(_) => ExprKind::Study(Box::new(lhs)),
7142 ExprKind::Await(_) => ExprKind::Await(Box::new(lhs)),
7143 ExprKind::Eval(_) => ExprKind::Eval(Box::new(lhs)),
7144 ExprKind::Rand(_) => ExprKind::Rand(Some(Box::new(lhs))),
7145 ExprKind::Srand(_) => ExprKind::Srand(Some(Box::new(lhs))),
7146 ExprKind::Pos(_) => ExprKind::Pos(Some(Box::new(lhs))),
7147 ExprKind::Exit(_) => ExprKind::Exit(Some(Box::new(lhs))),
7148
7149 ExprKind::MapExpr {
7151 block,
7152 list: _,
7153 flatten_array_refs,
7154 stream,
7155 } => ExprKind::MapExpr {
7156 block,
7157 list: Box::new(lhs),
7158 flatten_array_refs,
7159 stream,
7160 },
7161 ExprKind::MapExprComma {
7162 expr,
7163 list: _,
7164 flatten_array_refs,
7165 stream,
7166 } => ExprKind::MapExprComma {
7167 expr,
7168 list: Box::new(lhs),
7169 flatten_array_refs,
7170 stream,
7171 },
7172 ExprKind::GrepExpr {
7173 block,
7174 list: _,
7175 keyword,
7176 } => ExprKind::GrepExpr {
7177 block,
7178 list: Box::new(lhs),
7179 keyword,
7180 },
7181 ExprKind::GrepExprComma {
7182 expr,
7183 list: _,
7184 keyword,
7185 } => ExprKind::GrepExprComma {
7186 expr,
7187 list: Box::new(lhs),
7188 keyword,
7189 },
7190 ExprKind::ForEachExpr { block, list: _ } => ExprKind::ForEachExpr {
7191 block,
7192 list: Box::new(lhs),
7193 },
7194 ExprKind::SortExpr { cmp, list: _ } => ExprKind::SortExpr {
7195 cmp,
7196 list: Box::new(lhs),
7197 },
7198 ExprKind::JoinExpr { separator, list: _ } => ExprKind::JoinExpr {
7199 separator,
7200 list: Box::new(lhs),
7201 },
7202 ExprKind::ReduceExpr { block, list: _ } => ExprKind::ReduceExpr {
7203 block,
7204 list: Box::new(lhs),
7205 },
7206 ExprKind::PMapExpr {
7207 block,
7208 list: _,
7209 progress,
7210 flat_outputs,
7211 on_cluster,
7212 stream,
7213 } => ExprKind::PMapExpr {
7214 block,
7215 list: Box::new(lhs),
7216 progress,
7217 flat_outputs,
7218 on_cluster,
7219 stream,
7220 },
7221 ExprKind::ParExpr { block, list: _ } => ExprKind::ParExpr {
7222 block,
7223 list: Box::new(lhs),
7224 },
7225 ExprKind::ParReduceExpr {
7226 extract_block,
7227 reduce_block,
7228 list: _,
7229 } => ExprKind::ParReduceExpr {
7230 extract_block,
7231 reduce_block,
7232 list: Box::new(lhs),
7233 },
7234 ExprKind::PMapChunkedExpr {
7235 chunk_size,
7236 block,
7237 list: _,
7238 progress,
7239 } => ExprKind::PMapChunkedExpr {
7240 chunk_size,
7241 block,
7242 list: Box::new(lhs),
7243 progress,
7244 },
7245 ExprKind::PGrepExpr {
7246 block,
7247 list: _,
7248 progress,
7249 stream,
7250 } => ExprKind::PGrepExpr {
7251 block,
7252 list: Box::new(lhs),
7253 progress,
7254 stream,
7255 },
7256 ExprKind::PForExpr {
7257 block,
7258 list: _,
7259 progress,
7260 } => ExprKind::PForExpr {
7261 block,
7262 list: Box::new(lhs),
7263 progress,
7264 },
7265 ExprKind::PSortExpr {
7266 cmp,
7267 list: _,
7268 progress,
7269 } => ExprKind::PSortExpr {
7270 cmp,
7271 list: Box::new(lhs),
7272 progress,
7273 },
7274 ExprKind::PReduceExpr {
7275 block,
7276 list: _,
7277 progress,
7278 } => ExprKind::PReduceExpr {
7279 block,
7280 list: Box::new(lhs),
7281 progress,
7282 },
7283 ExprKind::PcacheExpr {
7284 block,
7285 list: _,
7286 progress,
7287 } => ExprKind::PcacheExpr {
7288 block,
7289 list: Box::new(lhs),
7290 progress,
7291 },
7292 ExprKind::PReduceInitExpr {
7293 init,
7294 block,
7295 list: _,
7296 progress,
7297 } => ExprKind::PReduceInitExpr {
7298 init,
7299 block,
7300 list: Box::new(lhs),
7301 progress,
7302 },
7303 ExprKind::PMapReduceExpr {
7304 map_block,
7305 reduce_block,
7306 list: _,
7307 progress,
7308 } => ExprKind::PMapReduceExpr {
7309 map_block,
7310 reduce_block,
7311 list: Box::new(lhs),
7312 progress,
7313 },
7314
7315 ExprKind::Push { array, mut values } => {
7320 values.insert(0, lhs);
7321 ExprKind::Push { array, values }
7322 }
7323 ExprKind::Unshift { array, mut values } => {
7324 values.insert(0, lhs);
7325 ExprKind::Unshift { array, values }
7326 }
7327
7328 ExprKind::SplitExpr {
7330 pattern,
7331 string: _,
7332 limit,
7333 } => ExprKind::SplitExpr {
7334 pattern,
7335 string: Box::new(lhs),
7336 limit,
7337 },
7338
7339 ExprKind::Substitution {
7343 pattern,
7344 replacement,
7345 mut flags,
7346 expr: _,
7347 delim,
7348 } => {
7349 if !flags.contains('r') {
7350 flags.push('r');
7351 }
7352 ExprKind::Substitution {
7353 expr: Box::new(lhs),
7354 pattern,
7355 replacement,
7356 flags,
7357 delim,
7358 }
7359 }
7360 ExprKind::Transliterate {
7361 from,
7362 to,
7363 mut flags,
7364 expr: _,
7365 delim,
7366 } => {
7367 if !flags.contains('r') {
7368 flags.push('r');
7369 }
7370 ExprKind::Transliterate {
7371 expr: Box::new(lhs),
7372 from,
7373 to,
7374 flags,
7375 delim,
7376 }
7377 }
7378 ExprKind::Match {
7379 pattern,
7380 flags,
7381 scalar_g,
7382 expr: _,
7383 delim,
7384 } => ExprKind::Match {
7385 expr: Box::new(lhs),
7386 pattern,
7387 flags,
7388 scalar_g,
7389 delim,
7390 },
7391 ExprKind::Regex(pattern, flags) => ExprKind::Match {
7393 expr: Box::new(lhs),
7394 pattern,
7395 flags,
7396 scalar_g: false,
7397 delim: '/',
7398 },
7399
7400 ExprKind::Bareword(name) => match name.as_str() {
7402 "reverse" => {
7403 if crate::no_interop_mode() {
7404 return Err(self.syntax_err(
7405 "stryke uses `rev` instead of `reverse` (--no-interop)",
7406 line,
7407 ));
7408 }
7409 ExprKind::ReverseExpr(Box::new(lhs))
7410 }
7411 "rv" | "reversed" | "rev" => ExprKind::Rev(Box::new(lhs)),
7412 "uq" | "uniq" | "distinct" => ExprKind::FuncCall {
7413 name: "uniq".to_string(),
7414 args: vec![lhs],
7415 },
7416 "fl" | "flatten" => ExprKind::FuncCall {
7417 name: "flatten".to_string(),
7418 args: vec![lhs],
7419 },
7420 _ => ExprKind::FuncCall {
7421 name,
7422 args: vec![lhs],
7423 },
7424 },
7425
7426 kind @ (ExprKind::ScalarVar(_)
7428 | ExprKind::ArrayElement { .. }
7429 | ExprKind::HashElement { .. }
7430 | ExprKind::Deref { .. }
7431 | ExprKind::ArrowDeref { .. }
7432 | ExprKind::CodeRef { .. }
7433 | ExprKind::SubroutineRef(_)
7434 | ExprKind::SubroutineCodeRef(_)
7435 | ExprKind::DynamicSubCodeRef(_)) => ExprKind::IndirectCall {
7436 target: Box::new(Expr { kind, line: rline }),
7437 args: vec![lhs],
7438 ampersand: false,
7439 pass_caller_arglist: false,
7440 },
7441
7442 ExprKind::Do(inner) if matches!(inner.kind, ExprKind::CodeRef { .. }) => {
7446 ExprKind::IndirectCall {
7447 target: inner,
7448 args: vec![lhs],
7449 ampersand: false,
7450 pass_caller_arglist: false,
7451 }
7452 }
7453
7454 other => {
7455 return Err(self.syntax_err(
7456 format!(
7457 "right-hand side of `|>` must be a call, builtin, or coderef \
7458 expression (got {})",
7459 Self::expr_kind_name(&other)
7460 ),
7461 line,
7462 ));
7463 }
7464 };
7465 Ok(Expr {
7466 kind: new_kind,
7467 line,
7468 })
7469 }
7470
7471 fn expr_kind_name(kind: &ExprKind) -> &'static str {
7473 match kind {
7474 ExprKind::Integer(_) | ExprKind::Float(_) => "numeric literal",
7475 ExprKind::String(_) | ExprKind::InterpolatedString(_) => "string literal",
7476 ExprKind::BinOp { .. } => "binary expression",
7477 ExprKind::UnaryOp { .. } => "unary expression",
7478 ExprKind::Ternary { .. } => "ternary expression",
7479 ExprKind::Assign { .. } | ExprKind::CompoundAssign { .. } => "assignment",
7480 ExprKind::List(_) => "list expression",
7481 ExprKind::Range { .. } => "range expression",
7482 _ => "expression",
7483 }
7484 }
7485
7486 fn parse_or_word(&mut self) -> StrykeResult<Expr> {
7488 let mut left = self.parse_and_word()?;
7489 while matches!(self.peek(), Token::LogOrWord) {
7490 let line = left.line;
7491 self.advance();
7492 let right = self.parse_and_word()?;
7493 left = Expr {
7494 kind: ExprKind::BinOp {
7495 left: Box::new(left),
7496 op: BinOp::LogOrWord,
7497 right: Box::new(right),
7498 },
7499 line,
7500 };
7501 }
7502 Ok(left)
7503 }
7504
7505 fn parse_and_word(&mut self) -> StrykeResult<Expr> {
7506 let mut left = self.parse_not_word()?;
7507 while matches!(self.peek(), Token::LogAndWord) {
7508 let line = left.line;
7509 self.advance();
7510 let right = self.parse_not_word()?;
7511 left = Expr {
7512 kind: ExprKind::BinOp {
7513 left: Box::new(left),
7514 op: BinOp::LogAndWord,
7515 right: Box::new(right),
7516 },
7517 line,
7518 };
7519 }
7520 Ok(left)
7521 }
7522
7523 fn parse_not_word(&mut self) -> StrykeResult<Expr> {
7524 if matches!(self.peek(), Token::LogNotWord) {
7525 let line = self.peek_line();
7526 self.advance();
7527 let expr = self.parse_not_word()?;
7528 return Ok(Expr {
7529 kind: ExprKind::UnaryOp {
7530 op: UnaryOp::LogNotWord,
7531 expr: Box::new(expr),
7532 },
7533 line,
7534 });
7535 }
7536 self.parse_assign_expr()
7539 }
7540
7541 fn parse_log_or(&mut self) -> StrykeResult<Expr> {
7542 let mut left = self.parse_log_and()?;
7543 loop {
7544 let op = match self.peek() {
7545 Token::LogOr => BinOp::LogOr,
7546 Token::DefinedOr => BinOp::DefinedOr,
7547 _ => break,
7548 };
7549 let line = left.line;
7550 self.advance();
7551 let right = self.parse_log_and()?;
7552 left = Expr {
7553 kind: ExprKind::BinOp {
7554 left: Box::new(left),
7555 op,
7556 right: Box::new(right),
7557 },
7558 line,
7559 };
7560 }
7561 Ok(left)
7562 }
7563
7564 fn parse_log_and(&mut self) -> StrykeResult<Expr> {
7565 let mut left = self.parse_bit_or()?;
7566 while matches!(self.peek(), Token::LogAnd) {
7567 let line = left.line;
7568 self.advance();
7569 let right = self.parse_bit_or()?;
7570 left = Expr {
7571 kind: ExprKind::BinOp {
7572 left: Box::new(left),
7573 op: BinOp::LogAnd,
7574 right: Box::new(right),
7575 },
7576 line,
7577 };
7578 }
7579 Ok(left)
7580 }
7581
7582 fn parse_bit_or(&mut self) -> StrykeResult<Expr> {
7583 let mut left = self.parse_bit_xor()?;
7584 while matches!(self.peek(), Token::BitOr) {
7585 let line = left.line;
7586 self.advance();
7587 let right = self.parse_bit_xor()?;
7588 left = Expr {
7589 kind: ExprKind::BinOp {
7590 left: Box::new(left),
7591 op: BinOp::BitOr,
7592 right: Box::new(right),
7593 },
7594 line,
7595 };
7596 }
7597 Ok(left)
7598 }
7599
7600 fn parse_bit_xor(&mut self) -> StrykeResult<Expr> {
7601 let mut left = self.parse_bit_and()?;
7602 while matches!(self.peek(), Token::BitXor) {
7603 let line = left.line;
7604 self.advance();
7605 let right = self.parse_bit_and()?;
7606 left = Expr {
7607 kind: ExprKind::BinOp {
7608 left: Box::new(left),
7609 op: BinOp::BitXor,
7610 right: Box::new(right),
7611 },
7612 line,
7613 };
7614 }
7615 Ok(left)
7616 }
7617
7618 fn parse_bit_and(&mut self) -> StrykeResult<Expr> {
7619 let mut left = self.parse_equality()?;
7620 while matches!(self.peek(), Token::BitAnd) {
7621 let line = left.line;
7622 self.advance();
7623 let right = self.parse_equality()?;
7624 left = Expr {
7625 kind: ExprKind::BinOp {
7626 left: Box::new(left),
7627 op: BinOp::BitAnd,
7628 right: Box::new(right),
7629 },
7630 line,
7631 };
7632 }
7633 Ok(left)
7634 }
7635
7636 fn parse_equality(&mut self) -> StrykeResult<Expr> {
7637 let mut left = self.parse_comparison()?;
7638 loop {
7639 let op = match self.peek() {
7640 Token::NumEq => BinOp::NumEq,
7641 Token::NumNe => BinOp::NumNe,
7642 Token::StrEq => BinOp::StrEq,
7643 Token::StrNe => BinOp::StrNe,
7644 Token::Spaceship => BinOp::Spaceship,
7645 Token::StrCmp => BinOp::StrCmp,
7646 _ => break,
7647 };
7648 let line = left.line;
7649 self.advance();
7650 let right = self.parse_comparison()?;
7651 left = Expr {
7652 kind: ExprKind::BinOp {
7653 left: Box::new(left),
7654 op,
7655 right: Box::new(right),
7656 },
7657 line,
7658 };
7659 }
7660 Ok(left)
7661 }
7662
7663 fn parse_comparison(&mut self) -> StrykeResult<Expr> {
7664 let left = self.parse_shift()?;
7665 let first_op = match self.peek() {
7666 Token::NumLt => BinOp::NumLt,
7667 Token::NumGt => BinOp::NumGt,
7668 Token::NumLe => BinOp::NumLe,
7669 Token::NumGe => BinOp::NumGe,
7670 Token::StrLt => BinOp::StrLt,
7671 Token::StrGt => BinOp::StrGt,
7672 Token::StrLe => BinOp::StrLe,
7673 Token::StrGe => BinOp::StrGe,
7674 _ => return Ok(left),
7675 };
7676 let line = left.line;
7677 self.advance();
7678 let middle = self.parse_shift()?;
7679
7680 let second_op = match self.peek() {
7681 Token::NumLt => Some(BinOp::NumLt),
7682 Token::NumGt => Some(BinOp::NumGt),
7683 Token::NumLe => Some(BinOp::NumLe),
7684 Token::NumGe => Some(BinOp::NumGe),
7685 Token::StrLt => Some(BinOp::StrLt),
7686 Token::StrGt => Some(BinOp::StrGt),
7687 Token::StrLe => Some(BinOp::StrLe),
7688 Token::StrGe => Some(BinOp::StrGe),
7689 _ => None,
7690 };
7691
7692 if second_op.is_none() {
7693 return Ok(Expr {
7694 kind: ExprKind::BinOp {
7695 left: Box::new(left),
7696 op: first_op,
7697 right: Box::new(middle),
7698 },
7699 line,
7700 });
7701 }
7702
7703 let mut operands = vec![left, middle];
7706 let mut ops = vec![first_op];
7707
7708 loop {
7709 let op = match self.peek() {
7710 Token::NumLt => BinOp::NumLt,
7711 Token::NumGt => BinOp::NumGt,
7712 Token::NumLe => BinOp::NumLe,
7713 Token::NumGe => BinOp::NumGe,
7714 Token::StrLt => BinOp::StrLt,
7715 Token::StrGt => BinOp::StrGt,
7716 Token::StrLe => BinOp::StrLe,
7717 Token::StrGe => BinOp::StrGe,
7718 _ => break,
7719 };
7720 self.advance();
7721 ops.push(op);
7722 operands.push(self.parse_shift()?);
7723 }
7724
7725 let mut result = Expr {
7727 kind: ExprKind::BinOp {
7728 left: Box::new(operands[0].clone()),
7729 op: ops[0],
7730 right: Box::new(operands[1].clone()),
7731 },
7732 line,
7733 };
7734
7735 for i in 1..ops.len() {
7736 let cmp = Expr {
7737 kind: ExprKind::BinOp {
7738 left: Box::new(operands[i].clone()),
7739 op: ops[i],
7740 right: Box::new(operands[i + 1].clone()),
7741 },
7742 line,
7743 };
7744 result = Expr {
7745 kind: ExprKind::BinOp {
7746 left: Box::new(result),
7747 op: BinOp::LogAnd,
7748 right: Box::new(cmp),
7749 },
7750 line,
7751 };
7752 }
7753
7754 Ok(result)
7755 }
7756
7757 fn parse_shift(&mut self) -> StrykeResult<Expr> {
7758 let mut left = self.parse_addition()?;
7759 loop {
7760 let op = match self.peek() {
7761 Token::ShiftLeft => BinOp::ShiftLeft,
7762 Token::ShiftRight => BinOp::ShiftRight,
7763 _ => break,
7764 };
7765 let line = left.line;
7766 self.advance();
7767 let right = self.parse_addition()?;
7768 left = Expr {
7769 kind: ExprKind::BinOp {
7770 left: Box::new(left),
7771 op,
7772 right: Box::new(right),
7773 },
7774 line,
7775 };
7776 }
7777 Ok(left)
7778 }
7779
7780 fn parse_addition(&mut self) -> StrykeResult<Expr> {
7781 let mut left = self.parse_multiplication()?;
7782 loop {
7783 let op = match self.peek() {
7786 Token::Plus if self.peek_line() == self.prev_line() => BinOp::Add,
7787 Token::Minus if self.peek_line() == self.prev_line() => BinOp::Sub,
7788 Token::Dot => BinOp::Concat,
7789 _ => break,
7790 };
7791 let line = left.line;
7792 self.advance();
7793 let right = self.parse_multiplication()?;
7794 left = Expr {
7795 kind: ExprKind::BinOp {
7796 left: Box::new(left),
7797 op,
7798 right: Box::new(right),
7799 },
7800 line,
7801 };
7802 }
7803 Ok(left)
7804 }
7805
7806 fn parse_multiplication(&mut self) -> StrykeResult<Expr> {
7807 let mut left = self.parse_regex_bind()?;
7808 loop {
7809 let op = match self.peek() {
7810 Token::Star => BinOp::Mul,
7811 Token::Slash if self.suppress_slash_as_div == 0 => BinOp::Div,
7812 Token::Percent if self.peek_line() == self.prev_line() => BinOp::Mod,
7815 Token::X => {
7816 let line = left.line;
7817 let list_repeat = self.list_construct_close_pos == Some(self.pos);
7824 self.advance();
7825 let right = self.parse_regex_bind()?;
7826 left = Expr {
7827 kind: ExprKind::Repeat {
7828 expr: Box::new(left),
7829 count: Box::new(right),
7830 list_repeat,
7831 },
7832 line,
7833 };
7834 continue;
7835 }
7836 _ => break,
7837 };
7838 let line = left.line;
7839 self.advance();
7840 let right = self.parse_regex_bind()?;
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_regex_bind(&mut self) -> StrykeResult<Expr> {
7854 let left = self.parse_unary()?;
7855 match self.peek() {
7856 Token::BindMatch => {
7857 let line = left.line;
7858 self.advance();
7859 match self.peek().clone() {
7860 Token::Regex(pattern, flags, delim) => {
7861 self.advance();
7862 Ok(Expr {
7863 kind: ExprKind::Match {
7864 expr: Box::new(left),
7865 pattern,
7866 flags,
7867 scalar_g: false,
7868 delim,
7869 },
7870 line,
7871 })
7872 }
7873 Token::Ident(ref s) if s.starts_with('\x00') => {
7874 let (Token::Ident(encoded), _) = self.advance() else {
7875 unreachable!()
7876 };
7877 let parts: Vec<&str> = encoded.split('\x00').collect();
7878 if parts.len() >= 4 && parts[1] == "s" {
7879 let delim = parts.get(5).and_then(|s| s.chars().next()).unwrap_or('/');
7880 Ok(Expr {
7881 kind: ExprKind::Substitution {
7882 expr: Box::new(left),
7883 pattern: parts[2].to_string(),
7884 replacement: parts[3].to_string(),
7885 flags: parts.get(4).unwrap_or(&"").to_string(),
7886 delim,
7887 },
7888 line,
7889 })
7890 } else if parts.len() >= 4 && parts[1] == "tr" {
7891 let delim = parts.get(5).and_then(|s| s.chars().next()).unwrap_or('/');
7892 Ok(Expr {
7893 kind: ExprKind::Transliterate {
7894 expr: Box::new(left),
7895 from: parts[2].to_string(),
7896 to: parts[3].to_string(),
7897 flags: parts.get(4).unwrap_or(&"").to_string(),
7898 delim,
7899 },
7900 line,
7901 })
7902 } else {
7903 Err(self.syntax_err("Invalid regex binding", line))
7904 }
7905 }
7906 _ => {
7907 let rhs = self.parse_unary()?;
7908 Ok(Expr {
7909 kind: ExprKind::BinOp {
7910 left: Box::new(left),
7911 op: BinOp::BindMatch,
7912 right: Box::new(rhs),
7913 },
7914 line,
7915 })
7916 }
7917 }
7918 }
7919 Token::BindNotMatch => {
7920 let line = left.line;
7921 self.advance();
7922 match self.peek().clone() {
7923 Token::Regex(pattern, flags, delim) => {
7924 self.advance();
7925 Ok(Expr {
7926 kind: ExprKind::UnaryOp {
7927 op: UnaryOp::LogNot,
7928 expr: Box::new(Expr {
7929 kind: ExprKind::Match {
7930 expr: Box::new(left),
7931 pattern,
7932 flags,
7933 scalar_g: false,
7934 delim,
7935 },
7936 line,
7937 }),
7938 },
7939 line,
7940 })
7941 }
7942 Token::Ident(ref s) if s.starts_with('\x00') => {
7943 let (Token::Ident(encoded), _) = self.advance() else {
7944 unreachable!()
7945 };
7946 let parts: Vec<&str> = encoded.split('\x00').collect();
7947 if parts.len() >= 4 && parts[1] == "s" {
7948 let delim = parts.get(5).and_then(|s| s.chars().next()).unwrap_or('/');
7949 Ok(Expr {
7950 kind: ExprKind::UnaryOp {
7951 op: UnaryOp::LogNot,
7952 expr: Box::new(Expr {
7953 kind: ExprKind::Substitution {
7954 expr: Box::new(left),
7955 pattern: parts[2].to_string(),
7956 replacement: parts[3].to_string(),
7957 flags: parts.get(4).unwrap_or(&"").to_string(),
7958 delim,
7959 },
7960 line,
7961 }),
7962 },
7963 line,
7964 })
7965 } else if parts.len() >= 4 && parts[1] == "tr" {
7966 let delim = parts.get(5).and_then(|s| s.chars().next()).unwrap_or('/');
7967 Ok(Expr {
7968 kind: ExprKind::UnaryOp {
7969 op: UnaryOp::LogNot,
7970 expr: Box::new(Expr {
7971 kind: ExprKind::Transliterate {
7972 expr: Box::new(left),
7973 from: parts[2].to_string(),
7974 to: parts[3].to_string(),
7975 flags: parts.get(4).unwrap_or(&"").to_string(),
7976 delim,
7977 },
7978 line,
7979 }),
7980 },
7981 line,
7982 })
7983 } else {
7984 Err(self.syntax_err("Invalid regex binding after !~", line))
7985 }
7986 }
7987 _ => {
7988 let rhs = self.parse_unary()?;
7989 Ok(Expr {
7990 kind: ExprKind::BinOp {
7991 left: Box::new(left),
7992 op: BinOp::BindNotMatch,
7993 right: Box::new(rhs),
7994 },
7995 line,
7996 })
7997 }
7998 }
7999 }
8000 _ => Ok(left),
8001 }
8002 }
8003
8004 fn parse_thread_input(&mut self) -> StrykeResult<Expr> {
8007 self.suppress_slash_as_div = self.suppress_slash_as_div.saturating_add(1);
8008 let result = self.parse_range();
8009 self.suppress_slash_as_div = self.suppress_slash_as_div.saturating_sub(1);
8010 result
8011 }
8012
8013 fn parse_thread_macro_chunk_par(
8018 &mut self,
8019 line: usize,
8020 thread_last: bool,
8021 ) -> StrykeResult<Expr> {
8022 self.suppress_parenless_call = self.suppress_parenless_call.saturating_add(1);
8024 let source_expr = self.parse_thread_input();
8025 self.suppress_parenless_call = self.suppress_parenless_call.saturating_sub(1);
8026 let source_expr = source_expr?;
8027
8028 self.pending_thread_input = Some(Expr {
8032 kind: ExprKind::ArrayVar("_".into()),
8033 line,
8034 });
8035 let chunk_chain = self.parse_thread_macro_inner(line, thread_last, None);
8036 self.pending_thread_input = None;
8037 let chunk_chain = chunk_chain?;
8038
8039 let extract_block: Block = match chunk_chain.kind {
8044 ExprKind::CodeRef { params: _, body } => body,
8045 _ => vec![Statement {
8046 label: None,
8047 kind: StmtKind::Expression(chunk_chain),
8048 line,
8049 }],
8050 };
8051
8052 let par_reduce = Expr {
8053 kind: ExprKind::ParReduceExpr {
8054 extract_block,
8055 reduce_block: None,
8056 list: Box::new(source_expr),
8057 },
8058 line,
8059 };
8060
8061 if self.eat_chunk_par_split_boundary() {
8065 return self.parse_thread_macro_continuation(par_reduce, line, thread_last);
8066 }
8067 Ok(par_reduce)
8068 }
8069
8070 fn parse_thread_macro_dist(&mut self, line: usize, thread_last: bool) -> StrykeResult<Expr> {
8077 let on_ok = matches!(self.peek(), Token::Ident(ref s) if s == "on");
8079 if !on_ok {
8080 return Err(
8081 self.syntax_err("~d>: expected `on <cluster-expr>` after the operator", line)
8082 );
8083 }
8084 self.advance(); self.suppress_parenless_call = self.suppress_parenless_call.saturating_add(1);
8088 self.suppress_indirect_paren_call = self.suppress_indirect_paren_call.saturating_add(1);
8092 let cluster_expr = self.parse_thread_input();
8093 self.suppress_indirect_paren_call = self.suppress_indirect_paren_call.saturating_sub(1);
8094 self.suppress_parenless_call = self.suppress_parenless_call.saturating_sub(1);
8095 let cluster_expr = cluster_expr?;
8096
8097 self.suppress_parenless_call = self.suppress_parenless_call.saturating_add(1);
8099 let source_expr = self.parse_thread_input();
8100 self.suppress_parenless_call = self.suppress_parenless_call.saturating_sub(1);
8101 let source_expr = source_expr?;
8102
8103 self.pending_thread_input = Some(Expr {
8108 kind: ExprKind::ArrayVar("_".into()),
8109 line,
8110 });
8111 let chunk_chain = self.parse_thread_macro_inner(line, thread_last, None);
8112 self.pending_thread_input = None;
8113 let chunk_chain = chunk_chain?;
8114
8115 let extract_block: Block = match chunk_chain.kind {
8116 ExprKind::CodeRef { params: _, body } => body,
8117 _ => vec![Statement {
8118 label: None,
8119 kind: StmtKind::Expression(chunk_chain),
8120 line,
8121 }],
8122 };
8123
8124 let dist_reduce = Expr {
8125 kind: ExprKind::DistReduceExpr {
8126 cluster: Box::new(cluster_expr),
8127 extract_block,
8128 list: Box::new(source_expr),
8129 },
8130 line,
8131 };
8132
8133 if self.eat_chunk_par_split_boundary() {
8135 return self.parse_thread_macro_continuation(dist_reduce, line, thread_last);
8136 }
8137 Ok(dist_reduce)
8138 }
8139
8140 fn parse_thread_macro_continuation(
8146 &mut self,
8147 prior: Expr,
8148 line: usize,
8149 thread_last: bool,
8150 ) -> StrykeResult<Expr> {
8151 self.pending_thread_input = Some(prior);
8152 let res = self.parse_thread_macro_inner(line, thread_last, None);
8153 self.pending_thread_input = None;
8154 res
8155 }
8156
8157 fn eat_chunk_par_split_boundary(&mut self) -> bool {
8161 if matches!(self.peek(), Token::LogOr) && matches!(self.peek_at(1), Token::NumGt) {
8163 self.advance(); self.advance(); return true;
8166 }
8167 if matches!(self.peek(), Token::BitOr) {
8169 if let Token::Ident(name) = self.peek_at(1).clone() {
8170 if name == "then" && matches!(self.peek_at(2), Token::BitOr) {
8171 self.advance(); self.advance(); self.advance(); return true;
8175 }
8176 }
8177 }
8178 false
8179 }
8180
8181 fn parse_range(&mut self) -> StrykeResult<Expr> {
8188 let left = self.parse_log_or()?;
8189 let line = left.line;
8190 let (exclusive, _colon_style) = if self.eat(&Token::RangeExclusive) {
8197 (true, false)
8198 } else if self.eat(&Token::Range) {
8199 (false, false)
8200 } else if self.suppress_colon_range == 0 && self.eat(&Token::Colon) {
8201 (false, true)
8204 } else if self.suppress_tilde_range == 0 && self.eat(&Token::BitNot) {
8205 (false, true)
8206 } else {
8207 return Ok(left);
8208 };
8209 let right = self.parse_log_or()?;
8210 let step = if self.eat(&Token::Colon)
8214 || (self.suppress_tilde_range == 0 && self.eat(&Token::BitNot))
8215 {
8216 Some(Box::new(self.parse_unary()?))
8217 } else {
8218 None
8219 };
8220 Ok(Expr {
8221 kind: ExprKind::Range {
8222 from: Box::new(left),
8223 to: Box::new(right),
8224 exclusive,
8225 step,
8226 },
8227 line,
8228 })
8229 }
8230
8231 fn parse_package_qualified_identifier(&mut self) -> StrykeResult<String> {
8233 let mut name = match self.advance() {
8234 (Token::Ident(n), _) => n,
8235 (tok, l) => {
8236 return Err(self.syntax_err(format!("Expected identifier, got {:?}", tok), l));
8237 }
8238 };
8239 while self.eat(&Token::PackageSep) {
8240 match self.advance() {
8241 (Token::Ident(part), _) => {
8242 name.push_str("::");
8243 name.push_str(&part);
8244 }
8245 (Token::ScalarVar(part), _) if Self::is_underscore_topic_slot(&part) => {
8253 name.push_str("::");
8254 name.push_str(&part);
8255 }
8256 (tok, l) => {
8257 return Err(self
8258 .syntax_err(format!("Expected identifier after `::`, got {:?}", tok), l));
8259 }
8260 }
8261 }
8262 Ok(name)
8263 }
8264
8265 fn parse_qualified_subroutine_name(&mut self) -> StrykeResult<String> {
8267 self.parse_package_qualified_identifier()
8268 }
8269
8270 fn parse_unary(&mut self) -> StrykeResult<Expr> {
8271 let line = self.peek_line();
8272 match self.peek().clone() {
8273 Token::Minus => {
8274 self.advance();
8275 let expr = self.parse_power()?;
8276 Ok(Expr {
8277 kind: ExprKind::UnaryOp {
8278 op: UnaryOp::Negate,
8279 expr: Box::new(expr),
8280 },
8281 line,
8282 })
8283 }
8284 Token::Plus => {
8291 self.advance();
8292 if matches!(self.peek(), Token::LBrace) {
8293 let line = self.peek_line();
8294 self.advance(); return self.parse_forced_hashref_body(line);
8296 }
8297 self.parse_unary()
8298 }
8299 Token::LogNot => {
8300 self.advance();
8301 let expr = self.parse_unary()?;
8302 Ok(Expr {
8303 kind: ExprKind::UnaryOp {
8304 op: UnaryOp::LogNot,
8305 expr: Box::new(expr),
8306 },
8307 line,
8308 })
8309 }
8310 Token::BitNot => {
8311 self.advance();
8312 let expr = self.parse_unary()?;
8313 Ok(Expr {
8314 kind: ExprKind::UnaryOp {
8315 op: UnaryOp::BitNot,
8316 expr: Box::new(expr),
8317 },
8318 line,
8319 })
8320 }
8321 Token::Increment => {
8322 self.advance();
8323 let expr = self.parse_postfix()?;
8324 Ok(Expr {
8325 kind: ExprKind::UnaryOp {
8326 op: UnaryOp::PreIncrement,
8327 expr: Box::new(expr),
8328 },
8329 line,
8330 })
8331 }
8332 Token::Decrement => {
8333 self.advance();
8334 let expr = self.parse_postfix()?;
8335 Ok(Expr {
8336 kind: ExprKind::UnaryOp {
8337 op: UnaryOp::PreDecrement,
8338 expr: Box::new(expr),
8339 },
8340 line,
8341 })
8342 }
8343 Token::BitAnd => {
8344 self.advance();
8347 if matches!(self.peek(), Token::LBrace) {
8348 self.advance();
8349 let inner = self.parse_expression()?;
8350 self.expect(&Token::RBrace)?;
8351 return Ok(Expr {
8352 kind: ExprKind::DynamicSubCodeRef(Box::new(inner)),
8353 line,
8354 });
8355 }
8356 if matches!(self.peek(), Token::Ident(_)) {
8357 let name = self.parse_qualified_subroutine_name()?;
8358 return Ok(Expr {
8359 kind: ExprKind::SubroutineRef(name),
8360 line,
8361 });
8362 }
8363 let target = self.parse_primary()?;
8364 if matches!(self.peek(), Token::LParen) {
8365 self.advance();
8366 let args = self.parse_arg_list()?;
8367 self.expect(&Token::RParen)?;
8368 return Ok(Expr {
8369 kind: ExprKind::IndirectCall {
8370 target: Box::new(target),
8371 args,
8372 ampersand: true,
8373 pass_caller_arglist: false,
8374 },
8375 line,
8376 });
8377 }
8378 Ok(Expr {
8380 kind: ExprKind::IndirectCall {
8381 target: Box::new(target),
8382 args: vec![],
8383 ampersand: true,
8384 pass_caller_arglist: true,
8385 },
8386 line,
8387 })
8388 }
8389 Token::Backslash => {
8390 self.advance();
8391 let expr = self.parse_unary()?;
8392 if let ExprKind::SubroutineRef(name) = expr.kind {
8393 return Ok(Expr {
8394 kind: ExprKind::SubroutineCodeRef(name),
8395 line,
8396 });
8397 }
8398 if matches!(expr.kind, ExprKind::DynamicSubCodeRef(_)) {
8399 return Ok(expr);
8400 }
8401 Ok(Expr {
8403 kind: ExprKind::ScalarRef(Box::new(expr)),
8404 line,
8405 })
8406 }
8407 Token::FileTest(op) => {
8408 self.advance();
8409 let expr = if Self::filetest_allows_implicit_topic(self.peek()) {
8411 Expr {
8412 kind: ExprKind::ScalarVar("_".into()),
8413 line: self.peek_line(),
8414 }
8415 } else {
8416 self.parse_unary()?
8417 };
8418 Ok(Expr {
8419 kind: ExprKind::FileTest {
8420 op,
8421 expr: Box::new(expr),
8422 },
8423 line,
8424 })
8425 }
8426 _ => self.parse_power(),
8427 }
8428 }
8429
8430 fn parse_power(&mut self) -> StrykeResult<Expr> {
8431 let left = self.parse_postfix()?;
8432 if matches!(self.peek(), Token::Power) {
8433 let line = left.line;
8434 self.advance();
8435 let right = self.parse_unary()?; return Ok(Expr {
8437 kind: ExprKind::BinOp {
8438 left: Box::new(left),
8439 op: BinOp::Pow,
8440 right: Box::new(right),
8441 },
8442 line,
8443 });
8444 }
8445 Ok(left)
8446 }
8447
8448 fn parse_postfix(&mut self) -> StrykeResult<Expr> {
8449 let mut expr = self.parse_primary()?;
8450 loop {
8451 match self.peek().clone() {
8452 Token::Increment => {
8453 if self.peek_line() > self.prev_line() {
8456 break;
8457 }
8458 let line = expr.line;
8459 self.advance();
8460 expr = Expr {
8461 kind: ExprKind::PostfixOp {
8462 expr: Box::new(expr),
8463 op: PostfixOp::Increment,
8464 },
8465 line,
8466 };
8467 }
8468 Token::Decrement => {
8469 if self.peek_line() > self.prev_line() {
8472 break;
8473 }
8474 let line = expr.line;
8475 self.advance();
8476 expr = Expr {
8477 kind: ExprKind::PostfixOp {
8478 expr: Box::new(expr),
8479 op: PostfixOp::Decrement,
8480 },
8481 line,
8482 };
8483 }
8484 Token::LParen => {
8485 if self.suppress_indirect_paren_call > 0 {
8486 break;
8487 }
8488 if self.peek_line() > self.prev_line() {
8492 break;
8493 }
8494 let line = expr.line;
8495 self.advance();
8496 let args = self.parse_arg_list()?;
8497 self.expect(&Token::RParen)?;
8498 expr = Expr {
8499 kind: ExprKind::IndirectCall {
8500 target: Box::new(expr),
8501 args,
8502 ampersand: false,
8503 pass_caller_arglist: false,
8504 },
8505 line,
8506 };
8507 }
8508 Token::Arrow => {
8509 let line = expr.line;
8510 self.advance();
8511 match self.peek().clone() {
8512 Token::LBracket => {
8513 self.advance();
8514 let index = self.parse_expression()?;
8515 self.expect(&Token::RBracket)?;
8516 expr = Expr {
8517 kind: ExprKind::ArrowDeref {
8518 expr: Box::new(expr),
8519 index: Box::new(index),
8520 kind: DerefKind::Array,
8521 },
8522 line,
8523 };
8524 }
8525 Token::LBrace => {
8526 self.advance();
8527 let key = self.parse_hash_subscript_key()?;
8528 self.expect(&Token::RBrace)?;
8529 expr = Expr {
8530 kind: ExprKind::ArrowDeref {
8531 expr: Box::new(expr),
8532 index: Box::new(key),
8533 kind: DerefKind::Hash,
8534 },
8535 line,
8536 };
8537 }
8538 Token::LParen => {
8539 self.advance();
8540 let args = self.parse_arg_list()?;
8541 self.expect(&Token::RParen)?;
8542 expr = Expr {
8543 kind: ExprKind::ArrowDeref {
8544 expr: Box::new(expr),
8545 index: Box::new(Expr {
8546 kind: ExprKind::List(args),
8547 line,
8548 }),
8549 kind: DerefKind::Call,
8550 },
8551 line,
8552 };
8553 }
8554 Token::Ident(method) => {
8555 self.advance();
8556 if method == "SUPER" {
8557 self.expect(&Token::PackageSep)?;
8558 let real_method = match self.advance() {
8559 (Token::Ident(n), _) => n,
8560 (tok, l) => {
8561 return Err(self.syntax_err(
8562 format!(
8563 "Expected method name after SUPER::, got {:?}",
8564 tok
8565 ),
8566 l,
8567 ));
8568 }
8569 };
8570 let args = if self.eat(&Token::LParen) {
8571 let a = self.parse_arg_list()?;
8572 self.expect(&Token::RParen)?;
8573 a
8574 } else {
8575 self.parse_method_arg_list_no_paren()?
8576 };
8577 expr = Expr {
8578 kind: ExprKind::MethodCall {
8579 object: Box::new(expr),
8580 method: real_method,
8581 args,
8582 super_call: true,
8583 },
8584 line,
8585 };
8586 } else {
8587 let mut method_name = method;
8588 while self.eat(&Token::PackageSep) {
8589 match self.advance() {
8590 (Token::Ident(part), _) => {
8591 method_name.push_str("::");
8592 method_name.push_str(&part);
8593 }
8594 (tok, l) => {
8595 return Err(self.syntax_err(
8596 format!(
8597 "Expected identifier after :: in method name, got {:?}",
8598 tok
8599 ),
8600 l,
8601 ));
8602 }
8603 }
8604 }
8605 let args = if self.eat(&Token::LParen) {
8606 let a = self.parse_arg_list()?;
8607 self.expect(&Token::RParen)?;
8608 a
8609 } else {
8610 self.parse_method_arg_list_no_paren()?
8611 };
8612 expr = Expr {
8613 kind: ExprKind::MethodCall {
8614 object: Box::new(expr),
8615 method: method_name,
8616 args,
8617 super_call: false,
8618 },
8619 line,
8620 };
8621 }
8622 }
8623 Token::ArrayAt => {
8629 self.advance(); match self.peek().clone() {
8631 Token::Star => {
8632 self.advance();
8633 expr = Expr {
8634 kind: ExprKind::Deref {
8635 expr: Box::new(expr),
8636 kind: Sigil::Array,
8637 },
8638 line,
8639 };
8640 }
8641 Token::LBracket => {
8642 self.advance();
8643 let indices = self.parse_slice_arg_list(false)?;
8644 self.expect(&Token::RBracket)?;
8645 let source = Expr {
8646 kind: ExprKind::Deref {
8647 expr: Box::new(expr),
8648 kind: Sigil::Array,
8649 },
8650 line,
8651 };
8652 expr = Expr {
8653 kind: ExprKind::AnonymousListSlice {
8654 source: Box::new(source),
8655 indices,
8656 },
8657 line,
8658 };
8659 }
8660 Token::LBrace => {
8661 self.advance();
8662 let keys = self.parse_slice_arg_list(true)?;
8663 self.expect(&Token::RBrace)?;
8664 expr = Expr {
8665 kind: ExprKind::HashSliceDeref {
8666 container: Box::new(expr),
8667 keys,
8668 },
8669 line,
8670 };
8671 }
8672 tok => {
8673 return Err(self.syntax_err(
8674 format!(
8675 "Expected `*`, `[…]`, or `{{…}}` after `->@`, got {:?}",
8676 tok
8677 ),
8678 line,
8679 ));
8680 }
8681 }
8682 }
8683 Token::HashPercent => {
8684 self.advance(); match self.peek().clone() {
8686 Token::Star => {
8687 self.advance();
8688 expr = Expr {
8689 kind: ExprKind::Deref {
8690 expr: Box::new(expr),
8691 kind: Sigil::Hash,
8692 },
8693 line,
8694 };
8695 }
8696 tok => {
8697 return Err(self.syntax_err(
8698 format!("Expected `*` after `->%`, got {:?}", tok),
8699 line,
8700 ));
8701 }
8702 }
8703 }
8704 Token::X => {
8706 self.advance();
8707 let args = if self.eat(&Token::LParen) {
8708 let a = self.parse_arg_list()?;
8709 self.expect(&Token::RParen)?;
8710 a
8711 } else {
8712 self.parse_method_arg_list_no_paren()?
8713 };
8714 expr = Expr {
8715 kind: ExprKind::MethodCall {
8716 object: Box::new(expr),
8717 method: "x".to_string(),
8718 args,
8719 super_call: false,
8720 },
8721 line,
8722 };
8723 }
8724 _ => break,
8725 }
8726 }
8727 Token::LBracket => {
8728 if self.peek_line() > self.prev_line() {
8731 break;
8732 }
8733 let line = expr.line;
8735 if matches!(expr.kind, ExprKind::ScalarVar(_)) {
8736 if let ExprKind::ScalarVar(ref name) = expr.kind {
8737 let name = name.clone();
8738 self.advance();
8739 let index = self.parse_expression()?;
8742 self.expect(&Token::RBracket)?;
8743 expr = Expr {
8744 kind: ExprKind::ArrayElement {
8745 array: name,
8746 index: Box::new(index),
8747 },
8748 line,
8749 };
8750 }
8751 } else if postfix_lbracket_is_arrow_container(&expr) {
8752 self.advance();
8753 let indices = self.parse_arg_list()?;
8754 self.expect(&Token::RBracket)?;
8755 expr = Expr {
8756 kind: ExprKind::ArrowDeref {
8757 expr: Box::new(expr),
8758 index: Box::new(Expr {
8759 kind: ExprKind::List(indices),
8760 line,
8761 }),
8762 kind: DerefKind::Array,
8763 },
8764 line,
8765 };
8766 } else {
8767 self.advance();
8768 let indices = self.parse_arg_list()?;
8769 self.expect(&Token::RBracket)?;
8770 expr = Expr {
8771 kind: ExprKind::AnonymousListSlice {
8772 source: Box::new(expr),
8773 indices,
8774 },
8775 line,
8776 };
8777 }
8778 }
8779 Token::LBrace => {
8780 if self.suppress_scalar_hash_brace > 0 {
8781 break;
8782 }
8783 if self.peek_line() > self.prev_line() {
8786 break;
8787 }
8788 let line = expr.line;
8791 let is_scalar_named_hash = matches!(expr.kind, ExprKind::ScalarVar(_));
8792 let is_chainable_hash_subscript = is_scalar_named_hash
8793 || matches!(
8794 expr.kind,
8795 ExprKind::HashElement { .. }
8796 | ExprKind::ArrayElement { .. }
8797 | ExprKind::ArrowDeref { .. }
8798 | ExprKind::Deref {
8799 kind: Sigil::Scalar,
8800 ..
8801 }
8802 );
8803 if !is_chainable_hash_subscript {
8804 break;
8805 }
8806 self.advance();
8807 let key = self.parse_hash_subscript_key()?;
8808 self.expect(&Token::RBrace)?;
8809 expr = if is_scalar_named_hash {
8810 if let ExprKind::ScalarVar(ref name) = expr.kind {
8811 let name = name.clone();
8812 if name == "_" {
8814 Expr {
8815 kind: ExprKind::ArrowDeref {
8816 expr: Box::new(Expr {
8817 kind: ExprKind::ScalarVar("_".into()),
8818 line,
8819 }),
8820 index: Box::new(key),
8821 kind: DerefKind::Hash,
8822 },
8823 line,
8824 }
8825 } else {
8826 Expr {
8827 kind: ExprKind::HashElement {
8828 hash: name,
8829 key: Box::new(key),
8830 },
8831 line,
8832 }
8833 }
8834 } else {
8835 unreachable!("is_scalar_named_hash implies ScalarVar");
8836 }
8837 } else {
8838 Expr {
8839 kind: ExprKind::ArrowDeref {
8840 expr: Box::new(expr),
8841 index: Box::new(key),
8842 kind: DerefKind::Hash,
8843 },
8844 line,
8845 }
8846 };
8847 }
8848 Token::LogNot | Token::BitNot => {
8849 if !matches!(expr.kind, ExprKind::ScalarVar(_)) {
8864 break;
8865 }
8866 if self.peek_line() > self.prev_line() {
8867 break;
8868 }
8869 let opener = self.peek().clone();
8870 let line = expr.line;
8871 let name = if let ExprKind::ScalarVar(ref n) = expr.kind {
8872 n.clone()
8873 } else {
8874 unreachable!()
8875 };
8876 self.advance(); self.suppress_tilde_range = self.suppress_tilde_range.saturating_add(1);
8882 let index_result = self.parse_expression();
8883 self.suppress_tilde_range = self.suppress_tilde_range.saturating_sub(1);
8884 let index = index_result?;
8885 let close_match = matches!(
8886 (&opener, self.peek()),
8887 (Token::LogNot, Token::LogNot) | (Token::BitNot, Token::BitNot)
8888 );
8889 if !close_match {
8890 let want = if matches!(opener, Token::LogNot) {
8891 "!"
8892 } else {
8893 "~"
8894 };
8895 return Err(self.syntax_err(
8896 format!("expected closing `{}` for string subscript", want),
8897 self.peek_line(),
8898 ));
8899 }
8900 self.advance(); expr = Expr {
8902 kind: ExprKind::ArrayElement {
8903 array: format!("__topicstr__{}", name),
8904 index: Box::new(index),
8905 },
8906 line,
8907 };
8908 }
8909 _ => break,
8910 }
8911 }
8912 Ok(expr)
8913 }
8914
8915 fn parse_primary(&mut self) -> StrykeResult<Expr> {
8916 let line = self.peek_line();
8917 if let Token::Ident(ref kw) = self.peek().clone() {
8922 if matches!(kw.as_str(), "my" | "our" | "state" | "local") {
8923 let kw_owned = kw.clone();
8924 let saved_pos = self.pos;
8929 let stmt = self.parse_my_our_local(&kw_owned, false)?;
8930 let decls = match stmt.kind {
8931 StmtKind::My(d)
8932 | StmtKind::Our(d)
8933 | StmtKind::State(d)
8934 | StmtKind::Local(d) => d,
8935 _ => {
8936 self.pos = saved_pos;
8941 return Err(self.syntax_err(
8942 "`my`/`our`/`local` in expression must declare variables",
8943 line,
8944 ));
8945 }
8946 };
8947 return Ok(Expr {
8948 kind: ExprKind::MyExpr {
8949 keyword: kw_owned,
8950 decls,
8951 },
8952 line,
8953 });
8954 }
8955 }
8956 match self.peek().clone() {
8957 Token::Integer(n) => {
8958 self.advance();
8959 Ok(Expr {
8960 kind: ExprKind::Integer(n),
8961 line,
8962 })
8963 }
8964 Token::Float(f) => {
8965 self.advance();
8966 Ok(Expr {
8967 kind: ExprKind::Float(f),
8968 line,
8969 })
8970 }
8971 Token::ArrowBrace => {
8977 self.advance();
8978 let mut stmts = Vec::new();
8979 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
8980 if self.eat(&Token::Semicolon) {
8981 continue;
8982 }
8983 stmts.push(self.parse_statement()?);
8984 }
8985 self.expect(&Token::RBrace)?;
8986 let inner_line = stmts.first().map(|s| s.line).unwrap_or(line);
8987 let inner = Expr {
8988 kind: ExprKind::CodeRef {
8989 params: vec![],
8990 body: stmts,
8991 },
8992 line: inner_line,
8993 };
8994 Ok(Expr {
8995 kind: ExprKind::Do(Box::new(inner)),
8996 line,
8997 })
8998 }
8999 Token::Star => {
9000 self.advance();
9001 if matches!(self.peek(), Token::LBrace) {
9002 self.advance();
9003 let inner = self.parse_expression()?;
9004 self.expect(&Token::RBrace)?;
9005 return Ok(Expr {
9006 kind: ExprKind::Deref {
9007 expr: Box::new(inner),
9008 kind: Sigil::Typeglob,
9009 },
9010 line,
9011 });
9012 }
9013 if matches!(
9015 self.peek(),
9016 Token::ScalarVar(_)
9017 | Token::ArrayVar(_)
9018 | Token::HashVar(_)
9019 | Token::DerefScalarVar(_)
9020 | Token::HashPercent
9021 ) {
9022 let inner = self.parse_postfix()?;
9023 return Ok(Expr {
9024 kind: ExprKind::TypeglobExpr(Box::new(inner)),
9025 line,
9026 });
9027 }
9028 let mut full_name = match self.advance() {
9030 (Token::Ident(n), _) => n,
9031 (Token::X, _) => "x".to_string(),
9032 (tok, l) => {
9033 return Err(self
9034 .syntax_err(format!("Expected identifier after *, got {:?}", tok), l));
9035 }
9036 };
9037 while self.eat(&Token::PackageSep) {
9038 match self.advance() {
9039 (Token::Ident(part), _) => {
9040 full_name = format!("{}::{}", full_name, part);
9041 }
9042 (Token::X, _) => {
9043 full_name = format!("{}::x", full_name);
9044 }
9045 (tok, l) => {
9046 return Err(self.syntax_err(
9047 format!("Expected identifier after :: in typeglob, got {:?}", tok),
9048 l,
9049 ));
9050 }
9051 }
9052 }
9053 Ok(Expr {
9054 kind: ExprKind::Typeglob(full_name),
9055 line,
9056 })
9057 }
9058 Token::SingleString(s) => {
9059 self.advance();
9060 Ok(Expr {
9061 kind: ExprKind::String(s),
9062 line,
9063 })
9064 }
9065 Token::DoubleString(s) => {
9066 self.advance();
9067 self.parse_interpolated_string(&s, line)
9068 }
9069 Token::BacktickString(s) => {
9070 self.advance();
9071 let inner = self.parse_interpolated_string(&s, line)?;
9072 Ok(Expr {
9073 kind: ExprKind::Qx(Box::new(inner)),
9074 line,
9075 })
9076 }
9077 Token::HereDoc(_, body, interpolate) => {
9078 self.advance();
9079 if interpolate {
9080 self.parse_interpolated_string(&body, line)
9081 } else {
9082 Ok(Expr {
9083 kind: ExprKind::String(body),
9084 line,
9085 })
9086 }
9087 }
9088 Token::Regex(pattern, flags, _delim) => {
9089 self.advance();
9090 Ok(Expr {
9091 kind: ExprKind::Regex(pattern, flags),
9092 line,
9093 })
9094 }
9095 Token::QW(words) => {
9096 self.advance();
9097 self.list_construct_close_pos = Some(self.pos);
9100 Ok(Expr {
9101 kind: ExprKind::QW(words),
9102 line,
9103 })
9104 }
9105 Token::DerefScalarVar(name) => {
9106 self.advance();
9107 Ok(Expr {
9108 kind: ExprKind::Deref {
9109 expr: Box::new(Expr {
9110 kind: ExprKind::ScalarVar(name),
9111 line,
9112 }),
9113 kind: Sigil::Scalar,
9114 },
9115 line,
9116 })
9117 }
9118 Token::ScalarVar(name) => {
9119 self.advance();
9120 Ok(Expr {
9121 kind: ExprKind::ScalarVar(name),
9122 line,
9123 })
9124 }
9125 Token::ArrayVar(name) => {
9126 self.advance();
9127 match self.peek() {
9129 Token::LBracket => {
9130 self.advance();
9131 let indices = self.parse_slice_arg_list(false)?;
9132 self.expect(&Token::RBracket)?;
9133 Ok(Expr {
9134 kind: ExprKind::ArraySlice {
9135 array: name,
9136 indices,
9137 },
9138 line,
9139 })
9140 }
9141 Token::LBrace if self.suppress_scalar_hash_brace == 0 => {
9142 self.advance();
9143 let keys = self.parse_slice_arg_list(true)?;
9144 self.expect(&Token::RBrace)?;
9145 Ok(Expr {
9146 kind: ExprKind::HashSlice { hash: name, keys },
9147 line,
9148 })
9149 }
9150 _ => Ok(Expr {
9151 kind: ExprKind::ArrayVar(name),
9152 line,
9153 }),
9154 }
9155 }
9156 Token::HashVar(name) => {
9157 self.advance();
9158 if matches!(self.peek(), Token::LBrace) && self.suppress_scalar_hash_brace == 0 {
9163 self.advance(); let keys = self.parse_slice_arg_list(true)?;
9165 self.expect(&Token::RBrace)?;
9166 return Ok(Expr {
9167 kind: ExprKind::HashKvSlice { hash: name, keys },
9168 line,
9169 });
9170 }
9171 Ok(Expr {
9172 kind: ExprKind::HashVar(name),
9173 line,
9174 })
9175 }
9176 Token::HashPercent => {
9177 self.advance();
9179 if matches!(self.peek(), Token::ScalarVar(_)) {
9180 let n = match self.advance() {
9181 (Token::ScalarVar(n), _) => n,
9182 (tok, l) => {
9183 return Err(self.syntax_err(
9184 format!("Expected scalar variable after %%, got {:?}", tok),
9185 l,
9186 ));
9187 }
9188 };
9189 return Ok(Expr {
9190 kind: ExprKind::Deref {
9191 expr: Box::new(Expr {
9192 kind: ExprKind::ScalarVar(n),
9193 line,
9194 }),
9195 kind: Sigil::Hash,
9196 },
9197 line,
9198 });
9199 }
9200 if matches!(self.peek(), Token::LBracket) {
9205 self.advance();
9206 let pairs = self.parse_hashref_pairs_until(&Token::RBracket)?;
9207 self.expect(&Token::RBracket)?;
9208 let href = Expr {
9209 kind: ExprKind::HashRef(pairs),
9210 line,
9211 };
9212 return Ok(Expr {
9213 kind: ExprKind::Deref {
9214 expr: Box::new(href),
9215 kind: Sigil::Hash,
9216 },
9217 line,
9218 });
9219 }
9220 self.expect(&Token::LBrace)?;
9221 let looks_like_pair = matches!(
9227 self.peek(),
9228 Token::Ident(_) | Token::SingleString(_) | Token::DoubleString(_)
9229 ) && matches!(self.peek_at(1), Token::FatArrow);
9230 let inner = if looks_like_pair {
9231 let pairs = self.parse_hashref_pairs_until(&Token::RBrace)?;
9232 Expr {
9233 kind: ExprKind::HashRef(pairs),
9234 line,
9235 }
9236 } else {
9237 self.parse_expression()?
9238 };
9239 self.expect(&Token::RBrace)?;
9240 Ok(Expr {
9241 kind: ExprKind::Deref {
9242 expr: Box::new(inner),
9243 kind: Sigil::Hash,
9244 },
9245 line,
9246 })
9247 }
9248 Token::ArrayAt => {
9249 self.advance();
9250 if matches!(self.peek(), Token::LBrace) {
9252 self.advance();
9253 let inner = self.parse_expression()?;
9254 self.expect(&Token::RBrace)?;
9255 if matches!(self.peek(), Token::LBrace) {
9260 self.advance();
9261 let keys = self.parse_slice_arg_list(true)?;
9262 self.expect(&Token::RBrace)?;
9263 return Ok(Expr {
9264 kind: ExprKind::HashSliceDeref {
9265 container: Box::new(inner),
9266 keys,
9267 },
9268 line,
9269 });
9270 }
9271 if matches!(self.peek(), Token::LBracket) {
9272 self.advance();
9273 let indices = self.parse_slice_arg_list(false)?;
9274 self.expect(&Token::RBracket)?;
9275 let source = Expr {
9276 kind: ExprKind::Deref {
9277 expr: Box::new(inner),
9278 kind: Sigil::Array,
9279 },
9280 line,
9281 };
9282 return Ok(Expr {
9283 kind: ExprKind::AnonymousListSlice {
9284 source: Box::new(source),
9285 indices,
9286 },
9287 line,
9288 });
9289 }
9290 return Ok(Expr {
9291 kind: ExprKind::Deref {
9292 expr: Box::new(inner),
9293 kind: Sigil::Array,
9294 },
9295 line,
9296 });
9297 }
9298 if matches!(self.peek(), Token::LBracket) {
9302 self.advance();
9303 let mut elems = Vec::new();
9304 if !matches!(self.peek(), Token::RBracket) {
9305 elems.push(self.parse_assign_expr()?);
9306 while self.eat(&Token::Comma) {
9307 if matches!(self.peek(), Token::RBracket) {
9308 break;
9309 }
9310 elems.push(self.parse_assign_expr()?);
9311 }
9312 }
9313 self.expect(&Token::RBracket)?;
9314 let aref = Expr {
9315 kind: ExprKind::ArrayRef(elems),
9316 line,
9317 };
9318 return Ok(Expr {
9319 kind: ExprKind::Deref {
9320 expr: Box::new(aref),
9321 kind: Sigil::Array,
9322 },
9323 line,
9324 });
9325 }
9326 let container = match self.peek().clone() {
9328 Token::ScalarVar(n) => {
9329 self.advance();
9330 Expr {
9331 kind: ExprKind::ScalarVar(n),
9332 line,
9333 }
9334 }
9335 _ => {
9336 return Err(self.syntax_err(
9337 "Expected `$name`, `{`, or `[` after `@` (e.g. `@$aref`, `@{expr}`, `@[1,2,3]`, or `@$href{keys}`)",
9338 line,
9339 ));
9340 }
9341 };
9342 if matches!(self.peek(), Token::LBrace) {
9343 self.advance();
9344 let keys = self.parse_slice_arg_list(true)?;
9345 self.expect(&Token::RBrace)?;
9346 return Ok(Expr {
9347 kind: ExprKind::HashSliceDeref {
9348 container: Box::new(container),
9349 keys,
9350 },
9351 line,
9352 });
9353 }
9354 Ok(Expr {
9355 kind: ExprKind::Deref {
9356 expr: Box::new(container),
9357 kind: Sigil::Array,
9358 },
9359 line,
9360 })
9361 }
9362 Token::LParen => {
9363 self.advance();
9364 if matches!(self.peek(), Token::RParen) {
9365 self.advance();
9366 self.list_construct_close_pos = Some(self.pos);
9369 return Ok(Expr {
9370 kind: ExprKind::List(vec![]),
9371 line,
9372 });
9373 }
9374 let saved_no_pipe = self.no_pipe_forward_depth;
9377 self.no_pipe_forward_depth = 0;
9378 let saved_indirect = self.suppress_indirect_paren_call;
9382 self.suppress_indirect_paren_call = 0;
9383 let expr = self.parse_expression();
9384 self.no_pipe_forward_depth = saved_no_pipe;
9385 self.suppress_indirect_paren_call = saved_indirect;
9386 let expr = expr?;
9387 self.expect(&Token::RParen)?;
9388 self.list_construct_close_pos = Some(self.pos);
9393 Ok(expr)
9394 }
9395 Token::LBracket => {
9396 self.advance();
9397 let elems = self.parse_arg_list()?;
9398 self.expect(&Token::RBracket)?;
9399 Ok(Expr {
9400 kind: ExprKind::ArrayRef(elems),
9401 line,
9402 })
9403 }
9404 Token::LBrace => {
9405 self.advance();
9407 let saved = self.pos;
9409 match self.try_parse_hash_ref() {
9410 Ok(pairs) => Ok(Expr {
9411 kind: ExprKind::HashRef(pairs),
9412 line,
9413 }),
9414 Err(_) => {
9415 self.pos = saved;
9416 let mut stmts = Vec::new();
9418 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
9419 if self.eat(&Token::Semicolon) {
9420 continue;
9421 }
9422 stmts.push(self.parse_statement()?);
9423 }
9424 self.expect(&Token::RBrace)?;
9425 Ok(Expr {
9426 kind: ExprKind::CodeRef {
9427 params: vec![],
9428 body: stmts,
9429 },
9430 line,
9431 })
9432 }
9433 }
9434 }
9435 Token::Diamond => {
9436 self.advance();
9437 Ok(Expr {
9438 kind: ExprKind::ReadLine(None),
9439 line,
9440 })
9441 }
9442 Token::ReadLine(handle) => {
9443 self.advance();
9444 Ok(Expr {
9445 kind: ExprKind::ReadLine(Some(handle)),
9446 line,
9447 })
9448 }
9449
9450 Token::ThreadArrow => {
9452 self.advance();
9453 self.parse_thread_macro(line, false)
9454 }
9455 Token::ThreadArrowLast => {
9456 self.advance();
9457 self.parse_thread_macro(line, true)
9458 }
9459 Token::ThreadArrowStream => {
9460 self.advance();
9461 let mut stages = Vec::new();
9462 self.parse_thread_macro_inner(line, false, Some(&mut stages))
9463 }
9464 Token::ThreadArrowStreamLast => {
9465 self.advance();
9466 let mut stages = Vec::new();
9467 self.parse_thread_macro_inner(line, true, Some(&mut stages))
9468 }
9469 Token::ThreadArrowPar => {
9470 self.advance();
9471 self.parse_thread_macro_chunk_par(line, false)
9472 }
9473 Token::ThreadArrowParLast => {
9474 self.advance();
9475 self.parse_thread_macro_chunk_par(line, true)
9476 }
9477 Token::ThreadArrowDist => {
9478 self.advance();
9479 self.parse_thread_macro_dist(line, false)
9480 }
9481 Token::ThreadArrowDistLast => {
9482 self.advance();
9483 self.parse_thread_macro_dist(line, true)
9484 }
9485 Token::Ident(ref name) => {
9486 let name = name.clone();
9487 if name.starts_with('\x00') {
9489 self.advance();
9490 let parts: Vec<&str> = name.split('\x00').collect();
9491 if parts.len() >= 4 && parts[1] == "s" {
9492 let delim = parts.get(5).and_then(|s| s.chars().next()).unwrap_or('/');
9493 return Ok(Expr {
9494 kind: ExprKind::Substitution {
9495 expr: Box::new(Expr {
9496 kind: ExprKind::ScalarVar("_".into()),
9497 line,
9498 }),
9499 pattern: parts[2].to_string(),
9500 replacement: parts[3].to_string(),
9501 flags: parts.get(4).unwrap_or(&"").to_string(),
9502 delim,
9503 },
9504 line,
9505 });
9506 }
9507 if parts.len() >= 4 && parts[1] == "tr" {
9508 let delim = parts.get(5).and_then(|s| s.chars().next()).unwrap_or('/');
9509 return Ok(Expr {
9510 kind: ExprKind::Transliterate {
9511 expr: Box::new(Expr {
9512 kind: ExprKind::ScalarVar("_".into()),
9513 line,
9514 }),
9515 from: parts[2].to_string(),
9516 to: parts[3].to_string(),
9517 flags: parts.get(4).unwrap_or(&"").to_string(),
9518 delim,
9519 },
9520 line,
9521 });
9522 }
9523 return Err(self.syntax_err("Unexpected encoded token", line));
9524 }
9525 self.parse_named_expr(name)
9526 }
9527
9528 Token::Percent => {
9531 self.advance();
9532 match self.peek().clone() {
9533 Token::Ident(name) => {
9534 self.advance();
9535 Ok(Expr {
9536 kind: ExprKind::HashVar(name),
9537 line,
9538 })
9539 }
9540 Token::ScalarVar(n) => {
9541 self.advance();
9542 Ok(Expr {
9543 kind: ExprKind::Deref {
9544 expr: Box::new(Expr {
9545 kind: ExprKind::ScalarVar(n),
9546 line,
9547 }),
9548 kind: Sigil::Hash,
9549 },
9550 line,
9551 })
9552 }
9553 Token::LBrace => {
9554 self.advance();
9555 let looks_like_pair = matches!(
9556 self.peek(),
9557 Token::Ident(_) | Token::SingleString(_) | Token::DoubleString(_)
9558 ) && matches!(self.peek_at(1), Token::FatArrow);
9559 let inner = if looks_like_pair {
9560 let pairs = self.parse_hashref_pairs_until(&Token::RBrace)?;
9561 Expr {
9562 kind: ExprKind::HashRef(pairs),
9563 line,
9564 }
9565 } else {
9566 self.parse_expression()?
9567 };
9568 self.expect(&Token::RBrace)?;
9569 Ok(Expr {
9570 kind: ExprKind::Deref {
9571 expr: Box::new(inner),
9572 kind: Sigil::Hash,
9573 },
9574 line,
9575 })
9576 }
9577 Token::LBracket => {
9578 self.advance();
9579 let pairs = self.parse_hashref_pairs_until(&Token::RBracket)?;
9580 self.expect(&Token::RBracket)?;
9581 let href = Expr {
9582 kind: ExprKind::HashRef(pairs),
9583 line,
9584 };
9585 Ok(Expr {
9586 kind: ExprKind::Deref {
9587 expr: Box::new(href),
9588 kind: Sigil::Hash,
9589 },
9590 line,
9591 })
9592 }
9593 tok => Err(self.syntax_err(
9594 format!(
9595 "Expected identifier, `$`, `{{`, or `[` after `%`, got {:?}",
9596 tok
9597 ),
9598 line,
9599 )),
9600 }
9601 }
9602
9603 tok => Err(self.syntax_err(format!("Unexpected token {:?}", tok), line)),
9604 }
9605 }
9606
9607 fn parse_named_expr(&mut self, mut name: String) -> StrykeResult<Expr> {
9608 let line = self.peek_line();
9609 self.advance(); while self.eat(&Token::PackageSep) {
9611 match self.advance() {
9612 (Token::Ident(part), _) => {
9613 name = format!("{}::{}", name, part);
9614 }
9615 (tok, err_line) => {
9616 return Err(self.syntax_err(
9617 format!("Expected identifier after `::`, got {:?}", tok),
9618 err_line,
9619 ));
9620 }
9621 }
9622 }
9623
9624 if matches!(self.peek(), Token::FatArrow) && !Self::is_underscore_topic_slot(&name) {
9631 return Ok(Expr {
9632 kind: ExprKind::String(name),
9633 line,
9634 });
9635 }
9636
9637 if crate::compat_mode() {
9638 if let Some(ext) = Self::stryke_extension_name(&name) {
9639 if !self.declared_subs.contains(&name) {
9640 return Err(self.syntax_err(
9641 format!("`{ext}` is a stryke extension (disabled by --compat)"),
9642 line,
9643 ));
9644 }
9645 }
9646 }
9647
9648 if let Some(rest) = name.strip_prefix("CORE::") {
9655 name = rest.to_string();
9656 }
9657
9658 match name.as_str() {
9659 "__FILE__" => Ok(Expr {
9660 kind: ExprKind::MagicConst(MagicConstKind::File),
9661 line,
9662 }),
9663 "__LINE__" => Ok(Expr {
9664 kind: ExprKind::MagicConst(MagicConstKind::Line),
9665 line,
9666 }),
9667 "__SUB__" => Ok(Expr {
9668 kind: ExprKind::MagicConst(MagicConstKind::Sub),
9669 line,
9670 }),
9671 "__PACKAGE__" => Ok(Expr {
9676 kind: ExprKind::String(self.current_package.clone()),
9677 line,
9678 }),
9679 "stdin" => Ok(Expr {
9680 kind: ExprKind::FuncCall {
9681 name: "stdin".into(),
9682 args: vec![],
9683 },
9684 line,
9685 }),
9686 "range" => {
9687 let args = self.parse_builtin_args()?;
9688 Ok(Expr {
9689 kind: ExprKind::FuncCall {
9690 name: "range".into(),
9691 args,
9692 },
9693 line,
9694 })
9695 }
9696 "print" | "pr" => self.parse_print_like(|h, a| ExprKind::Print { handle: h, args: a }),
9697 "say" => {
9698 if crate::no_interop_mode() {
9699 return Err(
9700 self.syntax_err("stryke uses `p` instead of `say` (--no-interop)", line)
9701 );
9702 }
9703 self.parse_print_like(|h, a| ExprKind::Say { handle: h, args: a })
9704 }
9705 "p" => self.parse_print_like(|h, a| ExprKind::Say { handle: h, args: a }),
9706 "printf" => self.parse_print_like(|h, a| ExprKind::Printf { handle: h, args: a }),
9707 "die" => {
9708 let args = self.parse_list_until_terminator()?;
9709 Ok(Expr {
9710 kind: ExprKind::Die(args),
9711 line,
9712 })
9713 }
9714 "warn" => {
9715 let args = self.parse_list_until_terminator()?;
9716 Ok(Expr {
9717 kind: ExprKind::Warn(args),
9718 line,
9719 })
9720 }
9721 "croak" | "confess" => {
9726 let args = self.parse_list_until_terminator()?;
9727 Ok(Expr {
9728 kind: ExprKind::Die(args),
9729 line,
9730 })
9731 }
9732 "carp" | "cluck" => {
9734 let args = self.parse_list_until_terminator()?;
9735 Ok(Expr {
9736 kind: ExprKind::Warn(args),
9737 line,
9738 })
9739 }
9740 "chomp" => {
9741 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9742 return Ok(e);
9743 }
9744 let a = self.parse_one_arg_or_default()?;
9745 Ok(Expr {
9746 kind: ExprKind::Chomp(Box::new(a)),
9747 line,
9748 })
9749 }
9750 "chop" => {
9751 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9752 return Ok(e);
9753 }
9754 let a = self.parse_one_arg_or_default()?;
9755 Ok(Expr {
9756 kind: ExprKind::Chop(Box::new(a)),
9757 line,
9758 })
9759 }
9760 "length" => {
9761 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9762 return Ok(e);
9763 }
9764 let a = self.parse_one_arg_or_default()?;
9765 Ok(Expr {
9766 kind: ExprKind::Length(Box::new(a)),
9767 line,
9768 })
9769 }
9770 "defined" => {
9771 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9772 return Ok(e);
9773 }
9774 let a = if matches!(
9782 self.peek(),
9783 Token::Semicolon
9784 | Token::RBrace
9785 | Token::RParen
9786 | Token::RBracket
9787 | Token::Eof
9788 | Token::Comma
9789 | Token::FatArrow
9790 | Token::PipeForward
9791 | Token::Question
9792 | Token::Colon
9793 | Token::NumEq
9794 | Token::NumNe
9795 | Token::NumLt
9796 | Token::NumGt
9797 | Token::NumLe
9798 | Token::NumGe
9799 | Token::Spaceship
9800 | Token::StrEq
9801 | Token::StrNe
9802 | Token::StrLt
9803 | Token::StrGt
9804 | Token::StrLe
9805 | Token::StrGe
9806 | Token::StrCmp
9807 | Token::LogAnd
9808 | Token::LogOr
9809 | Token::LogNot
9810 | Token::LogAndWord
9811 | Token::LogOrWord
9812 | Token::LogNotWord
9813 | Token::DefinedOr
9814 | Token::Range
9815 | Token::RangeExclusive
9816 | Token::Assign
9817 | Token::PlusAssign
9818 | Token::MinusAssign
9819 | Token::MulAssign
9820 | Token::DivAssign
9821 | Token::ModAssign
9822 | Token::PowAssign
9823 | Token::DotAssign
9824 | Token::AndAssign
9825 | Token::OrAssign
9826 | Token::XorAssign
9827 | Token::DefinedOrAssign
9828 | Token::ShiftLeftAssign
9829 | Token::ShiftRightAssign
9830 | Token::BitAndAssign
9831 | Token::BitOrAssign
9832 ) {
9833 Expr {
9834 kind: ExprKind::ScalarVar("_".into()),
9835 line: self.peek_line(),
9836 }
9837 } else if matches!(self.peek(), Token::LParen)
9838 && matches!(self.peek_at(1), Token::RParen)
9839 {
9840 let pl = self.peek_line();
9841 self.advance();
9842 self.advance();
9843 Expr {
9844 kind: ExprKind::ScalarVar("_".into()),
9845 line: pl,
9846 }
9847 } else {
9848 self.parse_named_unary_arg()?
9849 };
9850 Ok(Expr {
9851 kind: ExprKind::Defined(Box::new(a)),
9852 line,
9853 })
9854 }
9855 "ref" => {
9856 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9857 return Ok(e);
9858 }
9859 let a = self.parse_one_arg_or_default()?;
9860 Ok(Expr {
9861 kind: ExprKind::Ref(Box::new(a)),
9862 line,
9863 })
9864 }
9865 "undef" => {
9866 if self.peek_line() == self.prev_line()
9869 && matches!(
9870 self.peek(),
9871 Token::ScalarVar(_) | Token::ArrayVar(_) | Token::HashVar(_)
9872 )
9873 {
9874 let target = self.parse_primary()?;
9875 return Ok(Expr {
9876 kind: ExprKind::Assign {
9877 target: Box::new(target),
9878 value: Box::new(Expr {
9879 kind: ExprKind::Undef,
9880 line,
9881 }),
9882 },
9883 line,
9884 });
9885 }
9886 Ok(Expr {
9887 kind: ExprKind::Undef,
9888 line,
9889 })
9890 }
9891 "scalar" => {
9892 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9893 return Ok(e);
9894 }
9895 if crate::no_interop_mode() {
9896 return Err(self.syntax_err(
9897 "stryke uses `len` (also `cnt` / `count`) instead of `scalar` (--no-interop)",
9898 line,
9899 ));
9900 }
9901 let a = self.parse_one_arg_or_default()?;
9902 Ok(Expr {
9903 kind: ExprKind::ScalarContext(Box::new(a)),
9904 line,
9905 })
9906 }
9907 "abs" => {
9908 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9909 return Ok(e);
9910 }
9911 let a = self.parse_one_arg_or_default()?;
9912 Ok(Expr {
9913 kind: ExprKind::Abs(Box::new(a)),
9914 line,
9915 })
9916 }
9917 "inc" | "dec" => {
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::FuncCall {
9928 name,
9929 args: vec![a],
9930 },
9931 line,
9932 })
9933 }
9934 "int" => {
9935 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9936 return Ok(e);
9937 }
9938 let a = self.parse_one_arg_or_default()?;
9939 Ok(Expr {
9940 kind: ExprKind::Int(Box::new(a)),
9941 line,
9942 })
9943 }
9944 "sqrt" => {
9945 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9946 return Ok(e);
9947 }
9948 let a = self.parse_one_arg_or_default()?;
9949 Ok(Expr {
9950 kind: ExprKind::Sqrt(Box::new(a)),
9951 line,
9952 })
9953 }
9954 "sin" => {
9955 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9956 return Ok(e);
9957 }
9958 let a = self.parse_one_arg_or_default()?;
9959 Ok(Expr {
9960 kind: ExprKind::Sin(Box::new(a)),
9961 line,
9962 })
9963 }
9964 "cos" => {
9965 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9966 return Ok(e);
9967 }
9968 let a = self.parse_one_arg_or_default()?;
9969 Ok(Expr {
9970 kind: ExprKind::Cos(Box::new(a)),
9971 line,
9972 })
9973 }
9974 "atan2" => {
9975 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9976 return Ok(e);
9977 }
9978 let args = self.parse_builtin_args()?;
9979 if args.len() != 2 {
9980 return Err(self.syntax_err("atan2 requires two arguments", line));
9981 }
9982 Ok(Expr {
9983 kind: ExprKind::Atan2 {
9984 y: Box::new(args[0].clone()),
9985 x: Box::new(args[1].clone()),
9986 },
9987 line,
9988 })
9989 }
9990 "exp" => {
9991 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9992 return Ok(e);
9993 }
9994 let a = self.parse_one_arg_or_default()?;
9995 Ok(Expr {
9996 kind: ExprKind::Exp(Box::new(a)),
9997 line,
9998 })
9999 }
10000 "log" => {
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::Log(Box::new(a)),
10007 line,
10008 })
10009 }
10010 "input" => {
10011 let args = if matches!(
10012 self.peek(),
10013 Token::Semicolon
10014 | Token::RBrace
10015 | Token::RParen
10016 | Token::Eof
10017 | Token::Comma
10018 | Token::PipeForward
10019 ) {
10020 vec![]
10021 } else if matches!(self.peek(), Token::LParen) {
10022 self.advance();
10023 if matches!(self.peek(), Token::RParen) {
10024 self.advance();
10025 vec![]
10026 } else {
10027 let a = self.parse_expression()?;
10028 self.expect(&Token::RParen)?;
10029 vec![a]
10030 }
10031 } else {
10032 let a = self.parse_one_arg()?;
10033 vec![a]
10034 };
10035 Ok(Expr {
10036 kind: ExprKind::FuncCall {
10037 name: "input".to_string(),
10038 args,
10039 },
10040 line,
10041 })
10042 }
10043 "rand" => {
10044 if matches!(
10045 self.peek(),
10046 Token::Semicolon
10047 | Token::RBrace
10048 | Token::RParen
10049 | Token::Eof
10050 | Token::Comma
10051 | Token::PipeForward
10052 ) {
10053 Ok(Expr {
10054 kind: ExprKind::Rand(None),
10055 line,
10056 })
10057 } else if matches!(self.peek(), Token::LParen) {
10058 self.advance();
10059 if matches!(self.peek(), Token::RParen) {
10060 self.advance();
10061 Ok(Expr {
10062 kind: ExprKind::Rand(None),
10063 line,
10064 })
10065 } else {
10066 let a = self.parse_expression()?;
10067 self.expect(&Token::RParen)?;
10068 Ok(Expr {
10069 kind: ExprKind::Rand(Some(Box::new(a))),
10070 line,
10071 })
10072 }
10073 } else {
10074 let a = self.parse_one_arg()?;
10075 Ok(Expr {
10076 kind: ExprKind::Rand(Some(Box::new(a))),
10077 line,
10078 })
10079 }
10080 }
10081 "srand" => {
10082 if matches!(
10083 self.peek(),
10084 Token::Semicolon
10085 | Token::RBrace
10086 | Token::RParen
10087 | Token::Eof
10088 | Token::Comma
10089 | Token::PipeForward
10090 ) {
10091 Ok(Expr {
10092 kind: ExprKind::Srand(None),
10093 line,
10094 })
10095 } else if matches!(self.peek(), Token::LParen) {
10096 self.advance();
10097 if matches!(self.peek(), Token::RParen) {
10098 self.advance();
10099 Ok(Expr {
10100 kind: ExprKind::Srand(None),
10101 line,
10102 })
10103 } else {
10104 let a = self.parse_expression()?;
10105 self.expect(&Token::RParen)?;
10106 Ok(Expr {
10107 kind: ExprKind::Srand(Some(Box::new(a))),
10108 line,
10109 })
10110 }
10111 } else {
10112 let a = self.parse_one_arg()?;
10113 Ok(Expr {
10114 kind: ExprKind::Srand(Some(Box::new(a))),
10115 line,
10116 })
10117 }
10118 }
10119 "hex" => {
10120 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10121 return Ok(e);
10122 }
10123 let a = self.parse_one_arg_or_default()?;
10124 Ok(Expr {
10125 kind: ExprKind::Hex(Box::new(a)),
10126 line,
10127 })
10128 }
10129 "oct" => {
10130 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10131 return Ok(e);
10132 }
10133 let a = self.parse_one_arg_or_default()?;
10134 Ok(Expr {
10135 kind: ExprKind::Oct(Box::new(a)),
10136 line,
10137 })
10138 }
10139 "chr" => {
10140 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10141 return Ok(e);
10142 }
10143 let a = self.parse_one_arg_or_default()?;
10144 Ok(Expr {
10145 kind: ExprKind::Chr(Box::new(a)),
10146 line,
10147 })
10148 }
10149 "ord" => {
10150 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10151 return Ok(e);
10152 }
10153 let a = self.parse_one_arg_or_default()?;
10154 Ok(Expr {
10155 kind: ExprKind::Ord(Box::new(a)),
10156 line,
10157 })
10158 }
10159 "lc" => {
10160 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10161 return Ok(e);
10162 }
10163 let a = self.parse_one_arg_or_default()?;
10164 Ok(Expr {
10165 kind: ExprKind::Lc(Box::new(a)),
10166 line,
10167 })
10168 }
10169 "uc" => {
10170 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10171 return Ok(e);
10172 }
10173 let a = self.parse_one_arg_or_default()?;
10174 Ok(Expr {
10175 kind: ExprKind::Uc(Box::new(a)),
10176 line,
10177 })
10178 }
10179 "lcfirst" => {
10180 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10181 return Ok(e);
10182 }
10183 let a = self.parse_one_arg_or_default()?;
10184 Ok(Expr {
10185 kind: ExprKind::Lcfirst(Box::new(a)),
10186 line,
10187 })
10188 }
10189 "ucfirst" => {
10190 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10191 return Ok(e);
10192 }
10193 let a = self.parse_one_arg_or_default()?;
10194 Ok(Expr {
10195 kind: ExprKind::Ucfirst(Box::new(a)),
10196 line,
10197 })
10198 }
10199 "fc" => {
10200 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10201 return Ok(e);
10202 }
10203 let a = self.parse_one_arg_or_default()?;
10204 Ok(Expr {
10205 kind: ExprKind::Fc(Box::new(a)),
10206 line,
10207 })
10208 }
10209 "crypt" => {
10210 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10211 return Ok(e);
10212 }
10213 let args = self.parse_builtin_args()?;
10214 if args.len() != 2 {
10215 return Err(self.syntax_err("crypt requires two arguments", line));
10216 }
10217 Ok(Expr {
10218 kind: ExprKind::Crypt {
10219 plaintext: Box::new(args[0].clone()),
10220 salt: Box::new(args[1].clone()),
10221 },
10222 line,
10223 })
10224 }
10225 "pos" => {
10226 if matches!(
10227 self.peek(),
10228 Token::Semicolon
10229 | Token::RBrace
10230 | Token::RParen
10231 | Token::Eof
10232 | Token::Comma
10233 | Token::PipeForward
10234 ) {
10235 Ok(Expr {
10236 kind: ExprKind::Pos(None),
10237 line,
10238 })
10239 } else if matches!(self.peek(), Token::Assign) {
10240 self.advance();
10242 let rhs = self.parse_assign_expr()?;
10243 Ok(Expr {
10244 kind: ExprKind::Assign {
10245 target: Box::new(Expr {
10246 kind: ExprKind::Pos(Some(Box::new(Expr {
10247 kind: ExprKind::ScalarVar("_".into()),
10248 line,
10249 }))),
10250 line,
10251 }),
10252 value: Box::new(rhs),
10253 },
10254 line,
10255 })
10256 } else if matches!(self.peek(), Token::LParen) {
10257 self.advance();
10258 if matches!(self.peek(), Token::RParen) {
10259 self.advance();
10260 Ok(Expr {
10261 kind: ExprKind::Pos(None),
10262 line,
10263 })
10264 } else {
10265 let a = self.parse_expression()?;
10266 self.expect(&Token::RParen)?;
10267 Ok(Expr {
10268 kind: ExprKind::Pos(Some(Box::new(a))),
10269 line,
10270 })
10271 }
10272 } else {
10273 let saved = self.pos;
10274 let subj = self.parse_unary()?;
10275 if matches!(self.peek(), Token::Assign) {
10276 self.advance();
10277 let rhs = self.parse_assign_expr()?;
10278 Ok(Expr {
10279 kind: ExprKind::Assign {
10280 target: Box::new(Expr {
10281 kind: ExprKind::Pos(Some(Box::new(subj))),
10282 line,
10283 }),
10284 value: Box::new(rhs),
10285 },
10286 line,
10287 })
10288 } else {
10289 self.pos = saved;
10290 let a = self.parse_one_arg()?;
10291 Ok(Expr {
10292 kind: ExprKind::Pos(Some(Box::new(a))),
10293 line,
10294 })
10295 }
10296 }
10297 }
10298 "study" => {
10299 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10300 return Ok(e);
10301 }
10302 let a = self.parse_one_arg_or_default()?;
10303 Ok(Expr {
10304 kind: ExprKind::Study(Box::new(a)),
10305 line,
10306 })
10307 }
10308 "push" => {
10309 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10310 return Ok(e);
10311 }
10312 let args = self.parse_builtin_args()?;
10313 let (first, rest) = args
10314 .split_first()
10315 .ok_or_else(|| self.syntax_err("push requires arguments", line))?;
10316 if matches!(
10321 first.kind,
10322 ExprKind::ScalarVar(_)
10323 | ExprKind::Integer(_)
10324 | ExprKind::Float(_)
10325 | ExprKind::String(_)
10326 ) {
10327 return Err(self
10328 .syntax_err("Experimental push on scalar is now forbidden", line)
10329 .with_near("at EOF"));
10330 }
10331 Ok(Expr {
10332 kind: ExprKind::Push {
10333 array: Box::new(first.clone()),
10334 values: rest.to_vec(),
10335 },
10336 line,
10337 })
10338 }
10339 "pop" => {
10340 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10341 return Ok(e);
10342 }
10343 let a = self.parse_one_arg_or_argv()?;
10344 Ok(Expr {
10345 kind: ExprKind::Pop(Box::new(a)),
10346 line,
10347 })
10348 }
10349 "shift" => {
10350 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10351 return Ok(e);
10352 }
10353 let a = self.parse_one_arg_or_argv()?;
10354 Ok(Expr {
10355 kind: ExprKind::Shift(Box::new(a)),
10356 line,
10357 })
10358 }
10359 "unshift" => {
10360 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10361 return Ok(e);
10362 }
10363 let args = self.parse_builtin_args()?;
10364 let (first, rest) = args
10365 .split_first()
10366 .ok_or_else(|| self.syntax_err("unshift requires arguments", line))?;
10367 Ok(Expr {
10368 kind: ExprKind::Unshift {
10369 array: Box::new(first.clone()),
10370 values: rest.to_vec(),
10371 },
10372 line,
10373 })
10374 }
10375 "splice" => {
10376 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10377 return Ok(e);
10378 }
10379 let args = self.parse_builtin_args()?;
10380 let mut iter = args.into_iter();
10381 let array = Box::new(
10382 iter.next()
10383 .ok_or_else(|| self.syntax_err("splice requires arguments", line))?,
10384 );
10385 let offset = iter.next().map(Box::new);
10386 let length = iter.next().map(Box::new);
10387 let replacement: Vec<Expr> = iter.collect();
10388 Ok(Expr {
10389 kind: ExprKind::Splice {
10390 array,
10391 offset,
10392 length,
10393 replacement,
10394 },
10395 line,
10396 })
10397 }
10398 "splice_last" | "splice1" | "spl_last" => {
10403 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10404 return Ok(e);
10405 }
10406 let args = self.parse_builtin_args()?;
10407 let mut iter = args.into_iter();
10408 let array = Box::new(
10409 iter.next()
10410 .ok_or_else(|| self.syntax_err("splice_last requires arguments", line))?,
10411 );
10412 let offset = iter.next().map(Box::new);
10413 let length = iter.next().map(Box::new);
10414 let replacement: Vec<Expr> = iter.collect();
10415 let splice_expr = Expr {
10416 kind: ExprKind::Splice {
10417 array,
10418 offset,
10419 length,
10420 replacement,
10421 },
10422 line,
10423 };
10424 Ok(Expr {
10425 kind: ExprKind::FuncCall {
10426 name: "tail".to_string(),
10427 args: vec![splice_expr],
10428 },
10429 line,
10430 })
10431 }
10432 "delete" => {
10433 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10434 return Ok(e);
10435 }
10436 let a = self.parse_postfix()?;
10437 Ok(Expr {
10438 kind: ExprKind::Delete(Box::new(a)),
10439 line,
10440 })
10441 }
10442 "exists" => {
10443 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10444 return Ok(e);
10445 }
10446 let a = self.parse_unary()?;
10451 Ok(Expr {
10452 kind: ExprKind::Exists(Box::new(a)),
10453 line,
10454 })
10455 }
10456 "keys" => {
10457 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10458 return Ok(e);
10459 }
10460 let a = self.parse_one_arg_or_default()?;
10461 Ok(Expr {
10462 kind: ExprKind::Keys(Box::new(a)),
10463 line,
10464 })
10465 }
10466 "values" => {
10467 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10468 return Ok(e);
10469 }
10470 let a = self.parse_one_arg_or_default()?;
10471 Ok(Expr {
10472 kind: ExprKind::Values(Box::new(a)),
10473 line,
10474 })
10475 }
10476 "each" => {
10477 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10478 return Ok(e);
10479 }
10480 let a = self.parse_one_arg_or_default()?;
10481 Ok(Expr {
10482 kind: ExprKind::Each(Box::new(a)),
10483 line,
10484 })
10485 }
10486 "fore" | "e" | "ep" => {
10487 if matches!(self.peek(), Token::LBrace) {
10489 let (block, list) = self.parse_block_list()?;
10490 Ok(Expr {
10491 kind: ExprKind::ForEachExpr {
10492 block,
10493 list: Box::new(list),
10494 },
10495 line,
10496 })
10497 } else if self.in_pipe_rhs() {
10498 let is_terminal = matches!(
10501 self.peek(),
10502 Token::Semicolon
10503 | Token::RParen
10504 | Token::Eof
10505 | Token::PipeForward
10506 | Token::RBrace
10507 );
10508 let block = if name == "ep" && is_terminal {
10509 vec![Statement {
10510 label: None,
10511 kind: StmtKind::Expression(Expr {
10512 kind: ExprKind::Say {
10513 handle: None,
10514 args: vec![Expr {
10515 kind: ExprKind::ScalarVar("_".into()),
10516 line,
10517 }],
10518 },
10519 line,
10520 }),
10521 line,
10522 }]
10523 } else {
10524 let expr = self.parse_assign_expr_stop_at_pipe()?;
10525 let expr = Self::lift_bareword_to_topic_call(expr);
10526 vec![Statement {
10527 label: None,
10528 kind: StmtKind::Expression(expr),
10529 line,
10530 }]
10531 };
10532 let list = self.pipe_placeholder_list(line);
10533 Ok(Expr {
10534 kind: ExprKind::ForEachExpr {
10535 block,
10536 list: Box::new(list),
10537 },
10538 line,
10539 })
10540 } else {
10541 let expr = self.parse_assign_expr()?;
10550 let expr = Self::lift_bareword_to_topic_call(expr);
10551 if !matches!(self.peek(), Token::Comma) && name == "ep" {
10552 let block = vec![Statement {
10553 label: None,
10554 kind: StmtKind::Expression(Expr {
10555 kind: ExprKind::Say {
10556 handle: None,
10557 args: vec![Expr {
10558 kind: ExprKind::ScalarVar("_".into()),
10559 line,
10560 }],
10561 },
10562 line,
10563 }),
10564 line,
10565 }];
10566 return Ok(Expr {
10567 kind: ExprKind::ForEachExpr {
10568 block,
10569 list: Box::new(expr),
10570 },
10571 line,
10572 });
10573 }
10574 self.expect(&Token::Comma)?;
10575 let list_parts = self.parse_list_until_terminator()?;
10576 let list_expr = if list_parts.len() == 1 {
10577 list_parts.into_iter().next().unwrap()
10578 } else {
10579 Expr {
10580 kind: ExprKind::List(list_parts),
10581 line,
10582 }
10583 };
10584 let block = vec![Statement {
10585 label: None,
10586 kind: StmtKind::Expression(expr),
10587 line,
10588 }];
10589 Ok(Expr {
10590 kind: ExprKind::ForEachExpr {
10591 block,
10592 list: Box::new(list_expr),
10593 },
10594 line,
10595 })
10596 }
10597 }
10598 "rev" => {
10599 let prev = self.prev_line();
10605 let a = if self.in_pipe_rhs()
10606 && (matches!(
10607 self.peek(),
10608 Token::Semicolon | Token::RParen | Token::Eof | Token::PipeForward
10609 ) || self.peek_line() > prev)
10610 {
10611 self.pipe_placeholder_list(line)
10612 } else if self.peek_line() > prev {
10613 Expr {
10619 kind: ExprKind::ScalarVar("_".into()),
10620 line: prev,
10621 }
10622 } else if matches!(
10623 self.peek(),
10624 Token::Semicolon
10625 | Token::RBrace
10626 | Token::RParen
10627 | Token::RBracket
10628 | Token::Eof
10629 | Token::Comma
10630 | Token::FatArrow
10631 | Token::PipeForward
10632 ) {
10633 Expr {
10634 kind: ExprKind::ScalarVar("_".into()),
10635 line: self.peek_line(),
10636 }
10637 } else if matches!(self.peek(), Token::LParen)
10638 && matches!(self.peek_at(1), Token::RParen)
10639 {
10640 let pl = self.peek_line();
10643 self.advance(); self.advance(); Expr {
10646 kind: ExprKind::ScalarVar("_".into()),
10647 line: pl,
10648 }
10649 } else {
10650 self.parse_one_arg()?
10651 };
10652 Ok(Expr {
10653 kind: ExprKind::Rev(Box::new(a)),
10654 line,
10655 })
10656 }
10657 "reverse" => {
10658 if crate::no_interop_mode() {
10659 return Err(self.syntax_err(
10660 "stryke uses `rev` instead of `reverse` (--no-interop)",
10661 line,
10662 ));
10663 }
10664 let a = if self.in_pipe_rhs()
10666 && matches!(
10667 self.peek(),
10668 Token::Semicolon
10669 | Token::RBrace
10670 | Token::RParen
10671 | Token::Eof
10672 | Token::PipeForward
10673 ) {
10674 self.pipe_placeholder_list(line)
10675 } else if matches!(self.peek(), Token::LParen)
10676 && matches!(self.peek_at(1), Token::RParen)
10677 {
10678 self.advance();
10680 self.advance();
10681 Expr {
10682 kind: ExprKind::List(Vec::new()),
10683 line,
10684 }
10685 } else {
10686 self.parse_one_arg()?
10687 };
10688 Ok(Expr {
10689 kind: ExprKind::ReverseExpr(Box::new(a)),
10690 line,
10691 })
10692 }
10693 "reversed" | "rv" => {
10694 let a = if self.in_pipe_rhs()
10696 && matches!(
10697 self.peek(),
10698 Token::Semicolon
10699 | Token::RBrace
10700 | Token::RParen
10701 | Token::Eof
10702 | Token::PipeForward
10703 ) {
10704 self.pipe_placeholder_list(line)
10705 } else {
10706 self.parse_one_arg()?
10707 };
10708 Ok(Expr {
10709 kind: ExprKind::Rev(Box::new(a)),
10710 line,
10711 })
10712 }
10713 "join" => {
10714 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10715 return Ok(e);
10716 }
10717 let args = self.parse_builtin_args()?;
10718 if args.is_empty() {
10719 return Err(self.syntax_err("join requires separator and list", line));
10720 }
10721 if args.len() < 2 && !self.in_pipe_rhs() {
10723 return Err(self.syntax_err("join requires separator and list", line));
10724 }
10725 Ok(Expr {
10726 kind: ExprKind::JoinExpr {
10727 separator: Box::new(args[0].clone()),
10728 list: Box::new(Expr {
10729 kind: ExprKind::List(args[1..].to_vec()),
10730 line,
10731 }),
10732 },
10733 line,
10734 })
10735 }
10736 "split" => {
10737 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10738 return Ok(e);
10739 }
10740 let args = self.parse_builtin_args()?;
10741 let pattern = args.first().cloned().unwrap_or(Expr {
10742 kind: ExprKind::String(" ".into()),
10743 line,
10744 });
10745 let string = args.get(1).cloned().unwrap_or(Expr {
10746 kind: ExprKind::ScalarVar("_".into()),
10747 line,
10748 });
10749 let limit = args.get(2).cloned().map(Box::new);
10750 Ok(Expr {
10751 kind: ExprKind::SplitExpr {
10752 pattern: Box::new(pattern),
10753 string: Box::new(string),
10754 limit,
10755 },
10756 line,
10757 })
10758 }
10759 "substr" => {
10760 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10761 return Ok(e);
10762 }
10763 let args = self.parse_builtin_args()?;
10764 Ok(Expr {
10765 kind: ExprKind::Substr {
10766 string: Box::new(args[0].clone()),
10767 offset: Box::new(args[1].clone()),
10768 length: args.get(2).cloned().map(Box::new),
10769 replacement: args.get(3).cloned().map(Box::new),
10770 },
10771 line,
10772 })
10773 }
10774 "index" => {
10775 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10776 return Ok(e);
10777 }
10778 let args = self.parse_builtin_args()?;
10779 Ok(Expr {
10780 kind: ExprKind::Index {
10781 string: Box::new(args[0].clone()),
10782 substr: Box::new(args[1].clone()),
10783 position: args.get(2).cloned().map(Box::new),
10784 },
10785 line,
10786 })
10787 }
10788 "rindex" => {
10789 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10790 return Ok(e);
10791 }
10792 let args = self.parse_builtin_args()?;
10793 Ok(Expr {
10794 kind: ExprKind::Rindex {
10795 string: Box::new(args[0].clone()),
10796 substr: Box::new(args[1].clone()),
10797 position: args.get(2).cloned().map(Box::new),
10798 },
10799 line,
10800 })
10801 }
10802 "sprintf" => {
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 (first, rest) = args
10808 .split_first()
10809 .ok_or_else(|| self.syntax_err("sprintf requires format", line))?;
10810 Ok(Expr {
10811 kind: ExprKind::Sprintf {
10812 format: Box::new(first.clone()),
10813 args: rest.to_vec(),
10814 },
10815 line,
10816 })
10817 }
10818 "map" | "flat_map" | "maps" | "flat_maps" => {
10819 let flatten_array_refs = matches!(name.as_str(), "flat_map" | "flat_maps");
10820 let stream = matches!(name.as_str(), "maps" | "flat_maps");
10821 if matches!(self.peek(), Token::LBrace) {
10822 let (block, list) = self.parse_block_list()?;
10823 Ok(Expr {
10824 kind: ExprKind::MapExpr {
10825 block,
10826 list: Box::new(list),
10827 flatten_array_refs,
10828 stream,
10829 },
10830 line,
10831 })
10832 } else {
10833 let expr = self.parse_assign_expr_stop_at_pipe()?;
10834 let expr = Self::lift_bareword_to_topic_call(expr);
10837 let list_expr = if self.pipe_supplies_slurped_list_operand() {
10838 self.pipe_placeholder_list(line)
10839 } else {
10840 self.expect(&Token::Comma)?;
10841 let list_parts = self.parse_list_until_terminator()?;
10842 if list_parts.len() == 1 {
10843 list_parts.into_iter().next().unwrap()
10844 } else {
10845 Expr {
10846 kind: ExprKind::List(list_parts),
10847 line,
10848 }
10849 }
10850 };
10851 Ok(Expr {
10852 kind: ExprKind::MapExprComma {
10853 expr: Box::new(expr),
10854 list: Box::new(list_expr),
10855 flatten_array_refs,
10856 stream,
10857 },
10858 line,
10859 })
10860 }
10861 }
10862 "cond" => {
10863 if crate::compat_mode() {
10864 return Err(self
10865 .syntax_err("`cond` is a stryke extension (disabled by --compat)", line));
10866 }
10867 self.parse_cond_expr(line)
10868 }
10869 "match" => {
10870 if crate::compat_mode() {
10871 return Err(self.syntax_err(
10872 "algebraic `match` is a stryke extension (disabled by --compat)",
10873 line,
10874 ));
10875 }
10876 self.parse_algebraic_match_expr(line)
10877 }
10878 "grep" | "greps" | "filter" | "fi" | "find_all" => {
10879 let keyword = match name.as_str() {
10880 "grep" => crate::ast::GrepBuiltinKeyword::Grep,
10881 "greps" => crate::ast::GrepBuiltinKeyword::Greps,
10882 "filter" | "fi" => crate::ast::GrepBuiltinKeyword::Filter,
10883 "find_all" => crate::ast::GrepBuiltinKeyword::FindAll,
10884 _ => unreachable!(),
10885 };
10886 if matches!(self.peek(), Token::LBrace) {
10887 let (block, list) = self.parse_block_list()?;
10888 Ok(Expr {
10889 kind: ExprKind::GrepExpr {
10890 block,
10891 list: Box::new(list),
10892 keyword,
10893 },
10894 line,
10895 })
10896 } else {
10897 let expr = self.parse_assign_expr_stop_at_pipe()?;
10898 if self.pipe_supplies_slurped_list_operand() {
10899 let list = self.pipe_placeholder_list(line);
10904 let topic = Expr {
10905 kind: ExprKind::ScalarVar("_".into()),
10906 line,
10907 };
10908 let test = match &expr.kind {
10909 ExprKind::Integer(_) | ExprKind::Float(_) => Expr {
10910 kind: ExprKind::BinOp {
10911 op: BinOp::NumEq,
10912 left: Box::new(topic),
10913 right: Box::new(expr),
10914 },
10915 line,
10916 },
10917 ExprKind::String(_) | ExprKind::InterpolatedString(_) => Expr {
10918 kind: ExprKind::BinOp {
10919 op: BinOp::StrEq,
10920 left: Box::new(topic),
10921 right: Box::new(expr),
10922 },
10923 line,
10924 },
10925 ExprKind::Regex { .. } => Expr {
10926 kind: ExprKind::BinOp {
10927 op: BinOp::BindMatch,
10928 left: Box::new(topic),
10929 right: Box::new(expr),
10930 },
10931 line,
10932 },
10933 _ => {
10934 let expr = Self::lift_bareword_to_topic_call(expr);
10940 return Ok(Expr {
10941 kind: ExprKind::GrepExprComma {
10942 expr: Box::new(expr),
10943 list: Box::new(list),
10944 keyword,
10945 },
10946 line,
10947 });
10948 }
10949 };
10950 let block = vec![Statement {
10951 label: None,
10952 kind: StmtKind::Expression(test),
10953 line,
10954 }];
10955 Ok(Expr {
10956 kind: ExprKind::GrepExpr {
10957 block,
10958 list: Box::new(list),
10959 keyword,
10960 },
10961 line,
10962 })
10963 } else {
10964 let expr = Self::lift_bareword_to_topic_call(expr);
10965 self.expect(&Token::Comma)?;
10966 let list_parts = self.parse_list_until_terminator()?;
10967 let list_expr = if list_parts.len() == 1 {
10968 list_parts.into_iter().next().unwrap()
10969 } else {
10970 Expr {
10971 kind: ExprKind::List(list_parts),
10972 line,
10973 }
10974 };
10975 Ok(Expr {
10976 kind: ExprKind::GrepExprComma {
10977 expr: Box::new(expr),
10978 list: Box::new(list_expr),
10979 keyword,
10980 },
10981 line,
10982 })
10983 }
10984 }
10985 }
10986 "sort" => {
10987 use crate::ast::SortComparator;
10988 if matches!(self.peek(), Token::LBrace) {
10989 let block = self.parse_block()?;
10990 let block_end_line = self.prev_line();
10991 let _ = self.eat(&Token::Comma);
10992 let list = if self.in_pipe_rhs()
10993 && (matches!(
10994 self.peek(),
10995 Token::Semicolon
10996 | Token::RBrace
10997 | Token::RParen
10998 | Token::Eof
10999 | Token::PipeForward
11000 ) || self.peek_line() > block_end_line)
11001 {
11002 self.pipe_placeholder_list(line)
11003 } else {
11004 self.parse_expression()?
11005 };
11006 Ok(Expr {
11007 kind: ExprKind::SortExpr {
11008 cmp: Some(SortComparator::Block(block)),
11009 list: Box::new(list),
11010 },
11011 line,
11012 })
11013 } else if matches!(self.peek(), Token::ScalarVar(ref v) if v == "a" || v == "b") {
11014 let block = self.parse_block_or_bareword_cmp_block()?;
11016 let _ = self.eat(&Token::Comma);
11017 let list = if self.in_pipe_rhs()
11018 && matches!(
11019 self.peek(),
11020 Token::Semicolon
11021 | Token::RBrace
11022 | Token::RParen
11023 | Token::Eof
11024 | Token::PipeForward
11025 ) {
11026 self.pipe_placeholder_list(line)
11027 } else {
11028 self.parse_expression()?
11029 };
11030 Ok(Expr {
11031 kind: ExprKind::SortExpr {
11032 cmp: Some(SortComparator::Block(block)),
11033 list: Box::new(list),
11034 },
11035 line,
11036 })
11037 } else if matches!(self.peek(), Token::ScalarVar(_)) {
11038 self.suppress_indirect_paren_call =
11041 self.suppress_indirect_paren_call.saturating_add(1);
11042 let code = self.parse_assign_expr()?;
11043 self.suppress_indirect_paren_call =
11044 self.suppress_indirect_paren_call.saturating_sub(1);
11045 let _ = self.eat(&Token::Comma);
11046 let list = if self.in_pipe_rhs()
11047 && matches!(
11048 self.peek(),
11049 Token::Semicolon
11050 | Token::RBrace
11051 | Token::RParen
11052 | Token::Eof
11053 | Token::PipeForward
11054 ) {
11055 self.pipe_placeholder_list(line)
11056 } else if matches!(self.peek(), Token::LParen) {
11057 self.advance();
11058 let e = self.parse_expression()?;
11059 self.expect(&Token::RParen)?;
11060 e
11061 } else {
11062 self.parse_expression()?
11063 };
11064 Ok(Expr {
11065 kind: ExprKind::SortExpr {
11066 cmp: Some(SortComparator::Code(Box::new(code))),
11067 list: Box::new(list),
11068 },
11069 line,
11070 })
11071 } else if matches!(self.peek(), Token::Ident(ref name) if !Self::is_known_bareword(name))
11072 {
11073 let block = self.parse_block_or_bareword_cmp_block()?;
11075 let _ = self.eat(&Token::Comma);
11076 let list = if self.in_pipe_rhs()
11077 && matches!(
11078 self.peek(),
11079 Token::Semicolon
11080 | Token::RBrace
11081 | Token::RParen
11082 | Token::Eof
11083 | Token::PipeForward
11084 ) {
11085 self.pipe_placeholder_list(line)
11086 } else {
11087 self.parse_expression()?
11088 };
11089 Ok(Expr {
11090 kind: ExprKind::SortExpr {
11091 cmp: Some(SortComparator::Block(block)),
11092 list: Box::new(list),
11093 },
11094 line,
11095 })
11096 } else {
11097 let list = if self.in_pipe_rhs()
11103 && (matches!(
11104 self.peek(),
11105 Token::Semicolon
11106 | Token::RBrace
11107 | Token::RParen
11108 | Token::Eof
11109 | Token::PipeForward
11110 ) || self.peek_line() > line)
11111 {
11112 self.pipe_placeholder_list(line)
11113 } else {
11114 self.parse_expression()?
11115 };
11116 Ok(Expr {
11117 kind: ExprKind::SortExpr {
11118 cmp: None,
11119 list: Box::new(list),
11120 },
11121 line,
11122 })
11123 }
11124 }
11125 "reduce" | "fold" | "inject" => {
11126 let (block, list) = self.parse_block_list()?;
11127 Ok(Expr {
11128 kind: ExprKind::ReduceExpr {
11129 block,
11130 list: Box::new(list),
11131 },
11132 line,
11133 })
11134 }
11135 "pmap" => {
11137 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
11138 Ok(Expr {
11139 kind: ExprKind::PMapExpr {
11140 block,
11141 list: Box::new(list),
11142 progress: progress.map(Box::new),
11143 flat_outputs: false,
11144 on_cluster: None,
11145 stream: false,
11146 },
11147 line,
11148 })
11149 }
11150 "pmap_on" => {
11151 let (cluster, block, list, progress) =
11152 self.parse_cluster_block_then_list_optional_progress()?;
11153 Ok(Expr {
11154 kind: ExprKind::PMapExpr {
11155 block,
11156 list: Box::new(list),
11157 progress: progress.map(Box::new),
11158 flat_outputs: false,
11159 on_cluster: Some(Box::new(cluster)),
11160 stream: false,
11161 },
11162 line,
11163 })
11164 }
11165 "pflat_map" => {
11166 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
11167 Ok(Expr {
11168 kind: ExprKind::PMapExpr {
11169 block,
11170 list: Box::new(list),
11171 progress: progress.map(Box::new),
11172 flat_outputs: true,
11173 on_cluster: None,
11174 stream: false,
11175 },
11176 line,
11177 })
11178 }
11179 "pflat_map_on" => {
11180 let (cluster, block, list, progress) =
11181 self.parse_cluster_block_then_list_optional_progress()?;
11182 Ok(Expr {
11183 kind: ExprKind::PMapExpr {
11184 block,
11185 list: Box::new(list),
11186 progress: progress.map(Box::new),
11187 flat_outputs: true,
11188 on_cluster: Some(Box::new(cluster)),
11189 stream: false,
11190 },
11191 line,
11192 })
11193 }
11194 "pmaps" => {
11195 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
11196 Ok(Expr {
11197 kind: ExprKind::PMapExpr {
11198 block,
11199 list: Box::new(list),
11200 progress: progress.map(Box::new),
11201 flat_outputs: false,
11202 on_cluster: None,
11203 stream: true,
11204 },
11205 line,
11206 })
11207 }
11208 "pflat_maps" => {
11209 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
11210 Ok(Expr {
11211 kind: ExprKind::PMapExpr {
11212 block,
11213 list: Box::new(list),
11214 progress: progress.map(Box::new),
11215 flat_outputs: true,
11216 on_cluster: None,
11217 stream: true,
11218 },
11219 line,
11220 })
11221 }
11222 "pgreps" => {
11223 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
11224 Ok(Expr {
11225 kind: ExprKind::PGrepExpr {
11226 block,
11227 list: Box::new(list),
11228 progress: progress.map(Box::new),
11229 stream: true,
11230 },
11231 line,
11232 })
11233 }
11234 "pmap_chunked" => {
11235 let chunk_size = self.parse_assign_expr()?;
11236 let block = self.parse_block_or_bareword_block()?;
11237 self.eat(&Token::Comma);
11238 let (list, progress) = self.parse_assign_expr_list_optional_progress()?;
11239 Ok(Expr {
11240 kind: ExprKind::PMapChunkedExpr {
11241 chunk_size: Box::new(chunk_size),
11242 block,
11243 list: Box::new(list),
11244 progress: progress.map(Box::new),
11245 },
11246 line,
11247 })
11248 }
11249 "pgrep" => {
11250 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
11251 Ok(Expr {
11252 kind: ExprKind::PGrepExpr {
11253 block,
11254 list: Box::new(list),
11255 progress: progress.map(Box::new),
11256 stream: false,
11257 },
11258 line,
11259 })
11260 }
11261 "pfor" => {
11262 if matches!(self.peek(), Token::LParen) {
11263 self.expect(&Token::LParen)?;
11264 let list = self.parse_expression()?;
11265 self.expect(&Token::RParen)?;
11266 let block = self.parse_block()?;
11267 Ok(Expr {
11268 kind: ExprKind::PForExpr {
11269 block,
11270 list: Box::new(list),
11271 progress: None,
11272 },
11273 line,
11274 })
11275 } else {
11276 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
11277 Ok(Expr {
11278 kind: ExprKind::PForExpr {
11279 block,
11280 list: Box::new(list),
11281 progress: progress.map(Box::new),
11282 },
11283 line,
11284 })
11285 }
11286 }
11287 "par" => {
11293 let (block, list, _progress) = self.parse_block_then_list_optional_progress()?;
11294 Ok(Expr {
11295 kind: ExprKind::ParExpr {
11296 block,
11297 list: Box::new(list),
11298 },
11299 line,
11300 })
11301 }
11302 "par_lines" | "par_walk" => {
11303 let args = self.parse_builtin_args()?;
11304 if args.len() < 2 {
11305 return Err(
11306 self.syntax_err(format!("{} requires at least two arguments", name), line)
11307 );
11308 }
11309
11310 if name == "par_lines" {
11311 Ok(Expr {
11312 kind: ExprKind::ParLinesExpr {
11313 path: Box::new(args[0].clone()),
11314 callback: Box::new(args[1].clone()),
11315 progress: None,
11316 },
11317 line,
11318 })
11319 } else {
11320 Ok(Expr {
11321 kind: ExprKind::ParWalkExpr {
11322 path: Box::new(args[0].clone()),
11323 callback: Box::new(args[1].clone()),
11324 progress: None,
11325 },
11326 line,
11327 })
11328 }
11329 }
11330 "pwatch" | "watch" => {
11331 let args = self.parse_builtin_args()?;
11332 if args.len() < 2 {
11333 return Err(
11334 self.syntax_err(format!("{} requires at least two arguments", name), line)
11335 );
11336 }
11337 Ok(Expr {
11338 kind: ExprKind::PwatchExpr {
11339 path: Box::new(args[0].clone()),
11340 callback: Box::new(args[1].clone()),
11341 },
11342 line,
11343 })
11344 }
11345 "fan" => {
11346 let (count, block) = self.parse_fan_count_and_block(line)?;
11352 let progress = self.parse_fan_optional_progress("fan")?;
11353 Ok(Expr {
11354 kind: ExprKind::FanExpr {
11355 count,
11356 block,
11357 progress,
11358 capture: false,
11359 },
11360 line,
11361 })
11362 }
11363 "fan_cap" => {
11364 let (count, block) = self.parse_fan_count_and_block(line)?;
11365 let progress = self.parse_fan_optional_progress("fan_cap")?;
11366 Ok(Expr {
11367 kind: ExprKind::FanExpr {
11368 count,
11369 block,
11370 progress,
11371 capture: true,
11372 },
11373 line,
11374 })
11375 }
11376 "async" => {
11377 if !matches!(self.peek(), Token::LBrace) {
11378 return Err(self.syntax_err("async must be followed by { BLOCK }", line));
11379 }
11380 let block = self.parse_block()?;
11381 Ok(Expr {
11382 kind: ExprKind::AsyncBlock { body: block },
11383 line,
11384 })
11385 }
11386 "spawn" => {
11387 if !matches!(self.peek(), Token::LBrace) {
11388 return Err(self.syntax_err("spawn must be followed by { BLOCK }", line));
11389 }
11390 let block = self.parse_block()?;
11391 Ok(Expr {
11392 kind: ExprKind::SpawnBlock { body: block },
11393 line,
11394 })
11395 }
11396 "trace" => {
11397 if !matches!(self.peek(), Token::LBrace) {
11398 return Err(self.syntax_err("trace must be followed by { BLOCK }", line));
11399 }
11400 let block = self.parse_block()?;
11401 Ok(Expr {
11402 kind: ExprKind::Trace { body: block },
11403 line,
11404 })
11405 }
11406 "timer" => {
11407 let block = self.parse_block_or_bareword_block_no_args()?;
11408 Ok(Expr {
11409 kind: ExprKind::Timer { body: block },
11410 line,
11411 })
11412 }
11413 "bench" => {
11414 let block = self.parse_block_or_bareword_block_no_args()?;
11415 let times = Box::new(self.parse_expression()?);
11416 Ok(Expr {
11417 kind: ExprKind::Bench { body: block, times },
11418 line,
11419 })
11420 }
11421 "spinner" => {
11422 let (message, body) = if matches!(self.peek(), Token::LBrace) {
11424 let body = self.parse_block()?;
11425 (
11426 Box::new(Expr {
11427 kind: ExprKind::String("working".to_string()),
11428 line,
11429 }),
11430 body,
11431 )
11432 } else {
11433 let msg = self.parse_assign_expr()?;
11434 let body = self.parse_block()?;
11435 (Box::new(msg), body)
11436 };
11437 Ok(Expr {
11438 kind: ExprKind::Spinner { message, body },
11439 line,
11440 })
11441 }
11442 "thread" | "t" => {
11443 self.parse_thread_macro(line, false)
11453 }
11454 "retry" => {
11455 let body = if matches!(self.peek(), Token::LBrace) {
11458 self.parse_block()?
11459 } else {
11460 let bw_line = self.peek_line();
11461 let Token::Ident(ref name) = self.peek().clone() else {
11462 return Err(self
11463 .syntax_err("retry: expected block or bareword function name", line));
11464 };
11465 let name = name.clone();
11466 self.advance();
11467 vec![Statement::new(
11468 StmtKind::Expression(Expr {
11469 kind: ExprKind::FuncCall { name, args: vec![] },
11470 line: bw_line,
11471 }),
11472 bw_line,
11473 )]
11474 };
11475 self.eat(&Token::Comma);
11476 match self.peek() {
11477 Token::Ident(ref s) if s == "times" => {
11478 self.advance();
11479 }
11480 _ => {
11481 return Err(self.syntax_err("retry: expected `times =>` after block", line));
11482 }
11483 }
11484 self.expect(&Token::FatArrow)?;
11485 let times = Box::new(self.parse_assign_expr()?);
11486 let mut backoff = RetryBackoff::None;
11487 if self.eat(&Token::Comma) {
11488 match self.peek() {
11489 Token::Ident(ref s) if s == "backoff" => {
11490 self.advance();
11491 }
11492 _ => {
11493 return Err(
11494 self.syntax_err("retry: expected `backoff =>` after comma", line)
11495 );
11496 }
11497 }
11498 self.expect(&Token::FatArrow)?;
11499 let Token::Ident(mode) = self.peek().clone() else {
11500 return Err(self.syntax_err(
11501 "retry: expected backoff mode (none, linear, exponential)",
11502 line,
11503 ));
11504 };
11505 backoff = match mode.as_str() {
11506 "none" => RetryBackoff::None,
11507 "linear" => RetryBackoff::Linear,
11508 "exponential" => RetryBackoff::Exponential,
11509 _ => {
11510 return Err(
11511 self.syntax_err(format!("retry: invalid backoff `{mode}`"), line)
11512 );
11513 }
11514 };
11515 self.advance();
11516 }
11517 Ok(Expr {
11518 kind: ExprKind::RetryBlock {
11519 body,
11520 times,
11521 backoff,
11522 },
11523 line,
11524 })
11525 }
11526 "rate_limit" => {
11527 self.expect(&Token::LParen)?;
11528 let max = Box::new(self.parse_assign_expr()?);
11529 self.expect(&Token::Comma)?;
11530 let window = Box::new(self.parse_assign_expr()?);
11531 self.expect(&Token::RParen)?;
11532 let body = self.parse_block_or_bareword_block_no_args()?;
11533 let slot = self.alloc_rate_limit_slot();
11534 Ok(Expr {
11535 kind: ExprKind::RateLimitBlock {
11536 slot,
11537 max,
11538 window,
11539 body,
11540 },
11541 line,
11542 })
11543 }
11544 "every" => {
11545 let has_paren = self.eat(&Token::LParen);
11548 let interval = Box::new(self.parse_assign_expr()?);
11549 if has_paren {
11550 self.expect(&Token::RParen)?;
11551 }
11552 let body = if matches!(self.peek(), Token::LBrace) {
11553 self.parse_block()?
11554 } else {
11555 let bline = self.peek_line();
11556 let expr = self.parse_assign_expr()?;
11557 vec![Statement::new(StmtKind::Expression(expr), bline)]
11558 };
11559 Ok(Expr {
11560 kind: ExprKind::EveryBlock { interval, body },
11561 line,
11562 })
11563 }
11564 "gen" => {
11565 if !matches!(self.peek(), Token::LBrace) {
11566 return Err(self.syntax_err("gen must be followed by { BLOCK }", line));
11567 }
11568 let body = self.parse_block()?;
11569 Ok(Expr {
11570 kind: ExprKind::GenBlock { body },
11571 line,
11572 })
11573 }
11574 "yield" => {
11575 let e = self.parse_assign_expr()?;
11576 Ok(Expr {
11577 kind: ExprKind::Yield(Box::new(e)),
11578 line,
11579 })
11580 }
11581 "await" => {
11582 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
11583 return Ok(e);
11584 }
11585 let a = self.parse_one_arg_or_default()?;
11588 Ok(Expr {
11589 kind: ExprKind::Await(Box::new(a)),
11590 line,
11591 })
11592 }
11593 "slurp" | "cat" | "c" => {
11594 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
11595 return Ok(e);
11596 }
11597 let a = self.parse_one_arg_or_default()?;
11598 Ok(Expr {
11599 kind: ExprKind::Slurp(Box::new(a)),
11600 line,
11601 })
11602 }
11603 "capture" => {
11604 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
11605 return Ok(e);
11606 }
11607 let a = self.parse_one_arg()?;
11608 Ok(Expr {
11609 kind: ExprKind::Capture(Box::new(a)),
11610 line,
11611 })
11612 }
11613 "fetch_url" => {
11614 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
11615 return Ok(e);
11616 }
11617 let a = self.parse_one_arg()?;
11618 Ok(Expr {
11619 kind: ExprKind::FetchUrl(Box::new(a)),
11620 line,
11621 })
11622 }
11623 "pchannel" => {
11624 let capacity = if self.eat(&Token::LParen) {
11625 if matches!(self.peek(), Token::RParen) {
11626 self.advance();
11627 None
11628 } else {
11629 let e = self.parse_expression()?;
11630 self.expect(&Token::RParen)?;
11631 Some(Box::new(e))
11632 }
11633 } else {
11634 None
11635 };
11636 Ok(Expr {
11637 kind: ExprKind::Pchannel { capacity },
11638 line,
11639 })
11640 }
11641 "psort" => {
11642 if matches!(self.peek(), Token::LBrace)
11643 || matches!(self.peek(), Token::ScalarVar(ref v) if v == "a" || v == "b")
11644 || matches!(self.peek(), Token::Ident(ref name) if !Self::is_known_bareword(name))
11645 {
11646 let block = self.parse_block_or_bareword_cmp_block()?;
11647 let block_end_line = self.prev_line();
11655 self.eat(&Token::Comma);
11656 let use_placeholder = self.in_pipe_rhs()
11657 && (matches!(
11658 self.peek(),
11659 Token::Semicolon
11660 | Token::RBrace
11661 | Token::RParen
11662 | Token::Eof
11663 | Token::PipeForward
11664 ) || self.peek_line() > block_end_line);
11665 let (list, progress) = if use_placeholder {
11666 (self.pipe_placeholder_list(line), None)
11667 } else {
11668 self.parse_assign_expr_list_optional_progress()?
11669 };
11670 Ok(Expr {
11671 kind: ExprKind::PSortExpr {
11672 cmp: Some(block),
11673 list: Box::new(list),
11674 progress: progress.map(Box::new),
11675 },
11676 line,
11677 })
11678 } else {
11679 let (list, progress) = self.parse_assign_expr_list_optional_progress()?;
11680 Ok(Expr {
11681 kind: ExprKind::PSortExpr {
11682 cmp: None,
11683 list: Box::new(list),
11684 progress: progress.map(Box::new),
11685 },
11686 line,
11687 })
11688 }
11689 }
11690 "preduce" => {
11691 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
11692 Ok(Expr {
11693 kind: ExprKind::PReduceExpr {
11694 block,
11695 list: Box::new(list),
11696 progress: progress.map(Box::new),
11697 },
11698 line,
11699 })
11700 }
11701 "preduce_init" => {
11702 let (init, block, list, progress) =
11703 self.parse_init_block_then_list_optional_progress()?;
11704 Ok(Expr {
11705 kind: ExprKind::PReduceInitExpr {
11706 init: Box::new(init),
11707 block,
11708 list: Box::new(list),
11709 progress: progress.map(Box::new),
11710 },
11711 line,
11712 })
11713 }
11714 "pmap_reduce" => {
11715 let map_block = self.parse_block_or_bareword_block()?;
11716 let reduce_block = if matches!(self.peek(), Token::LBrace) {
11719 self.parse_block()?
11720 } else {
11721 self.expect(&Token::Comma)?;
11723 self.parse_block_or_bareword_cmp_block()?
11724 };
11725 self.eat(&Token::Comma);
11726 let line = self.peek_line();
11727 if let Token::Ident(ref kw) = self.peek().clone() {
11728 if kw == "progress" && matches!(self.peek_at(1), Token::FatArrow) {
11729 self.advance();
11730 self.expect(&Token::FatArrow)?;
11731 let prog = self.parse_assign_expr()?;
11732 return Ok(Expr {
11733 kind: ExprKind::PMapReduceExpr {
11734 map_block,
11735 reduce_block,
11736 list: Box::new(Expr {
11737 kind: ExprKind::List(vec![]),
11738 line,
11739 }),
11740 progress: Some(Box::new(prog)),
11741 },
11742 line,
11743 });
11744 }
11745 }
11746 if matches!(
11747 self.peek(),
11748 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof
11749 ) {
11750 return Ok(Expr {
11751 kind: ExprKind::PMapReduceExpr {
11752 map_block,
11753 reduce_block,
11754 list: Box::new(Expr {
11755 kind: ExprKind::List(vec![]),
11756 line,
11757 }),
11758 progress: None,
11759 },
11760 line,
11761 });
11762 }
11763 let mut parts = vec![self.parse_assign_expr()?];
11764 loop {
11765 if !self.eat(&Token::Comma) && !self.eat(&Token::FatArrow) {
11766 break;
11767 }
11768 if matches!(
11769 self.peek(),
11770 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof
11771 ) {
11772 break;
11773 }
11774 if let Token::Ident(ref kw) = self.peek().clone() {
11775 if kw == "progress" && matches!(self.peek_at(1), Token::FatArrow) {
11776 self.advance();
11777 self.expect(&Token::FatArrow)?;
11778 let prog = self.parse_assign_expr()?;
11779 return Ok(Expr {
11780 kind: ExprKind::PMapReduceExpr {
11781 map_block,
11782 reduce_block,
11783 list: Box::new(merge_expr_list(parts)),
11784 progress: Some(Box::new(prog)),
11785 },
11786 line,
11787 });
11788 }
11789 }
11790 parts.push(self.parse_assign_expr()?);
11791 }
11792 Ok(Expr {
11793 kind: ExprKind::PMapReduceExpr {
11794 map_block,
11795 reduce_block,
11796 list: Box::new(merge_expr_list(parts)),
11797 progress: None,
11798 },
11799 line,
11800 })
11801 }
11802 "puniq" => {
11803 if self.pipe_supplies_slurped_list_operand() {
11804 return Ok(Expr {
11805 kind: ExprKind::FuncCall {
11806 name: "puniq".to_string(),
11807 args: vec![],
11808 },
11809 line,
11810 });
11811 }
11812 let (list, progress) = self.parse_assign_expr_list_optional_progress()?;
11813 let mut args = vec![list];
11814 if let Some(p) = progress {
11815 args.push(p);
11816 }
11817 Ok(Expr {
11818 kind: ExprKind::FuncCall {
11819 name: "puniq".to_string(),
11820 args,
11821 },
11822 line,
11823 })
11824 }
11825 "pfirst" => {
11826 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
11827 let cr = Expr {
11828 kind: ExprKind::CodeRef {
11829 params: vec![],
11830 body: block,
11831 },
11832 line,
11833 };
11834 let mut args = vec![cr, list];
11835 if let Some(p) = progress {
11836 args.push(p);
11837 }
11838 Ok(Expr {
11839 kind: ExprKind::FuncCall {
11840 name: "pfirst".to_string(),
11841 args,
11842 },
11843 line,
11844 })
11845 }
11846 "pany" => {
11847 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
11848 let cr = Expr {
11849 kind: ExprKind::CodeRef {
11850 params: vec![],
11851 body: block,
11852 },
11853 line,
11854 };
11855 let mut args = vec![cr, list];
11856 if let Some(p) = progress {
11857 args.push(p);
11858 }
11859 Ok(Expr {
11860 kind: ExprKind::FuncCall {
11861 name: "pany".to_string(),
11862 args,
11863 },
11864 line,
11865 })
11866 }
11867 "uniq" | "distinct" => {
11868 if self.pipe_supplies_slurped_list_operand() {
11869 return Ok(Expr {
11870 kind: ExprKind::FuncCall {
11871 name: name.clone(),
11872 args: vec![],
11873 },
11874 line,
11875 });
11876 }
11877 let (list, progress) = self.parse_assign_expr_list_optional_progress()?;
11878 if progress.is_some() {
11879 return Err(self.syntax_err(
11880 "`progress =>` is not supported for uniq (use puniq for parallel + progress)",
11881 line,
11882 ));
11883 }
11884 Ok(Expr {
11885 kind: ExprKind::FuncCall {
11886 name: name.clone(),
11887 args: vec![list],
11888 },
11889 line,
11890 })
11891 }
11892 "flatten" => {
11893 if self.pipe_supplies_slurped_list_operand() {
11894 return Ok(Expr {
11895 kind: ExprKind::FuncCall {
11896 name: "flatten".to_string(),
11897 args: vec![],
11898 },
11899 line,
11900 });
11901 }
11902 let (list, progress) = self.parse_assign_expr_list_optional_progress()?;
11903 if progress.is_some() {
11904 return Err(self.syntax_err("`progress =>` is not supported for flatten", line));
11905 }
11906 Ok(Expr {
11907 kind: ExprKind::FuncCall {
11908 name: "flatten".to_string(),
11909 args: vec![list],
11910 },
11911 line,
11912 })
11913 }
11914 "set" => {
11915 if self.pipe_supplies_slurped_list_operand() {
11916 return Ok(Expr {
11917 kind: ExprKind::FuncCall {
11918 name: "set".to_string(),
11919 args: vec![],
11920 },
11921 line,
11922 });
11923 }
11924 let (list, progress) = self.parse_assign_expr_list_optional_progress()?;
11925 if progress.is_some() {
11926 return Err(self.syntax_err("`progress =>` is not supported for set", line));
11927 }
11928 Ok(Expr {
11929 kind: ExprKind::FuncCall {
11930 name: "set".to_string(),
11931 args: vec![list],
11932 },
11933 line,
11934 })
11935 }
11936 "size" => {
11940 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
11941 return Ok(e);
11942 }
11943 if self.pipe_supplies_slurped_list_operand() {
11944 return Ok(Expr {
11945 kind: ExprKind::FuncCall {
11946 name: "size".to_string(),
11947 args: vec![],
11948 },
11949 line,
11950 });
11951 }
11952 let a = self.parse_one_arg_or_default()?;
11953 Ok(Expr {
11954 kind: ExprKind::FuncCall {
11955 name: "size".to_string(),
11956 args: vec![a],
11957 },
11958 line,
11959 })
11960 }
11961 "list_count" | "list_size" | "count" | "len" | "cnt" => {
11962 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
11963 return Ok(e);
11964 }
11965 if self.pipe_supplies_slurped_list_operand() {
11966 return Ok(Expr {
11967 kind: ExprKind::FuncCall {
11968 name: name.clone(),
11969 args: vec![],
11970 },
11971 line,
11972 });
11973 }
11974 let args = if matches!(self.peek(), Token::LParen) {
11988 self.advance();
11989 if matches!(self.peek(), Token::RParen) {
11990 self.advance();
11991 Vec::new()
11992 } else {
11993 let inner = self.parse_expression()?;
11994 self.expect(&Token::RParen)?;
11995 vec![inner]
11996 }
11997 } else if self.peek_is_named_unary_terminator() {
11998 Vec::new()
11999 } else {
12000 let (list, progress) = self.parse_assign_expr_list_optional_progress()?;
12001 if progress.is_some() {
12002 return Err(self.syntax_err(
12003 "`progress =>` is not supported for list_count / list_size / count / cnt",
12004 line,
12005 ));
12006 }
12007 vec![list]
12008 };
12009 Ok(Expr {
12010 kind: ExprKind::FuncCall {
12011 name: name.clone(),
12012 args,
12013 },
12014 line,
12015 })
12016 }
12017 "shuffle" | "shuffled" => {
12018 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12019 return Ok(e);
12020 }
12021 if self.pipe_supplies_slurped_list_operand() {
12022 return Ok(Expr {
12023 kind: ExprKind::FuncCall {
12024 name: "shuffle".to_string(),
12025 args: vec![],
12026 },
12027 line,
12028 });
12029 }
12030 let (list, progress) = self.parse_assign_expr_list_optional_progress()?;
12031 if progress.is_some() {
12032 return Err(self.syntax_err("`progress =>` is not supported for shuffle", line));
12033 }
12034 Ok(Expr {
12035 kind: ExprKind::FuncCall {
12036 name: "shuffle".to_string(),
12037 args: vec![list],
12038 },
12039 line,
12040 })
12041 }
12042 "chunked" => {
12043 let mut parts = Vec::new();
12044 if self.eat(&Token::LParen) {
12045 if !matches!(self.peek(), Token::RParen) {
12046 parts.push(self.parse_assign_expr()?);
12047 while self.eat(&Token::Comma) {
12048 if matches!(self.peek(), Token::RParen) {
12049 break;
12050 }
12051 parts.push(self.parse_assign_expr()?);
12052 }
12053 }
12054 self.expect(&Token::RParen)?;
12055 } else {
12056 parts.push(self.parse_assign_expr_stop_at_pipe()?);
12060 loop {
12061 if !self.eat(&Token::Comma) && !self.eat(&Token::FatArrow) {
12062 break;
12063 }
12064 if matches!(
12065 self.peek(),
12066 Token::Semicolon
12067 | Token::RBrace
12068 | Token::RParen
12069 | Token::Eof
12070 | Token::PipeForward
12071 ) {
12072 break;
12073 }
12074 if self.peek_is_postfix_stmt_modifier_keyword() {
12075 break;
12076 }
12077 parts.push(self.parse_assign_expr_stop_at_pipe()?);
12078 }
12079 }
12080 if parts.len() == 1 {
12081 let n = parts.pop().unwrap();
12082 return Ok(Expr {
12083 kind: ExprKind::FuncCall {
12084 name: "chunked".to_string(),
12085 args: vec![n],
12086 },
12087 line,
12088 });
12089 }
12090 if parts.is_empty() {
12091 return Ok(Expr {
12092 kind: ExprKind::FuncCall {
12093 name: "chunked".to_string(),
12094 args: parts,
12095 },
12096 line,
12097 });
12098 }
12099 if parts.len() == 2 {
12100 let n = parts.pop().unwrap();
12101 let list = parts.pop().unwrap();
12102 return Ok(Expr {
12103 kind: ExprKind::FuncCall {
12104 name: "chunked".to_string(),
12105 args: vec![list, n],
12106 },
12107 line,
12108 });
12109 }
12110 Err(self.syntax_err(
12111 "chunked: use LIST |> chunked(N) or chunked((1,2,3), 2)",
12112 line,
12113 ))
12114 }
12115 "windowed" => {
12116 let mut parts = Vec::new();
12117 if self.eat(&Token::LParen) {
12118 if !matches!(self.peek(), Token::RParen) {
12119 parts.push(self.parse_assign_expr()?);
12120 while self.eat(&Token::Comma) {
12121 if matches!(self.peek(), Token::RParen) {
12122 break;
12123 }
12124 parts.push(self.parse_assign_expr()?);
12125 }
12126 }
12127 self.expect(&Token::RParen)?;
12128 } else {
12129 parts.push(self.parse_assign_expr_stop_at_pipe()?);
12132 loop {
12133 if !self.eat(&Token::Comma) && !self.eat(&Token::FatArrow) {
12134 break;
12135 }
12136 if matches!(
12137 self.peek(),
12138 Token::Semicolon
12139 | Token::RBrace
12140 | Token::RParen
12141 | Token::Eof
12142 | Token::PipeForward
12143 ) {
12144 break;
12145 }
12146 if self.peek_is_postfix_stmt_modifier_keyword() {
12147 break;
12148 }
12149 parts.push(self.parse_assign_expr_stop_at_pipe()?);
12150 }
12151 }
12152 if parts.len() == 1 {
12153 let n = parts.pop().unwrap();
12154 return Ok(Expr {
12155 kind: ExprKind::FuncCall {
12156 name: "windowed".to_string(),
12157 args: vec![n],
12158 },
12159 line,
12160 });
12161 }
12162 if parts.is_empty() {
12163 return Ok(Expr {
12164 kind: ExprKind::FuncCall {
12165 name: "windowed".to_string(),
12166 args: parts,
12167 },
12168 line,
12169 });
12170 }
12171 if parts.len() == 2 {
12172 let n = parts.pop().unwrap();
12173 let list = parts.pop().unwrap();
12174 return Ok(Expr {
12175 kind: ExprKind::FuncCall {
12176 name: "windowed".to_string(),
12177 args: vec![list, n],
12178 },
12179 line,
12180 });
12181 }
12182 Err(self.syntax_err(
12183 "windowed: use LIST |> windowed(N) or windowed((1,2,3), 2)",
12184 line,
12185 ))
12186 }
12187 "any" | "all" | "none" => {
12188 if matches!(self.peek(), Token::LParen) {
12190 self.advance();
12191 let args = self.parse_arg_list()?;
12192 self.expect(&Token::RParen)?;
12193 return Ok(Expr {
12194 kind: ExprKind::FuncCall {
12195 name: name.clone(),
12196 args,
12197 },
12198 line,
12199 });
12200 }
12201 if let Some(args) = self.try_parse_coderef_listop_args(line)? {
12205 return Ok(Expr {
12206 kind: ExprKind::FuncCall {
12207 name: name.clone(),
12208 args,
12209 },
12210 line,
12211 });
12212 }
12213 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
12215 if progress.is_some() {
12216 return Err(self.syntax_err(
12217 "`progress =>` is not supported for any/all/none (use pany for parallel + progress)",
12218 line,
12219 ));
12220 }
12221 let cr = Expr {
12222 kind: ExprKind::CodeRef {
12223 params: vec![],
12224 body: block,
12225 },
12226 line,
12227 };
12228 Ok(Expr {
12229 kind: ExprKind::FuncCall {
12230 name: name.clone(),
12231 args: vec![cr, list],
12232 },
12233 line,
12234 })
12235 }
12236 "first" | "detect" | "find" | "find_index" | "firstidx" | "first_index" => {
12238 let canonical =
12239 if matches!(name.as_str(), "find_index" | "firstidx" | "first_index") {
12240 "find_index"
12241 } else {
12242 "first"
12243 };
12244 if matches!(self.peek(), Token::LParen) {
12246 self.advance();
12247 let args = self.parse_arg_list()?;
12248 self.expect(&Token::RParen)?;
12249 return Ok(Expr {
12250 kind: ExprKind::FuncCall {
12251 name: canonical.to_string(),
12252 args,
12253 },
12254 line,
12255 });
12256 }
12257 if let Some(args) = self.try_parse_coderef_listop_args(line)? {
12259 return Ok(Expr {
12260 kind: ExprKind::FuncCall {
12261 name: canonical.to_string(),
12262 args,
12263 },
12264 line,
12265 });
12266 }
12267 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
12269 if progress.is_some() {
12270 return Err(self.syntax_err(
12271 "`progress =>` is not supported for first/detect/find/find_index (use pfirst for parallel + progress)",
12272 line,
12273 ));
12274 }
12275 let cr = Expr {
12276 kind: ExprKind::CodeRef {
12277 params: vec![],
12278 body: block,
12279 },
12280 line,
12281 };
12282 Ok(Expr {
12283 kind: ExprKind::FuncCall {
12284 name: canonical.to_string(),
12285 args: vec![cr, list],
12286 },
12287 line,
12288 })
12289 }
12290 "take_while" | "drop_while" | "skip_while" | "reject" | "grepv" | "tap" | "peek"
12291 | "partition" | "min_by" | "max_by" | "zip_with" | "count_by" => {
12292 if let Some(args) = self.try_parse_coderef_listop_args(line)? {
12294 return Ok(Expr {
12295 kind: ExprKind::FuncCall {
12296 name: name.to_string(),
12297 args,
12298 },
12299 line,
12300 });
12301 }
12302 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
12303 if progress.is_some() {
12304 return Err(
12305 self.syntax_err(format!("`progress =>` is not supported for {name}"), line)
12306 );
12307 }
12308 let cr = Expr {
12309 kind: ExprKind::CodeRef {
12310 params: vec![],
12311 body: block,
12312 },
12313 line,
12314 };
12315 Ok(Expr {
12316 kind: ExprKind::FuncCall {
12317 name: name.to_string(),
12318 args: vec![cr, list],
12319 },
12320 line,
12321 })
12322 }
12323 "group_by" | "chunk_by" => {
12324 if matches!(self.peek(), Token::LBrace) {
12325 let (block, list) = self.parse_block_list()?;
12326 let cr = Expr {
12327 kind: ExprKind::CodeRef {
12328 params: vec![],
12329 body: block,
12330 },
12331 line,
12332 };
12333 Ok(Expr {
12334 kind: ExprKind::FuncCall {
12335 name: name.to_string(),
12336 args: vec![cr, list],
12337 },
12338 line,
12339 })
12340 } else {
12341 let key_expr = self.parse_assign_expr()?;
12342 self.expect(&Token::Comma)?;
12343 let list_parts = self.parse_list_until_terminator()?;
12344 let list_expr = if list_parts.len() == 1 {
12345 list_parts.into_iter().next().unwrap()
12346 } else {
12347 Expr {
12348 kind: ExprKind::List(list_parts),
12349 line,
12350 }
12351 };
12352 Ok(Expr {
12353 kind: ExprKind::FuncCall {
12354 name: name.to_string(),
12355 args: vec![key_expr, list_expr],
12356 },
12357 line,
12358 })
12359 }
12360 }
12361 "with_index" => {
12362 if self.pipe_supplies_slurped_list_operand() {
12363 return Ok(Expr {
12364 kind: ExprKind::FuncCall {
12365 name: "with_index".to_string(),
12366 args: vec![],
12367 },
12368 line,
12369 });
12370 }
12371 let (list, progress) = self.parse_assign_expr_list_optional_progress()?;
12372 if progress.is_some() {
12373 return Err(
12374 self.syntax_err("`progress =>` is not supported for with_index", line)
12375 );
12376 }
12377 Ok(Expr {
12378 kind: ExprKind::FuncCall {
12379 name: "with_index".to_string(),
12380 args: vec![list],
12381 },
12382 line,
12383 })
12384 }
12385 "pcache" => {
12386 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
12387 Ok(Expr {
12388 kind: ExprKind::PcacheExpr {
12389 block,
12390 list: Box::new(list),
12391 progress: progress.map(Box::new),
12392 },
12393 line,
12394 })
12395 }
12396 "pselect" => {
12397 let paren = self.eat(&Token::LParen);
12398 let (receivers, timeout) = self.parse_comma_expr_list_with_timeout_tail(paren)?;
12399 if paren {
12400 self.expect(&Token::RParen)?;
12401 }
12402 if receivers.is_empty() {
12403 return Err(self.syntax_err("pselect needs at least one receiver", line));
12404 }
12405 Ok(Expr {
12406 kind: ExprKind::PselectExpr {
12407 receivers,
12408 timeout: timeout.map(Box::new),
12409 },
12410 line,
12411 })
12412 }
12413 "open" => {
12414 let paren = matches!(self.peek(), Token::LParen);
12415 if paren {
12416 self.advance();
12417 }
12418 if matches!(self.peek(), Token::Ident(ref s) if s == "my") {
12419 self.advance();
12420 let name = self.parse_scalar_var_name()?;
12421 self.expect(&Token::Comma)?;
12422 let mode = self.parse_assign_expr()?;
12423 let file = if self.eat(&Token::Comma) {
12424 Some(self.parse_assign_expr()?)
12425 } else {
12426 None
12427 };
12428 if paren {
12429 self.expect(&Token::RParen)?;
12430 }
12431 Ok(Expr {
12432 kind: ExprKind::Open {
12433 handle: Box::new(Expr {
12434 kind: ExprKind::OpenMyHandle { name },
12435 line,
12436 }),
12437 mode: Box::new(mode),
12438 file: file.map(Box::new),
12439 },
12440 line,
12441 })
12442 } else {
12443 let handle_lit = self.take_bareword_filehandle();
12451 if handle_lit.is_some() {
12452 self.expect(&Token::Comma)?;
12455 }
12456 let args = if paren {
12457 self.parse_arg_list()?
12458 } else {
12459 self.parse_list_until_terminator()?
12460 };
12461 if paren {
12462 self.expect(&Token::RParen)?;
12463 }
12464 let total = handle_lit.is_some() as usize + args.len();
12465 if total < 2 {
12466 return Err(self.syntax_err("open requires at least 2 arguments", line));
12467 }
12468 let (handle_expr, mode_expr, file_expr) = match handle_lit {
12469 Some(name) => {
12470 let h = Expr {
12471 kind: ExprKind::String(name),
12472 line,
12473 };
12474 (h, args[0].clone(), args.get(1).cloned())
12475 }
12476 None => (args[0].clone(), args[1].clone(), args.get(2).cloned()),
12477 };
12478 Ok(Expr {
12479 kind: ExprKind::Open {
12480 handle: Box::new(handle_expr),
12481 mode: Box::new(mode_expr),
12482 file: file_expr.map(Box::new),
12483 },
12484 line,
12485 })
12486 }
12487 }
12488 "close" => {
12489 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12490 return Ok(e);
12491 }
12492 let a = self
12494 .take_bareword_filehandle_arg(line)
12495 .map(Ok)
12496 .unwrap_or_else(|| self.parse_one_arg_or_default())?;
12497 Ok(Expr {
12498 kind: ExprKind::Close(Box::new(a)),
12499 line,
12500 })
12501 }
12502 "opendir" => {
12503 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12504 return Ok(e);
12505 }
12506 let args = self.parse_builtin_args()?;
12507 if args.len() != 2 {
12508 return Err(self.syntax_err("opendir requires two arguments", line));
12509 }
12510 Ok(Expr {
12511 kind: ExprKind::Opendir {
12512 handle: Box::new(args[0].clone()),
12513 path: Box::new(args[1].clone()),
12514 },
12515 line,
12516 })
12517 }
12518 "readdir" => {
12519 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12520 return Ok(e);
12521 }
12522 let a = self.parse_one_arg()?;
12523 Ok(Expr {
12524 kind: ExprKind::Readdir(Box::new(a)),
12525 line,
12526 })
12527 }
12528 "closedir" => {
12529 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12530 return Ok(e);
12531 }
12532 let a = self.parse_one_arg()?;
12533 Ok(Expr {
12534 kind: ExprKind::Closedir(Box::new(a)),
12535 line,
12536 })
12537 }
12538 "rewinddir" => {
12539 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12540 return Ok(e);
12541 }
12542 let a = self.parse_one_arg()?;
12543 Ok(Expr {
12544 kind: ExprKind::Rewinddir(Box::new(a)),
12545 line,
12546 })
12547 }
12548 "telldir" => {
12549 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12550 return Ok(e);
12551 }
12552 let a = self.parse_one_arg()?;
12553 Ok(Expr {
12554 kind: ExprKind::Telldir(Box::new(a)),
12555 line,
12556 })
12557 }
12558 "seekdir" => {
12559 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12560 return Ok(e);
12561 }
12562 let args = self.parse_builtin_args()?;
12563 if args.len() != 2 {
12564 return Err(self.syntax_err("seekdir requires two arguments", line));
12565 }
12566 Ok(Expr {
12567 kind: ExprKind::Seekdir {
12568 handle: Box::new(args[0].clone()),
12569 position: Box::new(args[1].clone()),
12570 },
12571 line,
12572 })
12573 }
12574 "eof" => {
12575 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12576 return Ok(e);
12577 }
12578 if let Some(a) = self.take_bareword_filehandle_arg(line) {
12582 return Ok(Expr {
12583 kind: ExprKind::Eof(Some(Box::new(a))),
12584 line,
12585 });
12586 }
12587 if matches!(self.peek(), Token::LParen) {
12588 self.advance();
12589 if matches!(self.peek(), Token::RParen) {
12590 self.advance();
12591 Ok(Expr {
12592 kind: ExprKind::Eof(None),
12593 line,
12594 })
12595 } else {
12596 let a = self
12598 .take_bareword_filehandle_arg(line)
12599 .map(Ok)
12600 .unwrap_or_else(|| self.parse_expression())?;
12601 self.expect(&Token::RParen)?;
12602 Ok(Expr {
12603 kind: ExprKind::Eof(Some(Box::new(a))),
12604 line,
12605 })
12606 }
12607 } else {
12608 Ok(Expr {
12609 kind: ExprKind::Eof(None),
12610 line,
12611 })
12612 }
12613 }
12614 "system" => {
12615 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12616 return Ok(e);
12617 }
12618 let args = self.parse_builtin_args()?;
12619 Ok(Expr {
12620 kind: ExprKind::System(args),
12621 line,
12622 })
12623 }
12624 "exec" => {
12625 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12626 return Ok(e);
12627 }
12628 let args = self.parse_builtin_args()?;
12629 Ok(Expr {
12630 kind: ExprKind::Exec(args),
12631 line,
12632 })
12633 }
12634 "eval" => {
12635 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12636 return Ok(e);
12637 }
12638 let a = if matches!(self.peek(), Token::LBrace) {
12639 let block = self.parse_block()?;
12640 Expr {
12641 kind: ExprKind::CodeRef {
12642 params: vec![],
12643 body: block,
12644 },
12645 line,
12646 }
12647 } else {
12648 self.parse_one_arg_or_default()?
12649 };
12650 Ok(Expr {
12651 kind: ExprKind::Eval(Box::new(a)),
12652 line,
12653 })
12654 }
12655 "do" => {
12656 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12657 return Ok(e);
12658 }
12659 let a = self.parse_one_arg()?;
12660 Ok(Expr {
12661 kind: ExprKind::Do(Box::new(a)),
12662 line,
12663 })
12664 }
12665 "require" => {
12666 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12667 return Ok(e);
12668 }
12669 let a = self.parse_one_arg()?;
12670 Ok(Expr {
12671 kind: ExprKind::Require(Box::new(a)),
12672 line,
12673 })
12674 }
12675 "exit" => {
12676 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12677 return Ok(e);
12678 }
12679 if matches!(
12680 self.peek(),
12681 Token::Semicolon | Token::RBrace | Token::Eof | Token::PipeForward
12682 ) {
12683 Ok(Expr {
12684 kind: ExprKind::Exit(None),
12685 line,
12686 })
12687 } else {
12688 let a = self.parse_one_arg()?;
12689 Ok(Expr {
12690 kind: ExprKind::Exit(Some(Box::new(a))),
12691 line,
12692 })
12693 }
12694 }
12695 "chdir" => {
12696 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12697 return Ok(e);
12698 }
12699 let a = self.parse_one_arg_or_default()?;
12700 Ok(Expr {
12701 kind: ExprKind::Chdir(Box::new(a)),
12702 line,
12703 })
12704 }
12705 "mkdir" => {
12706 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12707 return Ok(e);
12708 }
12709 let args = self.parse_builtin_args()?;
12710 Ok(Expr {
12711 kind: ExprKind::Mkdir {
12712 path: Box::new(args[0].clone()),
12713 mode: args.get(1).cloned().map(Box::new),
12714 },
12715 line,
12716 })
12717 }
12718 "unlink" | "rm" => {
12719 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12720 return Ok(e);
12721 }
12722 let args = self.parse_builtin_args()?;
12723 Ok(Expr {
12724 kind: ExprKind::Unlink(args),
12725 line,
12726 })
12727 }
12728 "rename" => {
12729 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12730 return Ok(e);
12731 }
12732 let args = self.parse_builtin_args()?;
12733 if args.len() != 2 {
12734 return Err(self.syntax_err("rename requires two arguments", line));
12735 }
12736 Ok(Expr {
12737 kind: ExprKind::Rename {
12738 old: Box::new(args[0].clone()),
12739 new: Box::new(args[1].clone()),
12740 },
12741 line,
12742 })
12743 }
12744 "chmod" => {
12745 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12746 return Ok(e);
12747 }
12748 let args = self.parse_builtin_args()?;
12749 if args.len() < 2 {
12750 return Err(self.syntax_err("chmod requires mode and at least one file", line));
12751 }
12752 Ok(Expr {
12753 kind: ExprKind::Chmod(args),
12754 line,
12755 })
12756 }
12757 "chown" => {
12758 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12759 return Ok(e);
12760 }
12761 let args = self.parse_builtin_args()?;
12762 if args.len() < 3 {
12763 return Err(
12764 self.syntax_err("chown requires uid, gid, and at least one file", line)
12765 );
12766 }
12767 Ok(Expr {
12768 kind: ExprKind::Chown(args),
12769 line,
12770 })
12771 }
12772 "stat" => {
12773 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12774 return Ok(e);
12775 }
12776 let args = self.parse_builtin_args()?;
12777 let arg = if args.len() == 1 {
12778 args[0].clone()
12779 } else if args.is_empty() {
12780 Expr {
12781 kind: ExprKind::ScalarVar("_".into()),
12782 line,
12783 }
12784 } else {
12785 return Err(self.syntax_err("stat requires zero or one argument", line));
12786 };
12787 Ok(Expr {
12788 kind: ExprKind::Stat(Box::new(arg)),
12789 line,
12790 })
12791 }
12792 "lstat" => {
12793 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12794 return Ok(e);
12795 }
12796 let args = self.parse_builtin_args()?;
12797 let arg = if args.len() == 1 {
12798 args[0].clone()
12799 } else if args.is_empty() {
12800 Expr {
12801 kind: ExprKind::ScalarVar("_".into()),
12802 line,
12803 }
12804 } else {
12805 return Err(self.syntax_err("lstat requires zero or one argument", line));
12806 };
12807 Ok(Expr {
12808 kind: ExprKind::Lstat(Box::new(arg)),
12809 line,
12810 })
12811 }
12812 "link" => {
12813 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12814 return Ok(e);
12815 }
12816 let args = self.parse_builtin_args()?;
12817 if args.len() != 2 {
12818 return Err(self.syntax_err("link requires two arguments", line));
12819 }
12820 Ok(Expr {
12821 kind: ExprKind::Link {
12822 old: Box::new(args[0].clone()),
12823 new: Box::new(args[1].clone()),
12824 },
12825 line,
12826 })
12827 }
12828 "symlink" => {
12829 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12830 return Ok(e);
12831 }
12832 let args = self.parse_builtin_args()?;
12833 if args.len() != 2 {
12834 return Err(self.syntax_err("symlink requires two arguments", line));
12835 }
12836 Ok(Expr {
12837 kind: ExprKind::Symlink {
12838 old: Box::new(args[0].clone()),
12839 new: Box::new(args[1].clone()),
12840 },
12841 line,
12842 })
12843 }
12844 "readlink" => {
12845 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12846 return Ok(e);
12847 }
12848 let args = self.parse_builtin_args()?;
12849 let arg = if args.len() == 1 {
12850 args[0].clone()
12851 } else if args.is_empty() {
12852 Expr {
12853 kind: ExprKind::ScalarVar("_".into()),
12854 line,
12855 }
12856 } else {
12857 return Err(self.syntax_err("readlink requires zero or one argument", line));
12858 };
12859 Ok(Expr {
12860 kind: ExprKind::Readlink(Box::new(arg)),
12861 line,
12862 })
12863 }
12864 "files" => {
12865 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12866 return Ok(e);
12867 }
12868 let args = self.parse_builtin_args()?;
12869 Ok(Expr {
12870 kind: ExprKind::Files(args),
12871 line,
12872 })
12873 }
12874 "filesf" | "f" => {
12875 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12876 return Ok(e);
12877 }
12878 let args = self.parse_builtin_args()?;
12879 Ok(Expr {
12880 kind: ExprKind::Filesf(args),
12881 line,
12882 })
12883 }
12884 "fr" => {
12885 let args = self.parse_builtin_args()?;
12886 Ok(Expr {
12887 kind: ExprKind::FilesfRecursive(args),
12888 line,
12889 })
12890 }
12891 "dirs" | "d" => {
12892 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12893 return Ok(e);
12894 }
12895 let args = self.parse_builtin_args()?;
12896 Ok(Expr {
12897 kind: ExprKind::Dirs(args),
12898 line,
12899 })
12900 }
12901 "dr" => {
12902 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12903 return Ok(e);
12904 }
12905 let args = self.parse_builtin_args()?;
12906 Ok(Expr {
12907 kind: ExprKind::DirsRecursive(args),
12908 line,
12909 })
12910 }
12911 "sym_links" => {
12912 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12913 return Ok(e);
12914 }
12915 let args = self.parse_builtin_args()?;
12916 Ok(Expr {
12917 kind: ExprKind::SymLinks(args),
12918 line,
12919 })
12920 }
12921 "sockets" => {
12922 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12923 return Ok(e);
12924 }
12925 let args = self.parse_builtin_args()?;
12926 Ok(Expr {
12927 kind: ExprKind::Sockets(args),
12928 line,
12929 })
12930 }
12931 "pipes" => {
12932 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12933 return Ok(e);
12934 }
12935 let args = self.parse_builtin_args()?;
12936 Ok(Expr {
12937 kind: ExprKind::Pipes(args),
12938 line,
12939 })
12940 }
12941 "block_devices" => {
12942 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12943 return Ok(e);
12944 }
12945 let args = self.parse_builtin_args()?;
12946 Ok(Expr {
12947 kind: ExprKind::BlockDevices(args),
12948 line,
12949 })
12950 }
12951 "char_devices" => {
12952 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12953 return Ok(e);
12954 }
12955 let args = self.parse_builtin_args()?;
12956 Ok(Expr {
12957 kind: ExprKind::CharDevices(args),
12958 line,
12959 })
12960 }
12961 "exe" | "executables" => {
12962 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12963 return Ok(e);
12964 }
12965 let args = self.parse_builtin_args()?;
12966 Ok(Expr {
12967 kind: ExprKind::Executables(args),
12968 line,
12969 })
12970 }
12971 "glob" => {
12972 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12973 return Ok(e);
12974 }
12975 let args = self.parse_builtin_args()?;
12976 Ok(Expr {
12977 kind: ExprKind::Glob(args),
12978 line,
12979 })
12980 }
12981 "glob_par" => {
12982 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12983 return Ok(e);
12984 }
12985 let (args, progress) = self.parse_glob_par_or_par_sed_args()?;
12986 Ok(Expr {
12987 kind: ExprKind::GlobPar { args, progress },
12988 line,
12989 })
12990 }
12991 "par_sed" => {
12992 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12993 return Ok(e);
12994 }
12995 let (args, progress) = self.parse_glob_par_or_par_sed_args()?;
12996 Ok(Expr {
12997 kind: ExprKind::ParSed { args, progress },
12998 line,
12999 })
13000 }
13001 "bless" => {
13002 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
13003 return Ok(e);
13004 }
13005 let args = self.parse_builtin_args()?;
13006 Ok(Expr {
13007 kind: ExprKind::Bless {
13008 ref_expr: Box::new(args[0].clone()),
13009 class: args.get(1).cloned().map(Box::new),
13010 },
13011 line,
13012 })
13013 }
13014 "caller" => {
13015 if matches!(self.peek(), Token::LParen) {
13016 self.advance();
13017 if matches!(self.peek(), Token::RParen) {
13018 self.advance();
13019 Ok(Expr {
13020 kind: ExprKind::Caller(None),
13021 line,
13022 })
13023 } else {
13024 let a = self.parse_expression()?;
13025 self.expect(&Token::RParen)?;
13026 Ok(Expr {
13027 kind: ExprKind::Caller(Some(Box::new(a))),
13028 line,
13029 })
13030 }
13031 } else {
13032 Ok(Expr {
13033 kind: ExprKind::Caller(None),
13034 line,
13035 })
13036 }
13037 }
13038 "wantarray" => {
13039 if crate::no_interop_mode() {
13040 return Err(self.syntax_err(
13041 "stryke `wantarray` is rejected under --no-interop — \
13042 use explicit return-shape (`@result` vs `$scalar`) \
13043 or pass a flag arg instead of context-sniffing",
13044 line,
13045 ));
13046 }
13047 if matches!(self.peek(), Token::LParen) {
13048 self.advance();
13049 self.expect(&Token::RParen)?;
13050 }
13051 Ok(Expr {
13052 kind: ExprKind::Wantarray,
13053 line,
13054 })
13055 }
13056 "sub" => {
13057 if crate::no_interop_mode() {
13059 return Err(self.syntax_err(
13060 "stryke uses `fn {}` instead of `sub {}` (--no-interop)",
13061 line,
13062 ));
13063 }
13064 let (params, _prototype) = self.parse_sub_sig_or_prototype_opt()?;
13066 let body = self.parse_block()?;
13067 Ok(Expr {
13068 kind: ExprKind::CodeRef { params, body },
13069 line,
13070 })
13071 }
13072 "fn" => {
13073 let (params, _prototype) = self.parse_sub_sig_or_prototype_opt()?;
13075 self.parse_sub_attributes()?;
13076 let body = self.parse_fn_eq_body_or_block(false)?;
13077 Ok(Expr {
13078 kind: ExprKind::CodeRef { params, body },
13079 line,
13080 })
13081 }
13082 _ => {
13083 if matches!(self.peek(), Token::FatArrow) && !Self::is_underscore_topic_slot(&name)
13088 {
13089 return Ok(Expr {
13090 kind: ExprKind::String(name),
13091 line,
13092 });
13093 }
13094 if Self::is_underscore_topic_slot(&name) {
13110 if matches!(self.peek(), Token::LBracket) && self.peek_line() == line {
13111 self.advance(); let index = self.parse_expression()?;
13113 self.expect(&Token::RBracket)?;
13114 return Ok(Expr {
13115 kind: ExprKind::ArrayElement {
13116 array: format!("__topicstr__{}", name),
13117 index: Box::new(index),
13118 },
13119 line,
13120 });
13121 }
13122 return Ok(Expr {
13123 kind: ExprKind::ScalarVar(name.clone()),
13124 line,
13125 });
13126 }
13127 if matches!(self.peek(), Token::LParen) {
13129 self.advance();
13130 let args = self.parse_arg_list()?;
13131 self.expect(&Token::RParen)?;
13132 Ok(Expr {
13133 kind: ExprKind::FuncCall { name, args },
13134 line,
13135 })
13136 } else if self.peek().is_term_start()
13137 && !(matches!(self.peek(), Token::Ident(ref kw) if kw == "sub")
13138 && matches!(self.peek_at(1), Token::Ident(_)))
13139 && !(self.suppress_parenless_call > 0 && matches!(self.peek(), Token::Ident(_)))
13140 && !(matches!(self.peek(), Token::LBrace)
13141 && self.peek_line() > self.prev_line())
13142 && !(matches!(self.peek(), Token::BitNot)
13143 && self.suppress_tilde_range == 0
13144 && matches!(
13145 self.peek_at(1),
13146 Token::Ident(_) | Token::Integer(_) | Token::Float(_)
13147 ))
13148 {
13149 let args = self.parse_list_until_terminator()?;
13163 Ok(Expr {
13164 kind: ExprKind::FuncCall { name, args },
13165 line,
13166 })
13167 } else {
13168 Ok(Expr {
13174 kind: ExprKind::Bareword(name),
13175 line,
13176 })
13177 }
13178 }
13179 }
13180 }
13181
13182 fn take_bareword_filehandle_if(&mut self, accept: &[Token]) -> Option<String> {
13194 let Token::Ident(h) = self.peek().clone() else {
13195 return None;
13196 };
13197 let mut chars = h.chars();
13198 let first = chars.next()?;
13199 if !(first.is_ascii_uppercase() || first == '_') {
13200 return None;
13201 }
13202 if !h
13203 .chars()
13204 .all(|c| c.is_ascii_uppercase() || c.is_ascii_digit() || c == '_')
13205 {
13206 return None;
13207 }
13208 let next = self.peek_at(1);
13209 let ok = if accept.is_empty() {
13210 matches!(
13211 next,
13212 Token::Comma
13213 | Token::Semicolon
13214 | Token::RParen
13215 | Token::RBrace
13216 | Token::Eof
13217 | Token::PipeForward
13218 )
13219 } else {
13220 accept
13221 .iter()
13222 .any(|t| std::mem::discriminant(t) == std::mem::discriminant(next))
13223 };
13224 if !ok {
13225 return None;
13226 }
13227 self.advance();
13228 Some(h)
13229 }
13230
13231 fn take_bareword_filehandle(&mut self) -> Option<String> {
13233 self.take_bareword_filehandle_if(&[Token::Comma])
13234 }
13235
13236 fn take_bareword_filehandle_arg(&mut self, line: usize) -> Option<Expr> {
13240 self.take_bareword_filehandle_if(&[]).map(|name| Expr {
13241 kind: ExprKind::String(name),
13242 line,
13243 })
13244 }
13245
13246 fn parse_print_like(
13247 &mut self,
13248 make: impl FnOnce(Option<String>, Vec<Expr>) -> ExprKind,
13249 ) -> StrykeResult<Expr> {
13250 let line = self.peek_line();
13251 let handle = if let Token::Ident(ref h) = self.peek().clone() {
13253 if h.chars().all(|c| c.is_uppercase() || c == '_')
13254 && !matches!(self.peek(), Token::LParen)
13255 {
13256 let h = h.clone();
13257 let saved = self.pos;
13258 self.advance();
13259 let is_tilde_range_after = matches!(self.peek(), Token::BitNot)
13266 && self.suppress_tilde_range == 0
13267 && matches!(
13268 self.peek_at(1),
13269 Token::Ident(_) | Token::Integer(_) | Token::Float(_)
13270 );
13271 if !is_tilde_range_after
13272 && (self.peek().is_term_start()
13273 || matches!(
13274 self.peek(),
13275 Token::DoubleString(_)
13276 | Token::BacktickString(_)
13277 | Token::SingleString(_)
13278 ))
13279 {
13280 Some(h)
13281 } else {
13282 self.pos = saved;
13283 None
13284 }
13285 } else {
13286 None
13287 }
13288 } else if let Token::ScalarVar(ref v) = self.peek().clone() {
13289 let v = v.clone();
13301 if v == "_" {
13302 None
13303 } else {
13304 let saved = self.pos;
13305 let var_line = self.peek_line();
13306 self.advance();
13307 let next = self.peek().clone();
13308 let next_line = self.peek_line();
13309 let is_stmt_modifier = matches!(&next, Token::Ident(kw)
13310 if matches!(kw.as_str(), "if" | "unless" | "while" | "until" | "for" | "foreach"));
13311 if !is_stmt_modifier
13312 && next_line == var_line
13313 && !matches!(next, Token::LBracket | Token::LBrace)
13314 && (next.is_term_start()
13315 || matches!(
13316 next,
13317 Token::DoubleString(_)
13318 | Token::BacktickString(_)
13319 | Token::SingleString(_)
13320 ))
13321 {
13322 Some(format!("${v}"))
13324 } else {
13325 self.pos = saved;
13326 None
13327 }
13328 }
13329 } else {
13330 None
13331 };
13332 let args =
13339 if matches!(self.peek(), Token::LParen) && matches!(self.peek_at(1), Token::RParen) {
13340 let line_topic = self.peek_line();
13341 self.advance(); self.advance(); vec![Expr {
13344 kind: ExprKind::ScalarVar("_".into()),
13345 line: line_topic,
13346 }]
13347 } else {
13348 self.parse_list_until_terminator_allow_pipe()?
13349 };
13350 Ok(Expr {
13351 kind: make(handle, args),
13352 line,
13353 })
13354 }
13355
13356 fn parse_block_list(&mut self) -> StrykeResult<(Block, Expr)> {
13357 let block = self.parse_block()?;
13358 let block_end_line = self.prev_line();
13359 self.eat(&Token::Comma);
13360 if self.in_pipe_rhs()
13364 && (matches!(
13365 self.peek(),
13366 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof | Token::PipeForward
13367 ) || self.peek_line() > block_end_line)
13368 {
13369 let line = self.peek_line();
13370 return Ok((block, self.pipe_placeholder_list(line)));
13371 }
13372 let list = self.parse_expression()?;
13373 Ok((block, list))
13374 }
13375
13376 fn parse_comma_expr_list_with_timeout_tail(
13379 &mut self,
13380 paren: bool,
13381 ) -> StrykeResult<(Vec<Expr>, Option<Expr>)> {
13382 let mut parts = vec![self.parse_assign_expr()?];
13383 loop {
13384 if !self.eat(&Token::Comma) && !self.eat(&Token::FatArrow) {
13385 break;
13386 }
13387 if paren && matches!(self.peek(), Token::RParen) {
13388 break;
13389 }
13390 if matches!(
13391 self.peek(),
13392 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof
13393 ) {
13394 break;
13395 }
13396 if self.peek_is_postfix_stmt_modifier_keyword() {
13397 break;
13398 }
13399 if let Token::Ident(ref kw) = self.peek().clone() {
13400 if kw == "timeout" && matches!(self.peek_at(1), Token::FatArrow) {
13401 self.advance();
13402 self.expect(&Token::FatArrow)?;
13403 let t = self.parse_assign_expr()?;
13404 return Ok((parts, Some(t)));
13405 }
13406 }
13407 parts.push(self.parse_assign_expr()?);
13408 }
13409 Ok((parts, None))
13410 }
13411
13412 fn parse_init_block_then_list_optional_progress(
13414 &mut self,
13415 ) -> StrykeResult<(Expr, Block, Expr, Option<Expr>)> {
13416 let init = self.parse_assign_expr()?;
13417 self.expect(&Token::Comma)?;
13418 let block = self.parse_block_or_bareword_block()?;
13419 self.eat(&Token::Comma);
13420 let line = self.peek_line();
13421 if let Token::Ident(ref kw) = self.peek().clone() {
13422 if kw == "progress" && matches!(self.peek_at(1), Token::FatArrow) {
13423 self.advance();
13424 self.expect(&Token::FatArrow)?;
13425 let prog = self.parse_assign_expr()?;
13426 return Ok((
13427 init,
13428 block,
13429 Expr {
13430 kind: ExprKind::List(vec![]),
13431 line,
13432 },
13433 Some(prog),
13434 ));
13435 }
13436 }
13437 if matches!(
13438 self.peek(),
13439 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof
13440 ) {
13441 return Ok((
13442 init,
13443 block,
13444 Expr {
13445 kind: ExprKind::List(vec![]),
13446 line,
13447 },
13448 None,
13449 ));
13450 }
13451 let mut parts = vec![self.parse_assign_expr()?];
13452 loop {
13453 if !self.eat(&Token::Comma) && !self.eat(&Token::FatArrow) {
13454 break;
13455 }
13456 if matches!(
13457 self.peek(),
13458 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof
13459 ) {
13460 break;
13461 }
13462 if self.peek_is_postfix_stmt_modifier_keyword() {
13463 break;
13464 }
13465 if let Token::Ident(ref kw) = self.peek().clone() {
13466 if kw == "progress" && matches!(self.peek_at(1), Token::FatArrow) {
13467 self.advance();
13468 self.expect(&Token::FatArrow)?;
13469 let prog = self.parse_assign_expr()?;
13470 return Ok((init, block, merge_expr_list(parts), Some(prog)));
13471 }
13472 }
13473 parts.push(self.parse_assign_expr()?);
13474 }
13475 Ok((init, block, merge_expr_list(parts), None))
13476 }
13477
13478 fn parse_cluster_block_then_list_optional_progress(
13480 &mut self,
13481 ) -> StrykeResult<(Expr, Block, Expr, Option<Expr>)> {
13482 self.suppress_scalar_hash_brace = self.suppress_scalar_hash_brace.saturating_add(1);
13485 let cluster = self.parse_assign_expr();
13486 self.suppress_scalar_hash_brace = self.suppress_scalar_hash_brace.saturating_sub(1);
13487 let cluster = cluster?;
13488 self.eat(&Token::Comma);
13490 let block = self.parse_block_or_bareword_block()?;
13491 let block_end_line = self.prev_line();
13492 self.eat(&Token::Comma);
13493 let line = self.peek_line();
13494 if let Token::Ident(ref kw) = self.peek().clone() {
13495 if kw == "progress" && matches!(self.peek_at(1), Token::FatArrow) {
13496 self.advance();
13497 self.expect(&Token::FatArrow)?;
13498 let prog = self.parse_assign_expr_stop_at_pipe()?;
13499 return Ok((
13500 cluster,
13501 block,
13502 Expr {
13503 kind: ExprKind::List(vec![]),
13504 line,
13505 },
13506 Some(prog),
13507 ));
13508 }
13509 }
13510 let empty_list_ok = matches!(
13511 self.peek(),
13512 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof | Token::PipeForward
13513 ) || (self.in_pipe_rhs()
13514 && (matches!(self.peek(), Token::Comma) || self.peek_line() > block_end_line));
13515 if empty_list_ok {
13516 return Ok((
13517 cluster,
13518 block,
13519 Expr {
13520 kind: ExprKind::List(vec![]),
13521 line,
13522 },
13523 None,
13524 ));
13525 }
13526 let mut parts = vec![self.parse_assign_expr_stop_at_pipe()?];
13527 loop {
13528 if !self.eat(&Token::Comma) && !self.eat(&Token::FatArrow) {
13529 break;
13530 }
13531 if matches!(
13532 self.peek(),
13533 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof | Token::PipeForward
13534 ) {
13535 break;
13536 }
13537 if self.peek_is_postfix_stmt_modifier_keyword() {
13538 break;
13539 }
13540 if let Token::Ident(ref kw) = self.peek().clone() {
13541 if kw == "progress" && matches!(self.peek_at(1), Token::FatArrow) {
13542 self.advance();
13543 self.expect(&Token::FatArrow)?;
13544 let prog = self.parse_assign_expr_stop_at_pipe()?;
13545 return Ok((cluster, block, merge_expr_list(parts), Some(prog)));
13546 }
13547 }
13548 parts.push(self.parse_assign_expr_stop_at_pipe()?);
13549 }
13550 Ok((cluster, block, merge_expr_list(parts), None))
13551 }
13552
13553 fn parse_block_then_list_optional_progress(
13562 &mut self,
13563 ) -> StrykeResult<(Block, Expr, Option<Expr>)> {
13564 let block = self.parse_block_or_bareword_block()?;
13565 let block_end_line = self.prev_line();
13566 self.eat(&Token::Comma);
13567 let line = self.peek_line();
13568 if let Token::Ident(ref kw) = self.peek().clone() {
13569 if kw == "progress" && matches!(self.peek_at(1), Token::FatArrow) {
13570 self.advance();
13571 self.expect(&Token::FatArrow)?;
13572 let prog = self.parse_assign_expr_stop_at_pipe()?;
13573 return Ok((
13574 block,
13575 Expr {
13576 kind: ExprKind::List(vec![]),
13577 line,
13578 },
13579 Some(prog),
13580 ));
13581 }
13582 }
13583 let empty_list_ok = matches!(
13591 self.peek(),
13592 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof | Token::PipeForward
13593 ) || (self.in_pipe_rhs()
13594 && (matches!(self.peek(), Token::Comma) || self.peek_line() > block_end_line));
13595 if empty_list_ok {
13596 return Ok((
13597 block,
13598 Expr {
13599 kind: ExprKind::List(vec![]),
13600 line,
13601 },
13602 None,
13603 ));
13604 }
13605 let mut parts = vec![self.parse_assign_expr_stop_at_pipe()?];
13606 loop {
13607 if !self.eat(&Token::Comma) && !self.eat(&Token::FatArrow) {
13608 break;
13609 }
13610 if matches!(
13611 self.peek(),
13612 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof | Token::PipeForward
13613 ) {
13614 break;
13615 }
13616 if self.peek_is_postfix_stmt_modifier_keyword() {
13617 break;
13618 }
13619 if let Token::Ident(ref kw) = self.peek().clone() {
13620 if kw == "progress" && matches!(self.peek_at(1), Token::FatArrow) {
13621 self.advance();
13622 self.expect(&Token::FatArrow)?;
13623 let prog = self.parse_assign_expr_stop_at_pipe()?;
13624 return Ok((block, merge_expr_list(parts), Some(prog)));
13625 }
13626 }
13627 parts.push(self.parse_assign_expr_stop_at_pipe()?);
13628 }
13629 Ok((block, merge_expr_list(parts), None))
13630 }
13631
13632 fn parse_fan_count_and_block(
13634 &mut self,
13635 line: usize,
13636 ) -> StrykeResult<(Option<Box<Expr>>, Block)> {
13637 if matches!(self.peek(), Token::LBrace) {
13639 let block = self.parse_block()?;
13640 return Ok((None, block));
13641 }
13642 let saved = self.pos;
13643 let first = self.parse_postfix()?;
13645 if matches!(self.peek(), Token::LBrace) {
13646 let block = self.parse_block()?;
13648 Ok((Some(Box::new(first)), block))
13649 } else if matches!(self.peek(), Token::Semicolon | Token::RBrace | Token::Eof)
13650 || (matches!(self.peek(), Token::Comma)
13651 && matches!(self.peek_at(1), Token::Ident(ref kw) if kw == "progress"))
13652 {
13653 let block = self.bareword_to_no_arg_block(first);
13655 Ok((None, block))
13656 } else if matches!(first.kind, ExprKind::Integer(_)) {
13657 self.eat(&Token::Comma);
13659 let body = self.parse_fan_blockless_body(line)?;
13660 Ok((Some(Box::new(first)), body))
13661 } else {
13662 self.pos = saved;
13665 let body = self.parse_fan_blockless_body(line)?;
13666 Ok((None, body))
13667 }
13668 }
13669
13670 fn parse_fan_blockless_body(&mut self, line: usize) -> StrykeResult<Block> {
13672 if matches!(self.peek(), Token::LBrace) {
13673 return self.parse_block();
13674 }
13675 if let Token::Ident(ref name) = self.peek().clone() {
13677 if matches!(
13678 self.peek_at(1),
13679 Token::Comma | Token::Semicolon | Token::RBrace | Token::Eof | Token::PipeForward
13680 ) {
13681 let name = name.clone();
13682 self.advance();
13683 let body = Expr {
13684 kind: ExprKind::FuncCall { name, args: vec![] },
13685 line,
13686 };
13687 return Ok(vec![Statement::new(StmtKind::Expression(body), line)]);
13688 }
13689 }
13690 let expr = self.parse_assign_expr_stop_at_pipe()?;
13692 Ok(vec![Statement::new(StmtKind::Expression(expr), line)])
13693 }
13694
13695 fn bareword_to_no_arg_block(&self, expr: Expr) -> Block {
13698 let line = expr.line;
13699 let body = match &expr.kind {
13700 ExprKind::Bareword(name) => Expr {
13701 kind: ExprKind::FuncCall {
13702 name: name.clone(),
13703 args: vec![],
13704 },
13705 line,
13706 },
13707 _ => expr,
13708 };
13709 vec![Statement::new(StmtKind::Expression(body), line)]
13710 }
13711
13712 fn parse_block_or_bareword_block(&mut self) -> StrykeResult<Block> {
13721 if matches!(self.peek(), Token::LBrace) {
13722 return self.parse_block();
13723 }
13724 let line = self.peek_line();
13725 if let Token::Ident(ref name) = self.peek().clone() {
13728 if matches!(
13729 self.peek_at(1),
13730 Token::Comma | Token::Semicolon | Token::RBrace | Token::Eof | Token::PipeForward
13731 ) {
13732 let name = name.clone();
13733 self.advance();
13734 let body = Expr {
13735 kind: ExprKind::FuncCall {
13736 name,
13737 args: vec![Expr {
13738 kind: ExprKind::ScalarVar("_".to_string()),
13739 line,
13740 }],
13741 },
13742 line,
13743 };
13744 return Ok(vec![Statement::new(StmtKind::Expression(body), line)]);
13745 }
13746 }
13747 let expr = self.parse_assign_expr_stop_at_pipe()?;
13749 Ok(vec![Statement::new(StmtKind::Expression(expr), line)])
13750 }
13751
13752 fn parse_block_or_bareword_block_no_args(&mut self) -> StrykeResult<Block> {
13757 if matches!(self.peek(), Token::LBrace) {
13758 return self.parse_block();
13759 }
13760 let line = self.peek_line();
13761 if let Token::Ident(ref name) = self.peek().clone() {
13762 if matches!(
13763 self.peek_at(1),
13764 Token::Comma
13765 | Token::Semicolon
13766 | Token::RBrace
13767 | Token::Eof
13768 | Token::PipeForward
13769 | Token::Integer(_)
13770 ) {
13771 let name = name.clone();
13772 self.advance();
13773 let body = Expr {
13774 kind: ExprKind::FuncCall { name, args: vec![] },
13775 line,
13776 };
13777 return Ok(vec![Statement::new(StmtKind::Expression(body), line)]);
13778 }
13779 }
13780 let expr = self.parse_postfix()?;
13781 Ok(vec![Statement::new(StmtKind::Expression(expr), line)])
13782 }
13783
13784 fn is_known_bareword(name: &str) -> bool {
13792 Self::is_perl5_core(name) || Self::stryke_extension_name(name).is_some()
13793 }
13794
13795 fn is_try_builtin_name(name: &str) -> bool {
13801 crate::builtins::BUILTIN_ARMS
13802 .iter()
13803 .any(|arm| arm.contains(&name))
13804 }
13805
13806 fn is_perl5_core(name: &str) -> bool {
13811 matches!(
13812 name,
13813 "map" | "grep" | "sort" | "reverse" | "join" | "split"
13815 | "push" | "pop" | "shift" | "unshift" | "splice"
13816 | "splice_last" | "splice1" | "spl_last"
13817 | "pack" | "unpack"
13818 | "unpack_first" | "unpack1" | "up1"
13819 | "keys" | "values" | "each"
13821 | "chomp" | "chop" | "chr" | "ord" | "hex" | "oct"
13823 | "lc" | "uc" | "lcfirst" | "ucfirst"
13824 | "length" | "substr" | "index" | "rindex"
13825 | "sprintf" | "printf" | "print" | "say"
13826 | "pos" | "quotemeta" | "study"
13827 | "abs" | "int" | "sqrt" | "sin" | "cos" | "atan2"
13829 | "exp" | "log" | "rand" | "srand"
13830 | "time" | "localtime" | "gmtime"
13832 | "defined" | "undef" | "ref" | "scalar" | "wantarray"
13834 | "caller" | "delete" | "exists" | "bless" | "prototype"
13835 | "tie" | "untie" | "tied"
13836 | "open" | "close" | "read" | "readline" | "write" | "seek" | "tell"
13838 | "eof" | "binmode" | "getc" | "fileno" | "truncate"
13839 | "format" | "formline" | "select" | "vec"
13840 | "sysopen" | "sysread" | "sysseek" | "syswrite"
13841 | "stat" | "lstat" | "rename" | "unlink" | "utime"
13843 | "mkdir" | "rmdir" | "chdir" | "chmod" | "chown"
13844 | "glob" | "opendir" | "readdir" | "closedir"
13845 | "link" | "readlink" | "symlink"
13846 | "fcntl" | "flock" | "ioctl" | "pipe" | "dbmopen" | "dbmclose"
13848 | "msgctl" | "msgget" | "msgrcv" | "msgsnd"
13850 | "semctl" | "semget" | "semop"
13851 | "shmctl" | "shmget" | "shmread" | "shmwrite"
13852 | "system" | "exec" | "exit" | "die" | "warn" | "dump"
13854 | "fork" | "wait" | "waitpid" | "kill" | "syscall" | "alarm" | "sleep"
13855 | "chroot" | "times" | "umask" | "reset"
13856 | "getpgrp" | "setpgrp" | "getppid"
13857 | "getpriority" | "setpriority"
13858 | "socket" | "socketpair" | "connect" | "listen" | "accept" | "shutdown"
13860 | "send" | "recv" | "bind" | "setsockopt" | "getsockopt"
13861 | "getpeername" | "getsockname"
13862 | "getpwnam" | "getpwuid" | "getpwent" | "setpwent"
13864 | "getgrnam" | "getgrgid" | "getgrent" | "setgrent"
13865 | "getlogin"
13866 | "gethostbyname" | "gethostbyaddr" | "gethostent"
13867 | "getnetbyname" | "getnetent"
13868 | "getprotobyname" | "getprotoent"
13869 | "getservbyname" | "getservent"
13870 | "sethostent" | "setnetent" | "setprotoent" | "setservent"
13871 | "endpwent" | "endgrent"
13872 | "endhostent" | "endnetent" | "endprotoent" | "endservent"
13873 | "return" | "do" | "eval" | "require"
13875 | "my" | "our" | "local" | "use" | "no"
13876 | "sub" | "if" | "unless" | "while" | "until"
13877 | "for" | "foreach" | "last" | "next" | "redo" | "goto"
13878 | "not" | "and" | "or"
13879 | "qw" | "qq" | "q"
13881 | "BEGIN" | "END"
13883 )
13884 }
13885
13886 fn stryke_extension_name(name: &str) -> Option<&str> {
13889 match name {
13890 | "ulid" | "is_ulid" | "ulid_timestamp"
13892 | "kahan_sum" | "welford_mean" | "welford_variance"
13893 | "welford_stddev" | "welford_pop_variance"
13894 | "clear" | "cls" | "whoami" | "groups"
13896 | "pushd" | "popd" | "dir_stack"
13897 | "history" | "repl_alias" | "repl_unalias" | "set_alias" | "unset_alias"
13898 | "term_size" | "term_width" | "term_height"
13899 | "set_title" | "beep" | "ring_bell" | "man" | "manpage"
13900 | "run" | "exec_script" | "source" | "src"
13901 | "rm" | "mktemp" | "mktempdir" | "whereis"
13903 | "nice" | "renice"
13904 | "tree" | "comm" | "column" | "xargs"
13905 | "openurl" | "xdg_open"
13906 | "curl_get" | "curl_post"
13907 | "iconv" | "strftime"
13908 | "tac" | "rev_lines"
13909 | "tty_raw" | "tty_cooked"
13910 | "bloom_filter" | "bloom_add" | "bloom_contains" | "bloom_len"
13912 | "bloom_clear" | "bloom_merge" | "bloom_fpr" | "bloom_bits"
13913 | "bloom_serialize" | "bloom_deserialize"
13914 | "hll" | "hyperloglog" | "hll_add" | "hll_count" | "hll_merge"
13915 | "hll_clear" | "hll_precision" | "hll_serialize" | "hll_deserialize"
13916 | "cms" | "count_min_sketch" | "cms_add" | "cms_count" | "cms_query"
13917 | "cms_merge" | "cms_clear" | "cms_serialize" | "cms_deserialize"
13918 | "topk" | "top_k_sketch" | "topk_add" | "topk_heavies" | "topk_count"
13919 | "topk_size" | "topk_merge" | "topk_clear"
13920 | "topk_serialize" | "topk_deserialize"
13921 | "t_digest" | "tdg" | "tdigest" | "td_add" | "td_quantile" | "td_count"
13922 | "td_min" | "td_max" | "td_sum" | "td_mean" | "td_merge" | "td_clear"
13923 | "td_serialize" | "td_deserialize"
13924 | "roaring" | "roaring_bitmap" | "rbm" | "rb_add" | "rb_remove" | "rb_contains"
13925 | "rb_len" | "rb_min" | "rb_max" | "rb_to_array" | "rb_rank"
13926 | "rb_or" | "rb_and" | "rb_xor" | "rb_andnot" | "rb_clear"
13927 | "rb_serialize" | "rb_deserialize"
13928 | "token_bucket" | "leaky_bucket" | "rl_try_take" | "rl_available"
13930 | "hash_ring" | "consistent_hash" | "hr_add" | "hr_remove" | "hr_get" | "hr_nodes"
13931 | "simhash" | "sh_add" | "sh_digest" | "sh_similarity"
13932 | "minhash" | "mh_add" | "mh_jaccard" | "mh_merge"
13933 | "interval_tree" | "it_insert" | "it_query_point" | "it_query_range"
13934 | "it_remove" | "it_len"
13935 | "bk_tree" | "bk_insert" | "bk_query" | "bk_len"
13936 | "rope" | "rope_insert" | "rope_delete" | "rope_substring"
13937 | "rope_to_string" | "rope_len"
13938 | "myers_diff" | "patience_diff"
13939 | "kv_open" | "kv_new" | "kv_put" | "kv_set" | "kv_get"
13941 | "kv_del" | "kv_delete" | "kv_remove" | "kv_exists" | "kv_has"
13942 | "kv_keys" | "kv_scan" | "kv_len" | "kv_count" | "kv_size"
13943 | "kv_commit" | "kv_flush" | "kv_batch" | "kv_close"
13944 | "kv_stats" | "kv_info"
13945 | "proceed" | "intercept_list" | "intercept_remove" | "intercept_clear"
13947 | "pmap" | "pmap_on" | "pflat_map" | "pflat_map_on" | "pmap_chunked"
13949 | "pgrep" | "pfor" | "psort" | "preduce" | "preduce_init" | "pmap_reduce"
13950 | "pcache" | "pchannel" | "pselect" | "puniq" | "pfirst" | "pany"
13951 | "fan" | "fan_cap" | "par_lines" | "par_walk" | "par_sed"
13952 | "par_find_files" | "par_line_count" | "pwatch" | "par_pipeline_stream"
13953 | "glob_par" | "ppool" | "barrier" | "pipeline" | "cluster"
13954 | "pmaps" | "pflat_maps" | "pgreps"
13955 | "fore" | "e" | "ep" | "flat_map" | "flat_maps" | "maps" | "filter" | "fi" | "find_all" | "reduce" | "fold"
13957 | "inject" | "collect" | "uniq" | "distinct" | "any" | "all" | "none"
13958 | "first" | "detect" | "find" | "find_index" | "firstidx" | "first_index"
13959 | "compact" | "concat" | "chain" | "reject" | "grepv" | "flatten" | "set"
13960 | "min_by" | "max_by" | "sort_by" | "tally"
13961 | "each_with_index" | "count" | "cnt" |"len" | "group_by" | "chunk_by"
13962 | "zip" | "chunk" | "chunked" | "sliding_window" | "windowed"
13963 | "enumerate" | "with_index" | "shuffle" | "shuffled"| "heap"
13964 | "take_while" | "drop_while" | "skip_while" | "tap" | "peek" | "partition"
13965 | "zip_with" | "count_by" | "skip" | "first_or"
13966 | "getopts"
13968 | "input" | "lines" | "words" | "chars" | "cindex" | "crindex"
13970 | "digits" | "letters" | "letters_uc" | "letters_lc"
13971 | "punctuation" | "punct"
13972 | "sentences" | "sents"
13973 | "paragraphs" | "paras" | "sections" | "sects"
13974 | "numbers" | "nums" | "graphemes" | "grs" | "columns" | "cols"
13975 | "trim" | "avg" | "stddev"
13976 | "squared" | "sq" | "square" | "cubed" | "cb" | "cube" | "expt" | "pow" | "pw"
13977 | "normalize" | "snake_case" | "camel_case" | "kebab_case"
13978 | "frequencies" | "freq" | "pfrequencies" | "pfreq"
13979 | "interleave" | "ddump" | "stringify" | "str" | "top"
13980 | "to_json" | "to_csv" | "to_toml" | "to_yaml" | "to_xml"
13981 | "to_html" | "to_markdown" | "to_table" | "xopen"
13982 | "from_json" | "from_csv" | "from_toml" | "from_yaml" | "from_xml"
13983 | "clip" | "clipboard" | "paste" | "pbcopy" | "pbpaste" | "preview"
13984 | "sparkline" | "spark" | "bar_chart" | "bars" | "flame" | "flamechart"
13985 | "histo" | "gauge" | "spinner" | "spinner_start" | "spinner_stop"
13986 | "to_hash" | "to_set"
13987 | "to_file" | "read_lines" | "append_file" | "write_json" | "read_json"
13988 | "tempfile" | "tempdir" | "list_count" | "list_size" | "size"
13989 | "clamp" | "grep_v" | "select_keys" | "pluck" | "glob_match" | "which_all"
13990 | "dedup" | "nth" | "tail" | "take" | "drop" | "tee" | "range"
13991 | "inc" | "dec" | "elapsed"
13992 | "files" | "filesf" | "f" | "fr" | "dirs" | "d" | "dr" | "sym_links"
13994 | "sockets" | "pipes" | "block_devices" | "char_devices" | "exe" | "executables"
13995 | "basename" | "dirname" | "fileparse" | "realpath" | "canonpath"
13996 | "copy" | "cp" | "move" | "spurt" | "spit" | "read_bytes" | "which"
13997 | "getcwd" | "cd" | "ls" | "touch" | "gethostname" | "uname"
13998 | "file" | "xxd"
13999 | "csv_read" | "csv_write" | "dataframe" | "sqlite"
14001 | "fetch" | "fetch_json" | "fetch_async" | "fetch_async_json"
14002 | "par_fetch" | "par_csv_read" | "par_pipeline"
14003 | "json_encode" | "json_decode" | "json_jq"
14004 | "http_request" | "serve" | "ssh"
14005 | "html_parse" | "css_select" | "xml_parse" | "xpath"
14006 | "smtp_send"
14007 | "net_interfaces" | "net_ipv4" | "net_ipv6" | "net_mac"
14008 | "net_public_ip" | "net_dns" | "net_reverse_dns"
14009 | "net_ping" | "net_port_open" | "net_ports_scan"
14010 | "net_latency" | "net_download" | "net_headers"
14011 | "net_dns_servers" | "net_gateway" | "net_whois" | "net_hostname"
14012 | "git_log" | "git_status" | "git_diff" | "git_branches"
14014 | "git_tags" | "git_blame" | "git_authors" | "git_files"
14015 | "git_show" | "git_root"
14016 | "gh_get" | "gh_user" | "gh_org" | "gh_followers" | "gh_following"
14018 | "gh_repo" | "gh_repos" | "gh_org_repos" | "gh_starred"
14019 | "gh_gists" | "gh_gist"
14020 | "gh_issues" | "gh_prs" | "gh_commits" | "gh_branches"
14021 | "gh_tags" | "gh_releases" | "gh_contributors" | "gh_forks"
14022 | "gh_stargazers" | "gh_topics" | "gh_languages"
14023 | "gh_readme" | "gh_workflows" | "gh_runs"
14024 | "gh_search_repos" | "gh_search_users" | "gh_search_code" | "gh_search_issues"
14025 | "gh_rate_limit" | "gh_meta" | "gh_emojis" | "gh_zen"
14026 | "audio_convert" | "audio_info" | "id3_read" | "id3_write"
14028 | "to_pdf" | "pdf_text" | "pdf_pages"
14030 | "toml_encode" | "toml_decode"
14032 | "yaml_encode" | "yaml_decode"
14033 | "xml_encode" | "xml_decode"
14034 | "md5" | "sha1" | "sha224" | "sha256" | "sha384" | "sha512"
14036 | "sha3_256" | "s3_256" | "sha3_512" | "s3_512"
14037 | "shake128" | "shake256"
14038 | "hmac_sha256" | "hmac_sha1" | "hmac_sha384" | "hmac_sha512" | "hmac_md5"
14039 | "uuid" | "crc32"
14040 | "blake2b" | "b2b" | "blake2s" | "b2s" | "blake3" | "b3"
14041 | "ripemd160" | "rmd160" | "md4"
14042 | "xxh32" | "xxhash32" | "xxh64" | "xxhash64" | "xxh3" | "xxhash3" | "xxh3_128" | "xxhash3_128"
14043 | "murmur3" | "murmur3_32" | "murmur3_128"
14044 | "siphash" | "siphash_keyed"
14045 | "hkdf_sha256" | "hkdf" | "hkdf_sha512"
14046 | "poly1305" | "poly1305_mac"
14047 | "base32_encode" | "b32e" | "base32_decode" | "b32d"
14048 | "base58_encode" | "b58e" | "base58_decode" | "b58d"
14049 | "totp" | "totp_generate" | "totp_verify" | "hotp" | "hotp_generate"
14050 | "aes_cbc_encrypt" | "aes_cbc_enc" | "aes_cbc_decrypt" | "aes_cbc_dec"
14051 | "blowfish_encrypt" | "bf_enc" | "blowfish_decrypt" | "bf_dec"
14052 | "des3_encrypt" | "3des_enc" | "tdes_enc" | "des3_decrypt" | "3des_dec" | "tdes_dec"
14053 | "twofish_encrypt" | "tf_enc" | "twofish_decrypt" | "tf_dec"
14054 | "camellia_encrypt" | "cam_enc" | "camellia_decrypt" | "cam_dec"
14055 | "cast5_encrypt" | "cast5_enc" | "cast5_decrypt" | "cast5_dec"
14056 | "salsa20" | "salsa20_encrypt" | "salsa20_decrypt"
14057 | "xsalsa20" | "xsalsa20_encrypt" | "xsalsa20_decrypt"
14058 | "secretbox" | "secretbox_seal" | "secretbox_open"
14059 | "nacl_box_keygen" | "box_keygen" | "nacl_box" | "nacl_box_seal" | "box_seal"
14060 | "nacl_box_open" | "box_open"
14061 | "qr_ascii" | "qr" | "qr_png" | "qr_svg"
14062 | "barcode_code128" | "code128" | "barcode_code39" | "code39"
14063 | "barcode_ean13" | "ean13" | "barcode_svg"
14064 | "argon2_hash" | "argon2" | "argon2_verify"
14065 | "bcrypt_hash" | "bcrypt" | "bcrypt_verify"
14066 | "scrypt_hash" | "scrypt" | "scrypt_verify"
14067 | "pbkdf2" | "pbkdf2_derive"
14068 | "random_bytes" | "randbytes" | "random_bytes_hex" | "randhex"
14069 | "aes_encrypt" | "aes_enc" | "aes_decrypt" | "aes_dec"
14070 | "chacha_encrypt" | "chacha_enc" | "chacha_decrypt" | "chacha_dec"
14071 | "rsa_keygen" | "rsa_encrypt" | "rsa_enc" | "rsa_decrypt" | "rsa_dec"
14072 | "rsa_encrypt_pkcs1" | "rsa_decrypt_pkcs1" | "rsa_sign" | "rsa_verify"
14073 | "ecdsa_p256_keygen" | "p256_keygen" | "ecdsa_p256_sign" | "p256_sign"
14074 | "ecdsa_p256_verify" | "p256_verify"
14075 | "ecdsa_p384_keygen" | "p384_keygen" | "ecdsa_p384_sign" | "p384_sign"
14076 | "ecdsa_p384_verify" | "p384_verify"
14077 | "ecdsa_secp256k1_keygen" | "secp256k1_keygen"
14078 | "ecdsa_secp256k1_sign" | "secp256k1_sign"
14079 | "ecdsa_secp256k1_verify" | "secp256k1_verify"
14080 | "ecdh_p256" | "p256_dh" | "ecdh_p384" | "p384_dh"
14081 | "ed25519_keygen" | "ed_keygen" | "ed25519_sign" | "ed_sign"
14082 | "ed25519_verify" | "ed_verify"
14083 | "x25519_keygen" | "x_keygen" | "x25519_dh" | "x_dh"
14084 | "base64_encode" | "base64_decode"
14085 | "hex_encode" | "hex_decode"
14086 | "url_encode" | "url_decode"
14087 | "gzip" | "gunzip" | "gz" | "ugz" | "zstd" | "zstd_decode" | "zst" | "uzst"
14088 | "brotli" | "br" | "brotli_decode" | "ubr"
14089 | "xz" | "lzma" | "xz_decode" | "unxz" | "unlzma"
14090 | "bzip2" | "bz2" | "bzip2_decode" | "bunzip2" | "ubz2"
14091 | "lz4" | "lz4_decode" | "unlz4"
14092 | "snappy" | "snp" | "snappy_decode" | "unsnappy"
14093 | "lzw" | "lzw_decode" | "unlzw"
14094 | "tar_create" | "tar" | "tar_extract" | "untar" | "tar_list"
14095 | "tar_gz_create" | "tgz" | "tar_gz_extract" | "untgz"
14096 | "zip_create" | "zip_archive" | "zip_extract" | "unzip_archive" | "zip_list"
14097 | "erf" | "erfc" | "gamma" | "tgamma" | "lgamma" | "ln_gamma"
14099 | "digamma" | "psi" | "beta_fn" | "lbeta" | "ln_beta"
14100 | "betainc" | "beta_reg" | "gammainc" | "gamma_li"
14101 | "gammaincc" | "gamma_ui" | "gammainc_reg" | "gamma_lr"
14102 | "gammaincc_reg" | "gamma_ur"
14103 | "datetime_utc" | "datetime_now_tz" | "now"
14105 | "datetime_format_tz" | "datetime_add_seconds"
14106 | "datetime_from_epoch"
14107 | "datetime_parse_rfc3339" | "datetime_parse_local"
14108 | "datetime_strftime"
14109 | "dateseq" | "dategrep" | "dateround" | "datesort"
14110 | "jwt_encode" | "jwt_decode" | "jwt_decode_unsafe"
14112 | "log_info" | "log_warn" | "log_error"
14114 | "log_debug" | "log_trace" | "log_json" | "log_level"
14115 | "async" | "spawn" | "trace" | "timer" | "bench"
14117 | "eval_timeout" | "retry" | "rate_limit" | "every"
14118 | "gen" | "watch"
14119 | "cache_clear" | "cache_exists" | "cache_stats" | "cacheview"
14121 | "assert_eq" | "assert_ne" | "assert_ok" | "assert_err"
14123 | "assert_true" | "assert_false"
14124 | "assert_gt" | "assert_lt" | "assert_ge" | "assert_le"
14125 | "assert_match" | "assert_contains" | "assert_near" | "assert_dies"
14126 | "test_run" | "run_tests" | "test_skip" | "skip_test" | "skip_assert"
14127 | "mounts" | "du" | "du_tree" | "process_list"
14129 | "thread_count" | "pool_info" | "par_bench"
14130 | "perfview" | "pfv"
14131 | "docs" | "help" | "h"
14132 | "banner"
14133 | "ip_parse" | "ip_is_valid" | "ip_version" | "ip_family"
14135 | "ip_to_int" | "int_to_ip" | "ip_to_bytes" | "bytes_to_ip"
14136 | "ip_to_bits" | "bits_to_ip"
14137 | "ip_is_private" | "ip_is_loopback" | "ip_is_multicast"
14138 | "ip_is_link_local" | "ip_is_unspecified" | "ip_is_global"
14139 | "ip_is_documentation" | "ip_is_benchmarking" | "ip_is_shared"
14140 | "ip_is_reserved" | "ip_is_broadcast"
14141 | "ip_canonical" | "ip_reverse" | "ip_arpa"
14142 | "ip_compare" | "ip_sort" | "ip_random"
14143 | "ipv4_parse" | "ipv4_is_valid" | "ipv4_classful_class"
14144 | "ipv6_parse" | "ipv6_is_valid" | "ipv6_canonical"
14145 | "ipv6_expand" | "ipv6_compress" | "ipv6_strip_zone" | "ipv6_zone_id"
14146 | "ipv6_link_local" | "ipv6_unique_local" | "ipv6_solicited_node"
14147 | "ipv6_eui64_addr" | "ipv6_link_local_from_mac"
14148 | "ipv4_to_ipv6_mapped" | "ipv4_to_ipv6_6to4" | "ipv6_to_ipv4_compat"
14149 | "ipv6_is_6to4" | "ipv6_6to4_extract"
14150 | "ipv6_is_teredo" | "ipv6_teredo_extract"
14151 | "ipv6_is_isatap" | "ipv6_isatap_extract"
14152 | "cidr_parse" | "cidr_valid_subnet" | "cidr_format"
14153 | "cidr_prefix_len" | "cidr_class"
14154 | "cidr_network" | "cidr_broadcast" | "cidr_netmask"
14155 | "cidr_hostmask" | "cidr_wildcard"
14156 | "cidr_to_netmask" | "netmask_to_prefix"
14157 | "cidr_first_host" | "cidr_last_host" | "cidr_num_hosts"
14158 | "cidr_size" | "cidr_hosts" | "cidr_iterate"
14159 | "cidr_contains" | "ip_in_cidr" | "ip_in_subnet"
14160 | "cidr_subnet" | "cidr_supernet" | "cidr_subnets" | "cidr_split"
14161 | "cidr_overlaps" | "cidr_aggregate" | "cidr_summarize"
14162 | "cidr_intersection" | "cidr_difference" | "cidr_union"
14163 | "cidr_minimum_covering" | "cidr_is_aggregable"
14164 | "cidr_next" | "cidr_prev" | "cidr_distance"
14165 | "cidr_random_ip" | "ip_random_in_cidr"
14166 | "cidr_compare" | "cidr_sort"
14167 | "mac_parse" | "mac_is_valid" | "mac_normalize" | "mac_format"
14168 | "mac_to_int" | "int_to_mac" | "mac_to_bytes" | "bytes_to_mac"
14169 | "mac_oui" | "mac_vendor_lookup" | "mac_lookup_vendor"
14170 | "mac_is_unicast" | "mac_is_multicast" | "mac_is_broadcast"
14171 | "mac_is_locally_administered" | "mac_is_universally_administered"
14172 | "mac_random" | "mac_random_local" | "mac_compare"
14173 | "eui48_to_eui64" | "eui64_to_eui48" | "eui64_from_mac"
14174 | "port_name" | "port_is_well_known" | "port_is_assigned"
14175 | "port_is_registered" | "port_is_ephemeral" | "port_is_dynamic"
14176 | "port_to_service" | "port_service_lookup"
14177 | "port_parse_range" | "port_random_ephemeral"
14178 | "ws_handshake_key" | "ws_handshake_accept"
14179 | "ws_mask" | "ws_unmask" | "ws_frame_encode" | "ws_frame_decode"
14180 | "ws_close_frame"
14181 | "cookie_parse" | "cookie_format"
14182 | "cookie_jar_new" | "cookie_jar_add" | "cookie_jar_get"
14183 | "cookie_is_session" | "cookie_is_expired"
14184 | "cookie_domain_matches" | "cookie_path_matches"
14185 | "cookie_set_max_age"
14186 | "http_method_is_idempotent" | "http_method_is_safe"
14187 | "http_method_has_body"
14188 | "http_status_class"
14189 | "http_status_is_informational" | "http_status_is_success"
14190 | "http_status_is_redirect" | "http_status_is_client_error"
14191 | "http_status_is_server_error"
14192 | "http_status_text" | "http_date_parse" | "http_date_format"
14193 | "mime_type_for_extension" | "mime_extension_for_type"
14194 | "mime_is_text" | "mime_is_image" | "mime_is_audio"
14195 | "mime_is_video" | "mime_is_application"
14196 | "bandwidth_format" | "bandwidth_parse"
14197 | "latency_ms" | "packet_loss" | "jitter_ms"
14198 | "rtt_min" | "rtt_max" | "rtt_avg"
14199 | "is_alpha_only" | "is_alphanumeric_only" | "is_numeric_only"
14201 | "is_ascii_only" | "is_printable_ascii" | "is_utf8"
14202 | "is_lowercase" | "is_uppercase" | "is_titlecase"
14203 | "is_palindrome_str"
14204 | "is_hex" | "is_octal" | "is_binary" | "is_base32"
14205 | "is_md5_hash" | "is_sha1_hash" | "is_sha256_hash"
14206 | "is_ipv6" | "is_cidr" | "is_mac"
14207 | "is_url_http" | "is_url_https"
14208 | "is_uuid_v4" | "is_uuid_v7"
14209 | "is_jwt" | "is_email_strict"
14210 | "luhn_digit" | "is_imei" | "is_imsi"
14211 | "is_vin" | "vin_decode"
14212 | "is_ean13" | "is_upc"
14213 | "is_isbn" | "isbn10_to_isbn13" | "isbn13_to_isbn10"
14214 | "iban_format" | "iban_country" | "is_bic" | "is_swift"
14215 | "is_phone" | "is_phone_e164"
14216 | "is_zip_us" | "is_zip_plus4" | "is_postal_code" | "is_ssn_us"
14217 | "semver_compare" | "semver_satisfies"
14218 | "semver_increment_major" | "semver_increment_minor" | "semver_increment_patch"
14219 | "extended_gcd" | "modinverse" | "modpow" | "modular_sqrt"
14221 | "stirling_1" | "stirling_2" | "catalan_number" | "lucas_n"
14222 | "prime_count_below" | "divisor_count" | "divisor_sum" | "sigma_divisors"
14223 | "sum_digits" | "product_digits" | "collatz_steps"
14224 | "hyperoperation" | "busy_beaver"
14225 | "quadratic_residue" | "is_quadratic_residue"
14226 | "discrete_log" | "order_modulo" | "square_free"
14227 | "perfect_number" | "abundant" | "deficient"
14228 | "random_bernoulli" | "random_normal" | "random_lognormal"
14230 | "random_exponential" | "random_poisson" | "random_gamma" | "random_beta"
14231 | "random_alphanumeric" | "random_alphabetic" | "random_password"
14232 | "random_choices_weighted"
14233 | "sample_weighted_unique" | "reservoir_sample_weighted"
14234 | "seeded_rng" | "save_random_state" | "restore_random_state"
14235 | "complex_new" | "complex_real" | "complex_imag"
14237 | "complex_polar" | "complex_from_polar"
14238 | "complex_magnitude" | "complex_abs" | "complex_phase" | "complex_angle"
14239 | "complex_conjugate"
14240 | "complex_add" | "complex_sub" | "complex_mul" | "complex_div"
14241 | "complex_pow" | "complex_sqrt" | "complex_exp" | "complex_log"
14242 | "complex_sin" | "complex_cos" | "complex_tan"
14243 | "complex_sinh" | "complex_cosh" | "complex_tanh"
14244 | "complex_equal"
14245 | "point_angle"
14246 | "line_intersect" | "line_segment_intersect" | "line_distance_point"
14247 | "polygon_signed_area" | "polygon_orientation" | "polygon_reverse"
14248 | "polygon_contains_point" | "polygon_convex"
14249 | "polygon_simplify_dp" | "polygon_convex_hull_2d"
14250 | "triangle_area" | "triangle_centroid"
14251 | "triangle_circumcircle" | "triangle_incircle"
14252 | "triangle_contains_point"
14253 | "circle_circumference" | "circle_area"
14254 | "circle_intersects_line" | "circle_intersects_circle"
14255 | "rect_area" | "rect_perimeter" | "rect_intersect"
14256 | "rect_contains_point" | "rect_union"
14257 | "ellipse_area"
14258 | "sphere_surface_area" | "cylinder_surface_area"
14259 | "cone_surface_area" | "torus_surface_area"
14260 | "srgb_to_rgb" | "rgb_to_srgb"
14261 | "rgb_to_p3" | "p3_to_rgb"
14262 | "rgb_to_adobe_rgb" | "adobe_rgb_to_rgb"
14263 | "xyz_d65_to_d50" | "xyz_d50_to_d65"
14264 | "gamma_apply" | "gamma_remove"
14265 | "white_point_d65" | "white_point_d50"
14266 | "color_temperature_to_rgb" | "rgb_to_color_temperature"
14267 | "chromatic_adaptation"
14268 | "color_interpolate_rgb" | "color_interpolate_hsl"
14269 | "color_interpolate_lab" | "color_interpolate_oklab"
14270 | "color_blend_screen"
14271 | "atan2_deg" | "atan2_quadrant"
14272 | "polar_to_cartesian" | "cartesian_to_polar"
14273 | "spherical_to_cartesian" | "cartesian_to_spherical"
14274 | "cylindrical_to_cartesian" | "cartesian_to_cylindrical"
14275 | "versine_fn"
14276 | "triples" | "n_tuples" | "peekable" | "runs" | "unique_by"
14278 | "multipeek" | "lookahead_n"
14279 | "sliding_average" | "sliding_sum" | "sliding_max" | "sliding_min"
14280 | "top_n_by" | "bottom_n_by" | "all_equal" | "take_n_random"
14281 | "unzip3" | "roundrobin" | "mode_iter" | "distinct_sample"
14282 | "ranked_choice" | "boyer_moore_majority"
14283 | "quickselect_nth" | "quickselect_median"
14284 | "top_k_min_heap" | "bottom_k_max_heap"
14285 | "unique_consecutive" | "exclude" | "exclude_first" | "exclude_last"
14286 | "weave_n" | "pad_left_n" | "pad_right_n"
14287 | "collect_into_string" | "collect_into_hashset" | "collect_into_btreeset"
14288 | "collect_into_hashmap" | "collect_into_btreemap"
14289 | "foldl1_iter" | "foldr1_iter"
14290 | "sort_by_cached_key"
14291 | "position_max" | "position_min" | "position_max_by" | "position_min_by"
14292 | "group_map"
14293 | "levenshtein_normalized" | "ratcliff_obershelp" | "match_rating"
14294 | "str_lcs" | "str_lcs_length" | "str_longest_common_substring"
14295 | "str_kmp" | "str_boyer_moore" | "str_rabin_karp"
14296 | "str_aho_corasick" | "str_z_array" | "str_suffix_array"
14297 | "str_rotations" | "str_compress_rle" | "str_decompress_rle"
14298 | "str_huffman_encode" | "str_huffman_decode"
14299 | "str_compress_lzss" | "str_decompress_lzss"
14300 | "str_isogram" | "fold_case"
14301 | "bignum_new" | "bignum_from_str" | "bignum_to_str" | "bignum_to_int"
14303 | "bignum_add" | "bignum_sub" | "bignum_mul" | "bignum_div" | "bignum_mod"
14304 | "bignum_pow" | "bignum_modpow" | "bignum_gcd" | "bignum_lcm"
14305 | "bignum_factorial" | "bignum_sqrt" | "bignum_bit_length"
14306 | "bignum_set_bit" | "bignum_clear_bit" | "bignum_test_bit"
14307 | "bignum_and" | "bignum_or" | "bignum_xor" | "bignum_not"
14308 | "bignum_shl" | "bignum_shr" | "bignum_compare"
14309 | "bignum_negate" | "bignum_abs" | "bignum_sign"
14310 | "bignum_is_zero" | "bignum_is_negative" | "bignum_is_prime"
14311 | "bignum_random"
14312 | "gravity_constant" | "physics_apply_force" | "physics_apply_impulse"
14313 | "physics_collide_aabb" | "physics_collide_sphere"
14314 | "physics_raycast" | "physics_step"
14315 | "particle_emit" | "particle_update"
14316 | "vector2_new" | "vector2_add" | "vector2_sub" | "vector2_scale"
14317 | "vector2_dot" | "vector2_cross" | "vector2_length"
14318 | "vector2_normalize" | "vector2_distance" | "vector2_rotate"
14319 | "quaternion_new" | "quaternion_from_axis_angle"
14320 | "quaternion_multiply" | "quaternion_normalize" | "quaternion_to_matrix"
14321 | "freq_to_note" | "note_to_freq" | "midi_note_to_name"
14322 | "chord_notes" | "scale_notes" | "transpose_note"
14323 | "window_tukey" | "zero_crossing_rate" | "peak_db"
14324 | "audio_normalize" | "audio_fade_in" | "audio_fade_out"
14325 | "audio_to_mono" | "audio_to_stereo"
14326 | "biquad_lowpass" | "biquad_highpass" | "biquad_bandpass" | "biquad_notch"
14327 | "oscillator_sine" | "oscillator_square"
14328 | "oscillator_sawtooth" | "oscillator_triangle"
14329 | "adsr_envelope" | "ar_envelope" | "crossfade"
14330 | "fade_curve_linear" | "fade_curve_logarithmic" | "fade_curve_exponential"
14331 | "bbox_contains" | "bbox_union" | "bbox_intersect"
14332 | "bbox_center" | "bbox_area"
14333 | "mercator_unproject" | "geohash_precision"
14334 | "jq_get" | "jq_set" | "jq_delete" | "jq_select"
14336 | "jq_keys_at" | "jq_values_at" | "jq_length_at"
14337 | "jq_type" | "jq_has" | "jq_paths" | "jq_leaf_paths"
14338 | "jq_walk" | "jq_map_values" | "jq_filter"
14339 | "jq_to_entries" | "jq_from_entries" | "jq_with_entries"
14340 | "jq_recurse" | "jq_min_by" | "jq_max_by"
14341 | "jq_sort_by" | "jq_group_by" | "jq_unique_by"
14342 | "jq_any" | "jq_all" | "jq_flatten"
14343 | "jq_index" | "jq_indices" | "jq_first" | "jq_last"
14344 | "jq_split_at" | "jq_chunks" | "jq_zip" | "jq_combinations"
14345 | "json_diff" | "json_patch" | "json_merge_patch"
14346 | "json_pointer_resolve" | "json_pointer_set"
14347 | "html_to_text" | "html_pretty" | "html_minify"
14348 | "html_sanitize" | "html_strip_tags" | "html_strip_scripts" | "html_strip_styles"
14349 | "html_extract_links" | "html_extract_images" | "html_extract_text"
14350 | "html_extract_meta" | "html_extract_title"
14351 | "html_extract_headings" | "html_extract_tables"
14352 | "html_inner_text" | "html_canonical_url"
14353 | "html_meta_charset" | "html_meta_keywords" | "html_meta_description"
14354 | "html_meta_og" | "html_meta_twitter"
14355 | "html_to_markdown" | "markdown_to_html" | "markdown_render"
14356 | "xml_pretty" | "xml_minify"
14357 | "xml_namespace" | "xml_text" | "xml_attrs"
14358 | "xml_children_by_tag" | "xml_root"
14359 | "xpath_select_one" | "xpath_attribute" | "xpath_text"
14360 | "xml_to_json" | "json_to_xml" | "xml_canonicalize"
14361 | "css_parse" | "css_minify" | "css_pretty"
14362 | "css_selector_parse" | "css_rule_extract" | "css_specificity"
14363 | "css_var_resolve" | "css_property_set" | "css_property_get"
14364 | "css_url_extract" | "css_import_extract" | "css_font_extract"
14365 | "selector_to_xpath" | "xpath_to_selector"
14366 | "http_status_continue" | "http_status_switching_protocols"
14368 | "http_status_ok" | "http_status_created" | "http_status_accepted"
14369 | "http_status_no_content" | "http_status_partial_content"
14370 | "http_status_multiple_choices" | "http_status_moved_permanently"
14371 | "http_status_found" | "http_status_see_other" | "http_status_not_modified"
14372 | "http_status_temporary_redirect" | "http_status_permanent_redirect"
14373 | "http_status_bad_request" | "http_status_unauthorized"
14374 | "http_status_payment_required" | "http_status_forbidden"
14375 | "http_status_not_found" | "http_status_method_not_allowed"
14376 | "http_status_not_acceptable" | "http_status_conflict" | "http_status_gone"
14377 | "http_status_length_required" | "http_status_precondition_failed"
14378 | "http_status_payload_too_large" | "http_status_uri_too_long"
14379 | "http_status_unsupported_media_type" | "http_status_range_not_satisfiable"
14380 | "http_status_expectation_failed" | "http_status_im_a_teapot"
14381 | "http_status_unprocessable_entity" | "http_status_too_many_requests"
14382 | "http_status_internal_server_error" | "http_status_not_implemented"
14383 | "http_status_bad_gateway" | "http_status_service_unavailable"
14384 | "http_status_gateway_timeout" | "http_status_http_version_not_supported"
14385 | "http_method_get" | "http_method_post" | "http_method_put"
14386 | "http_method_delete" | "http_method_patch" | "http_method_head"
14387 | "http_method_options" | "http_method_trace" | "http_method_connect"
14388 | "dbeta" | "qbeta" | "rbeta" | "dcauchy" | "qcauchy" | "rcauchy"
14389 | "dexp" | "qexp" | "rexp" | "dgamma" | "qgamma" | "rgamma"
14390 | "dlnorm" | "qlnorm" | "rlnorm" | "dlogis" | "qlogis" | "rlogis"
14391 | "dpois" | "qpois" | "rpois" | "dweibull" | "qweibull" | "rweibull"
14392 | "qnorm" | "rnorm" | "qunif" | "runif"
14393 | "qbinom" | "rbinom" | "qgeom" | "rgeom" | "qhyper" | "rhyper"
14394 | "qchisq" | "rchisq" | "qf" | "rf" | "qt" | "rt"
14395 | "currency_format" | "currency_parse" | "currency_round"
14397 | "currency_split_thousands" | "currency_code_to_symbol"
14398 | "currency_symbol_to_code" | "currency_convert" | "currency_rate"
14399 | "currency_iso_4217" | "currency_decimal_places"
14400 | "money_add" | "money_sub" | "money_mul" | "money_div" | "money_compare"
14401 | "tokenize_simple" | "tokenize_word" | "tokenize_subword"
14402 | "tokenize_bpe" | "tokenize_sentencepiece" | "embed_text"
14403 | "cosine_similarity" | "euclidean_distance" | "manhattan_distance"
14404 | "dot_product" | "normalize_vector"
14405 | "vector_add" | "vector_sub" | "vector_scale" | "vector_mean"
14406 | "top_k_indices" | "softmax" | "sigmoid" | "log_softmax" | "cross_entropy"
14407 | "path_canonical" | "path_relative_to" | "path_components"
14408 | "path_filename" | "path_stem" | "path_extension"
14409 | "path_join_many" | "path_with_extension" | "path_with_filename"
14410 | "path_is_subdirectory" | "path_common_ancestor" | "path_strip_prefix"
14411 | "path_glob_match_regex"
14412 | "file_mime" | "file_kind" | "file_attr_get" | "file_attr_set"
14413 | "xattr_get" | "xattr_set" | "xattr_list"
14414 | "file_chmod_string" | "file_chmod_octal" | "file_locked"
14415 | "file_acl_get" | "file_acl_set"
14416 | "locale_parse" | "locale_format" | "locale_language"
14417 | "locale_region" | "locale_script" | "locale_variant" | "locale_canonical"
14418 | "bcp47_parse" | "bcp47_format" | "bcp47_validate"
14419 | "language_tag_match" | "language_tag_subtags"
14420 | "locale_likely_subtags" | "locale_minimize" | "locale_collation"
14421 | "locale_calendar" | "locale_currency"
14422 | "locale_number_format" | "locale_date_format" | "locale_time_format"
14423 | "locale_decimal_separator" | "locale_group_separator"
14424 | "locale_first_day_of_week" | "locale_measurement_system"
14425 | "country_code_alpha2" | "country_code_alpha3" | "country_code_numeric"
14426 | "country_name" | "country_phone_prefix" | "country_currency"
14427 | "country_languages"
14428 | "language_iso_639_1" | "language_iso_639_2" | "language_iso_639_3"
14429 | "language_name"
14430 | "channel_unbounded" | "channel_bounded" | "channel_sync"
14431 | "channel_send_timeout" | "channel_recv_timeout"
14432 | "channel_try_recv" | "channel_try_send"
14433 | "channel_drain" | "channel_close" | "channel_is_closed"
14434 | "broadcast_channel_new" | "broadcast_channel_subscribe"
14435 | "broadcast_channel_publish"
14436 | "mpsc_new" | "mpmc_new" | "spmc_new" | "oneshot_new"
14437 | "mutex" | "mutex_lock" | "mutex_unlock" | "mutex_try_lock" | "mutex_is_locked"
14439 | "semaphore" | "sem"
14440 | "semaphore_acquire" | "sem_acquire"
14441 | "semaphore_release" | "sem_release"
14442 | "semaphore_try_acquire" | "sem_try_acquire"
14443 | "semaphore_permits" | "sem_permits"
14444 | "semaphore_limit" | "sem_limit"
14445 | "stress_cpu" | "scpu" | "stress_mem" | "smem"
14447 | "stress_io" | "sio" | "stress_test" | "st"
14448 | "heat" | "fire" | "fire_and_forget" | "pin"
14449 | "slurp" | "cat" | "c" | "capture" | "pager" | "pg" | "less"
14451 | "stdin"
14452 | "__stryke_rust_compile"
14454 | "vec_set_value"
14455 | "p" | "rev"
14457 | "even" | "odd" | "zero" | "nonzero"
14459 | "positive" | "pos_n" | "negative" | "neg_n"
14460 | "sign" | "negate" | "double" | "triple" | "half"
14461 | "identity" | "id"
14462 | "round" | "floor" | "ceil" | "ceiling" | "trunc" | "truncn"
14463 | "gcd" | "lcm" | "min2" | "max2"
14464 | "log2" | "log10" | "hypot"
14465 | "rad_to_deg" | "r2d" | "deg_to_rad" | "d2r"
14466 | "pow2" | "abs_diff"
14467 | "factorial" | "fact" | "fibonacci" | "fib"
14468 | "is_prime" | "is_square" | "is_power_of_two" | "is_pow2"
14469 | "cbrt" | "exp2" | "percent" | "pct" | "inverse"
14470 | "median" | "mode_val" | "variance"
14471 | "is_empty" | "is_blank" | "is_numeric"
14473 | "is_upper" | "is_lower" | "is_alpha" | "is_digit" | "is_alnum"
14474 | "is_space" | "is_whitespace"
14475 | "starts_with" | "sw" | "ends_with" | "ew" | "contains"
14476 | "capitalize" | "cap" | "swap_case" | "repeat"
14477 | "title_case" | "title" | "squish"
14478 | "pad_left" | "lpad" | "pad_right" | "rpad" | "center"
14479 | "truncate_at" | "shorten" | "reverse_str" | "rev_str"
14480 | "char_count" | "word_count" | "wc" | "line_count" | "lc_lines"
14481 | "is_array" | "is_arrayref" | "is_hash" | "is_hashref"
14483 | "is_code" | "is_coderef" | "is_ref"
14484 | "is_undef" | "is_defined" | "is_def"
14485 | "is_string" | "is_str" | "is_int" | "is_integer" | "is_float"
14486 | "invert" | "merge_hash"
14488 | "hash_map_values" | "hash_filter_keys" | "hash_filter_values"
14489 | "has_key" | "hk" | "has_any_key" | "has_all_keys"
14490 | "both" | "either" | "neither" | "xor_bool" | "bool_to_int" | "b2i"
14492 | "riffle" | "intersperse" | "every_nth"
14494 | "drop_n" | "take_n" | "rotate" | "swap_pairs"
14495 | "to_bin" | "bin_of" | "to_hex" | "hex_of" | "to_oct" | "oct_of"
14497 | "from_bin" | "from_hex" | "from_oct" | "to_base" | "from_base"
14498 | "bits_count" | "popcount" | "leading_zeros" | "lz"
14499 | "trailing_zeros" | "tz" | "bit_length" | "bitlen"
14500 | "bit_and" | "bit_or" | "bit_xor" | "bit_not"
14502 | "shift_left" | "shl" | "shift_right" | "shr"
14503 | "bit_set" | "bit_clear" | "bit_toggle" | "bit_test"
14504 | "c_to_f" | "f_to_c" | "c_to_k" | "k_to_c" | "f_to_k" | "k_to_f"
14506 | "miles_to_km" | "km_to_miles" | "miles_to_m" | "m_to_miles"
14508 | "feet_to_m" | "m_to_feet" | "inches_to_cm" | "cm_to_inches"
14509 | "yards_to_m" | "m_to_yards"
14510 | "kg_to_lbs" | "lbs_to_kg" | "g_to_oz" | "oz_to_g"
14512 | "stone_to_kg" | "kg_to_stone"
14513 | "bytes_to_kb" | "b_to_kb" | "kb_to_bytes" | "kb_to_b"
14515 | "bytes_to_mb" | "mb_to_bytes" | "bytes_to_gb" | "gb_to_bytes"
14516 | "kb_to_mb" | "mb_to_gb"
14517 | "bits_to_bytes" | "bytes_to_bits"
14518 | "seconds_to_minutes" | "s_to_m" | "minutes_to_seconds" | "m_to_s"
14520 | "seconds_to_hours" | "hours_to_seconds"
14521 | "seconds_to_days" | "days_to_seconds"
14522 | "minutes_to_hours" | "hours_to_minutes"
14523 | "hours_to_days" | "days_to_hours"
14524 | "is_leap_year" | "is_leap" | "days_in_month"
14526 | "month_name" | "month_short"
14527 | "weekday_name" | "weekday_short" | "quarter_of"
14528 | "now_ms" | "now_us" | "now_ns"
14530 | "unix_epoch" | "epoch" | "unix_epoch_ms" | "epoch_ms"
14531 | "rgb_to_hex" | "hex_to_rgb"
14533 | "ansi_red" | "ansi_green" | "ansi_yellow" | "ansi_blue"
14534 | "ansi_magenta" | "ansi_cyan" | "ansi_white" | "ansi_black"
14535 | "ansi_bold" | "ansi_dim" | "ansi_underline" | "ansi_reverse"
14536 | "strip_ansi"
14537 | "red" | "green" | "yellow" | "blue" | "magenta" | "purple" | "cyan"
14538 | "white" | "black" | "bold" | "dim" | "italic" | "underline"
14539 | "strikethrough" | "ansi_off" | "off" | "gray" | "grey"
14540 | "bright_red" | "bright_green" | "bright_yellow" | "bright_blue"
14541 | "bright_magenta" | "bright_cyan" | "bright_white"
14542 | "bg_red" | "bg_green" | "bg_yellow" | "bg_blue"
14543 | "bg_magenta" | "bg_cyan" | "bg_white" | "bg_black"
14544 | "red_bold" | "bold_red" | "green_bold" | "bold_green"
14545 | "yellow_bold" | "bold_yellow" | "blue_bold" | "bold_blue"
14546 | "magenta_bold" | "bold_magenta" | "cyan_bold" | "bold_cyan"
14547 | "white_bold" | "bold_white"
14548 | "blink" | "rapid_blink" | "hidden" | "overline"
14549 | "bg_bright_red" | "bg_bright_green" | "bg_bright_yellow" | "bg_bright_blue"
14550 | "bg_bright_magenta" | "bg_bright_cyan" | "bg_bright_white"
14551 | "rgb" | "bg_rgb" | "color256" | "c256" | "bg_color256" | "bg_c256"
14552 | "ipv4_to_int" | "int_to_ipv4"
14554 | "is_valid_ipv4" | "is_valid_ipv6" | "is_valid_email" | "is_valid_url"
14555 | "path_ext" | "path_parent" | "path_join" | "path_split"
14557 | "strip_prefix" | "strip_suffix" | "ensure_prefix" | "ensure_suffix"
14558 | "const_fn" | "always_true" | "always_false"
14560 | "flip_args" | "first_arg" | "second_arg" | "last_arg"
14561 | "count_eq" | "count_ne" | "all_eq"
14563 | "all_distinct" | "all_unique" | "has_duplicates"
14564 | "sum_of" | "product_of" | "max_of" | "min_of" | "range_of"
14565 | "quote" | "single_quote" | "unquote"
14567 | "extract_between" | "ellipsis"
14568 | "coin_flip" | "dice_roll"
14570 | "random_int" | "random_float" | "random_bool"
14571 | "random_choice" | "random_between"
14572 | "random_string" | "random_alpha" | "random_digit"
14573 | "refresh_stashes"
14575 | "os_name" | "os_arch" | "num_cpus"
14577 | "pid" | "ppid" | "uid" | "gid"
14578 | "username" | "home_dir" | "temp_dir"
14579 | "mem_total" | "mem_free" | "mem_used"
14580 | "swap_total" | "swap_free" | "swap_used"
14581 | "disk_total" | "disk_free" | "disk_avail" | "disk_used"
14582 | "load_avg" | "sys_uptime" | "page_size"
14583 | "os_version" | "os_family" | "endianness" | "pointer_width"
14584 | "proc_mem" | "rss"
14585 | "transpose" | "unzip"
14587 | "run_length_encode" | "rle" | "run_length_decode" | "rld"
14588 | "sliding_pairs" | "consecutive_eq" | "flatten_deep"
14589 | "tan" | "asin" | "acos" | "atan"
14591 | "sinh" | "cosh" | "tanh" | "asinh" | "acosh" | "atanh"
14592 | "sqr" | "cube_fn"
14593 | "mod_op" | "ceil_div" | "floor_div"
14594 | "is_finite" | "is_infinite" | "is_inf" | "is_nan"
14595 | "degrees" | "radians"
14596 | "min_abs" | "max_abs"
14597 | "saturate" | "sat01" | "wrap_around"
14598 | "rot13" | "rot47" | "caesar_shift" | "reverse_words"
14600 | "count_vowels" | "count_consonants" | "is_vowel" | "is_consonant"
14601 | "first_word" | "last_word"
14602 | "left_str" | "head_str" | "right_str" | "tail_str" | "mid_str"
14603 | "lowercase" | "uppercase"
14604 | "pascal_case" | "pc_case"
14605 | "constant_case" | "upper_snake" | "dot_case" | "path_case"
14606 | "is_palindrome" | "hamming_distance"
14607 | "longest_common_prefix" | "lcp"
14608 | "ascii_ord" | "ascii_chr" | "count_char" | "indexes_of"
14609 | "replace_first" | "replace_all_str"
14610 | "contains_any" | "contains_all"
14611 | "starts_with_any" | "ends_with_any"
14612 | "is_pair" | "is_triple"
14614 | "is_sorted" | "is_asc" | "is_sorted_desc" | "is_desc"
14615 | "is_empty_arr" | "is_empty_hash"
14616 | "is_subset" | "is_superset" | "is_permutation"
14617 | "first_eq" | "last_eq"
14619 | "index_of" | "last_index_of" | "positions_of"
14620 | "batch" | "binary_search" | "bsearch" | "linear_search" | "lsearch"
14621 | "distinct_count" | "longest" | "shortest"
14622 | "array_union" | "list_union"
14623 | "array_intersection" | "list_intersection"
14624 | "array_difference" | "list_difference"
14625 | "symmetric_diff" | "group_of_n" | "chunk_n"
14626 | "repeat_list" | "cycle_n" | "random_sample" | "sample_n"
14627 | "pick_keys" | "pick" | "omit_keys" | "omit"
14629 | "map_keys_fn" | "map_values_fn"
14630 | "hash_size" | "hash_from_pairs" | "pairs_from_hash"
14631 | "hash_eq" | "keys_sorted" | "values_sorted" | "remove_keys"
14632 | "today" | "yesterday" | "tomorrow" | "is_weekend" | "is_weekday"
14634 | "json_pretty" | "json_minify" | "escape_json" | "json_escape"
14636 | "cmd_exists" | "env_get" | "env_has" | "env_keys"
14638 | "argc" | "script_name"
14639 | "has_stdin_tty" | "has_stdout_tty" | "has_stderr_tty"
14640 | "uuid_v4" | "nanoid" | "short_id" | "is_uuid" | "token"
14642 | "email_domain" | "email_local"
14644 | "url_host" | "url_path" | "url_query" | "url_scheme"
14645 | "file_size" | "fsize" | "file_mtime" | "mtime"
14647 | "file_atime" | "atime" | "file_ctime" | "ctime"
14648 | "is_symlink" | "is_readable" | "is_writable" | "is_executable"
14649 | "path_is_abs" | "path_is_rel"
14650 | "min_max" | "percentile" | "harmonic_mean" | "geometric_mean" | "zscore"
14652 | "sorted" | "sorted_desc" | "sorted_nums" | "sorted_by_length"
14653 | "reverse_list" | "list_reverse"
14654 | "without" | "without_nth" | "take_last" | "drop_last"
14655 | "pairwise" | "zipmap"
14656 | "format_bytes" | "human_bytes"
14657 | "format_duration" | "human_duration"
14658 | "format_number" | "group_number"
14659 | "format_percent" | "pad_number"
14660 | "spaceship" | "cmp_num" | "cmp_str"
14661 | "compare_versions" | "version_cmp"
14662 | "hash_insert" | "hash_update" | "hash_delete"
14663 | "matches_regex" | "re_match"
14664 | "count_regex_matches" | "regex_extract"
14665 | "regex_split_str" | "regex_replace_str"
14666 | "shuffle_chars" | "random_char" | "nth_word"
14667 | "head_lines" | "tail_lines" | "count_substring"
14668 | "is_valid_hex" | "hex_upper" | "hex_lower"
14669 | "ms_to_s" | "s_to_ms" | "ms_to_ns" | "ns_to_ms"
14670 | "us_to_ns" | "ns_to_us"
14671 | "liters_to_gallons" | "gallons_to_liters"
14672 | "liters_to_ml" | "ml_to_liters"
14673 | "cups_to_ml" | "ml_to_cups"
14674 | "newtons_to_lbf" | "lbf_to_newtons"
14675 | "joules_to_cal" | "cal_to_joules"
14676 | "watts_to_hp" | "hp_to_watts"
14677 | "pascals_to_psi" | "psi_to_pascals"
14678 | "bar_to_pascals" | "pascals_to_bar"
14679 | "match"
14681 | "fst" | "rest" | "rst" | "second" | "snd"
14683 | "last_clj" | "lastc" | "butlast" | "bl"
14684 | "ffirst" | "ffs" | "fnext" | "fne" | "nfirst" | "nfs" | "nnext" | "nne"
14685 | "cons" | "conj"
14686 | "peek_clj" | "pkc" | "pop_clj" | "popc"
14687 | "some" | "not_any" | "not_every"
14688 | "comp" | "compose" | "partial" | "constantly" | "complement" | "compl"
14689 | "fnil" | "juxt"
14690 | "memoize" | "memo" | "curry" | "once"
14691 | "deep_clone" | "dclone" | "deep_merge" | "dmerge" | "deep_equal" | "deq"
14692 | "iterate" | "iter" | "repeatedly" | "rptd" | "cycle" | "cyc"
14693 | "mapcat" | "mcat" | "keep" | "kp" | "remove_clj" | "remc"
14694 | "reductions" | "rdcs"
14695 | "partition_by" | "pby" | "partition_all" | "pall"
14696 | "split_at" | "spat" | "split_with" | "spw"
14697 | "assoc" | "dissoc" | "get_in" | "gin" | "assoc_in" | "ain" | "update_in" | "uin"
14698 | "into" | "empty_clj" | "empc" | "seq" | "vec_clj" | "vecc"
14699 | "apply" | "appl"
14700 | "divmod" | "dm" | "accumulate" | "accum" | "starmap" | "smap"
14702 | "zip_longest" | "zipl" | "zip_fill" | "zipf" | "combinations" | "comb" | "permutations" | "perm"
14703 | "cartesian_product" | "cprod" | "compress" | "cmpr" | "filterfalse" | "falf"
14704 | "islice" | "isl" | "chain_from" | "chfr" | "pairwise_iter" | "pwi"
14705 | "tee_iter" | "teei" | "groupby_iter" | "gbi"
14706 | "each_slice" | "eslice" | "each_cons" | "econs"
14707 | "one" | "none_match" | "nonem"
14708 | "find_index_fn" | "fidx" | "rindex_fn" | "ridx"
14709 | "minmax" | "mmx" | "minmax_by" | "mmxb"
14710 | "dig" | "values_at" | "vat" | "fetch_val" | "fv" | "slice_arr" | "sla"
14711 | "transform_keys" | "tkeys" | "transform_values" | "tvals"
14712 | "sum_by" | "sumb" | "uniq_by" | "uqb"
14713 | "flat_map_fn" | "fmf" | "then_fn" | "thfn" | "times_fn" | "timf"
14714 | "step" | "upto" | "downto"
14715 | "find_last" | "fndl" | "find_last_index" | "fndli"
14717 | "at_index" | "ati" | "replace_at" | "repa"
14718 | "to_sorted" | "tsrt" | "to_reversed" | "trev" | "to_spliced" | "tspl"
14719 | "flat_depth" | "fltd" | "fill_arr" | "filla" | "includes_val" | "incv"
14720 | "object_keys" | "okeys" | "object_values" | "ovals"
14721 | "object_entries" | "oents" | "object_from_entries" | "ofents"
14722 | "span_fn" | "spanf" | "break_fn" | "brkf" | "group_runs" | "gruns"
14724 | "nub" | "sort_on" | "srton"
14725 | "intersperse_val" | "isp" | "intercalate" | "ical"
14726 | "replicate_val" | "repv" | "elem_of" | "elof" | "not_elem" | "ntelm"
14727 | "lookup_assoc" | "lkpa" | "scanl" | "scanr" | "unfoldr" | "unfr"
14728 | "find_map" | "fndm" | "filter_map" | "fltm" | "fold_right" | "fldr"
14730 | "partition_either" | "peith" | "try_fold" | "tfld"
14731 | "map_while" | "mapw" | "inspect" | "insp"
14732 | "tally_by" | "talb" | "sole" | "chunk_while" | "chkw" | "count_while" | "cntw"
14734 | "insert_at" | "insa" | "delete_at" | "dela" | "update_at" | "upda"
14736 | "split_on" | "spon" | "words_from" | "wfrm" | "unwords" | "unwds"
14737 | "lines_from" | "lfrm" | "unlines" | "unlns"
14738 | "window_n" | "winn" | "adjacent_pairs" | "adjp"
14739 | "zip_all" | "zall" | "unzip_pairs" | "uzp"
14740 | "interpose" | "ipos" | "partition_n" | "partn"
14741 | "map_indexed" | "mapi" | "reduce_indexed" | "redi" | "filter_indexed" | "flti"
14742 | "group_by_fn" | "gbf" | "index_by" | "idxb" | "associate" | "assoc_fn"
14743 | "combinations_rep" | "combrep" | "inits" | "tails" | "subsequences" | "subseqs"
14745 | "nub_by" | "nubb" | "slice_when" | "slcw" | "slice_before" | "slcb" | "slice_after" | "slca"
14746 | "each_with_object" | "ewo" | "reduce_right" | "redr"
14747 | "is_sorted_by" | "issrtb" | "intersperse_with" | "ispw"
14748 | "running_reduce" | "runred" | "windowed_circular" | "wincirc"
14749 | "distinct_by" | "distb" | "average" | "mean" | "copy_within" | "cpyw"
14750 | "and_list" | "andl" | "or_list" | "orl" | "concat_map" | "cmap"
14751 | "elem_index" | "elidx" | "elem_indices" | "elidxs" | "find_indices" | "fndidxs"
14752 | "delete_first" | "delfst" | "delete_by" | "delby" | "insert_sorted" | "inssrt"
14753 | "union_list" | "unionl" | "intersect_list" | "intl"
14754 | "maximum_by" | "maxby" | "minimum_by" | "minby" | "batched" | "btch"
14755 | "match_all" | "mall" | "capture_groups" | "capg" | "is_match" | "ism"
14757 | "split_regex" | "splre" | "replace_regex" | "replre"
14758 | "is_ascii" | "isasc" | "to_ascii" | "toasc"
14759 | "char_at" | "chat" | "code_point_at" | "cpat" | "from_code_point" | "fcp"
14760 | "normalize_spaces" | "nrmsp" | "remove_whitespace" | "rmws"
14761 | "pluralize" | "plur" | "ordinalize" | "ordn"
14762 | "parse_int" | "pint" | "parse_float" | "pflt" | "parse_bool" | "pbool"
14763 | "levenshtein" | "lev" | "soundex" | "sdx" | "similarity" | "sim"
14764 | "common_prefix" | "cpfx" | "common_suffix" | "csfx"
14765 | "wrap_text" | "wrpt" | "dedent" | "ddt" | "indent" | "idt"
14766 | "lerp" | "inv_lerp" | "ilerp" | "smoothstep" | "smst" | "remap"
14768 | "dotp" | "cross_product" | "crossp"
14769 | "matrix_mul" | "matmul" | "mm"
14770 | "magnitude" | "mag" | "normalize_vec" | "nrmv"
14771 | "distance" | "dist" | "mdist"
14772 | "covariance" | "cov" | "correlation" | "corr"
14773 | "iqr" | "quantile" | "qntl" | "quantiles" | "qntls"
14774 | "lsp_completion_words" | "lsp_words"
14775 | "doctor" | "health"
14776 | "clamp_int" | "clpi"
14777 | "in_range" | "inrng" | "wrap_range" | "wrprng"
14778 | "sum_squares" | "sumsq" | "rms" | "cumsum" | "csum" | "cumprod" | "cprod_acc" | "diff"
14779 | "add_days" | "addd" | "add_hours" | "addh" | "add_minutes" | "addm"
14781 | "diff_days" | "diffd" | "diff_hours" | "diffh"
14782 | "start_of_day" | "sod" | "end_of_day" | "eod"
14783 | "start_of_hour" | "soh" | "start_of_minute" | "som"
14784 | "urle" | "urld"
14786 | "html_encode" | "htmle" | "html_decode" | "htmld"
14787 | "adler32" | "adl32" | "fnv1a" | "djb2"
14788 | "is_credit_card" | "iscc" | "is_isbn10" | "isbn10" | "is_isbn13" | "isbn13"
14790 | "is_iban" | "isiban" | "is_hex_str" | "ishex" | "is_binary_str" | "isbin"
14791 | "is_octal_str" | "isoct" | "is_json" | "isjson" | "is_base64" | "isb64"
14792 | "is_semver" | "issv" | "is_slug" | "isslug" | "slugify" | "slug"
14793 | "mode_stat" | "mstat" | "sampn" | "weighted_sample" | "wsamp"
14795 | "shuffle_arr" | "shuf" | "argmax" | "amax" | "argmin" | "amin"
14796 | "argsort" | "asrt" | "rank" | "rnk" | "dense_rank" | "drnk"
14797 | "partition_point" | "ppt" | "lower_bound" | "lbound"
14798 | "upper_bound" | "ubound" | "equal_range" | "eqrng"
14799 | "matrix_add" | "madd" | "matrix_sub" | "msub" | "matrix_mult" | "mmult"
14801 | "matrix_scalar" | "mscal" | "matrix_identity" | "mident"
14802 | "matrix_zeros" | "mzeros" | "matrix_ones" | "mones"
14803 | "matrix_diag" | "mdiag" | "matrix_trace" | "mtrace"
14804 | "matrix_row" | "mrow" | "matrix_col" | "mcol"
14805 | "matrix_shape" | "mshape" | "matrix_det" | "mdet"
14806 | "matrix_scale" | "mat_scale" | "diagonal" | "diag"
14807 | "topological_sort" | "toposort" | "bfs_traverse" | "bfs"
14809 | "dfs_traverse" | "dfs" | "shortest_path_bfs" | "spbfs"
14810 | "connected_components_graph" | "ccgraph"
14811 | "has_cycle_graph" | "hascyc" | "is_bipartite_graph" | "isbip"
14812 | "is_ipv4_addr" | "isip4" | "is_ipv6_addr" | "isip6"
14814 | "is_mac_addr" | "ismac" | "is_port_num" | "isport"
14815 | "is_hostname_valid" | "ishost"
14816 | "is_iso_date" | "isisodt" | "is_iso_time" | "isisotm"
14817 | "is_iso_datetime" | "isisodtm"
14818 | "is_phone_num" | "isphone" | "is_us_zip" | "iszip"
14819 | "word_wrap_text" | "wwrap" | "center_text" | "ctxt"
14821 | "ljust_text" | "ljt" | "rjust_text" | "rjt" | "zfill_num" | "zfill"
14822 | "remove_all_str" | "rmall" | "replace_n_times" | "repln"
14823 | "find_all_indices" | "fndalli"
14824 | "text_between" | "txbtwn" | "text_before" | "txbef" | "text_after" | "txaft"
14825 | "text_before_last" | "txbefl" | "text_after_last" | "txaftl"
14826 | "is_even_num" | "iseven" | "is_odd_num" | "isodd"
14828 | "is_positive_num" | "ispos" | "is_negative_num" | "isneg"
14829 | "is_zero_num" | "iszero" | "is_whole_num" | "iswhole"
14830 | "log_with_base" | "logb" | "nth_root_of" | "nroot"
14831 | "frac_part" | "fracp" | "reciprocal_of" | "recip"
14832 | "copy_sign" | "cpsgn" | "fused_mul_add" | "fmadd"
14833 | "floor_mod" | "fmod" | "floor_div_op" | "fdivop"
14834 | "signum_of" | "sgnum" | "midpoint_of" | "midpt"
14835 | "longest_run" | "lrun" | "longest_increasing" | "linc"
14837 | "longest_decreasing" | "ldec" | "max_sum_subarray" | "maxsub"
14838 | "majority_element" | "majority" | "kth_largest" | "kthl"
14839 | "kth_smallest" | "kths" | "count_inversions" | "cinv"
14840 | "is_monotonic" | "ismono" | "equilibrium_index" | "eqidx"
14841 | "jaccard_index" | "jaccard" | "dice_coefficient" | "dicecoef"
14843 | "overlap_coefficient" | "overlapcoef"
14844 | "power_set" | "powerset" | "cartesian_power" | "cartpow"
14845 | "is_isogram" | "isiso" | "is_heterogram" | "ishet"
14847 | "hamdist" | "jaro_similarity" | "jarosim"
14848 | "longest_common_substring" | "lcsub"
14849 | "longest_common_subsequence" | "lcseq"
14850 | "count_words" | "wcount" | "count_lines" | "lcount"
14851 | "count_chars" | "ccount" | "count_bytes" | "bcount"
14852 | "binomial" | "binom" | "catalan" | "catn" | "pascal_row" | "pascrow"
14854 | "is_coprime" | "iscopr" | "euler_totient" | "etot"
14855 | "mobius" | "mob" | "is_squarefree" | "issqfr"
14856 | "digital_root" | "digroot" | "is_narcissistic" | "isnarc"
14857 | "is_harshad" | "isharsh" | "is_kaprekar" | "iskap"
14858 | "day_of_year" | "doy" | "week_of_year" | "woy"
14860 | "days_in_month_fn" | "daysinmo" | "is_valid_date" | "isvdate"
14861 | "age_in_years" | "ageyrs"
14862 | "when_true" | "when_false" | "if_else" | "clamp_fn"
14865 | "attempt" | "try_fn" | "safe_div" | "safe_mod" | "safe_sqrt" | "safe_log"
14866 | "juxt2" | "juxt3" | "tap_val" | "debug_val" | "converge"
14867 | "iterate_n" | "unfold" | "arity_of" | "is_callable"
14868 | "coalesce" | "default_to" | "fallback"
14869 | "apply_list" | "zip_apply" | "scan"
14870 | "keep_if" | "reject_if" | "group_consecutive"
14871 | "after_n" | "before_n" | "clamp_list" | "normalize_list"
14872
14873 | "matrix_multiply" | "mat_mul"
14877 | "identity_matrix" | "eye" | "zeros_matrix" | "zeros" | "ones_matrix" | "ones"
14878
14879
14880
14881 | "vec_normalize" | "unit_vec" | "vec_add" | "vec_sub" | "vec_scale"
14882 | "linspace" | "arange"
14883 | "re_test" | "re_find_all" | "re_groups" | "re_escape"
14885 | "re_split_limit" | "glob_to_regex" | "is_regex_valid"
14886 | "cwd" | "pwd_str" | "cpu_count" | "is_root" | "uptime_secs"
14888 | "env_pairs" | "env_set" | "env_remove" | "hostname_str" | "is_tty" | "signal_name"
14889 | "stack_new" | "queue_new" | "lru_new"
14891 | "counter" | "counter_most_common" | "defaultdict" | "ordered_set"
14892 | "bitset_new" | "bitset_set" | "bitset_test" | "bitset_clear"
14893 | "abs_ceil" | "abs_each" | "abs_floor" | "ceil_each" | "dec_each"
14895 | "double_each" | "floor_each" | "half_each" | "inc_each" | "length_each"
14896 | "negate_each" | "not_each" | "offset_each" | "reverse_each" | "round_each"
14897 | "scale_each" | "sqrt_each" | "square_each" | "to_float_each" | "to_int_each"
14898 | "trim_each" | "type_each" | "upcase_each" | "downcase_each" | "bool_each"
14899 | "avogadro" | "boltzmann" | "golden_ratio" | "gravity" | "ln10" | "ln2"
14901 | "planck" | "speed_of_light" | "sqrt2"
14902 | "bmi_calc" | "compound_interest" | "dew_point" | "discount_amount"
14904 | "force_mass_acc" | "freq_wavelength" | "future_value" | "haversine"
14905 | "heat_index" | "kinetic_energy" | "margin_price" | "markup_price"
14906 | "mortgage_payment" | "ohms_law_i" | "ohms_law_r" | "ohms_law_v"
14907 | "potential_energy" | "present_value" | "simple_interest" | "speed_distance_time"
14908 | "tax_amount" | "tip_amount" | "wavelength_freq" | "wind_chill"
14909 | "angle_between_deg" | "approx_eq" | "chebyshev_distance" | "copysign"
14911 | "cube_root" | "entropy" | "float_bits" | "fma"
14912 | "int_bits" | "jaccard_similarity" | "log_base" | "mae" | "mse" | "nth_root"
14913 | "r_squared" | "reciprocal" | "relu" | "rmse" | "rotate_point" | "round_to"
14914 | "signum" | "square_root"
14915 | "cubes_seq" | "fibonacci_seq" | "powers_of_seq" | "primes_seq"
14917 | "squares_seq" | "triangular_seq"
14918 | "alternate_case" | "angle_bracket" | "bracket" | "byte_length"
14920 | "bytes_to_hex_str" | "camel_words" | "char_length" | "chars_to_string"
14921 | "chomp_str" | "chop_str" | "filter_chars" | "from_csv_line" | "hex_to_bytes"
14922 | "insert_str" | "intersperse_char" | "ljust" | "map_chars" | "mirror_string"
14923 | "normalize_whitespace" | "only_alnum" | "only_alpha" | "only_ascii"
14924 | "only_digits" | "parenthesize" | "remove_str" | "repeat_string" | "rjust"
14925 | "sentence_case" | "string_count" | "string_sort" | "string_to_chars"
14926 | "string_unique_chars" | "substring" | "to_csv_line" | "trim_left" | "trim_right"
14927 | "xor_strings"
14928 | "adjacent_difference" | "append_elem" | "consecutive_pairs" | "contains_elem"
14930 | "count_elem" | "drop_every" | "duplicate_count" | "elem_at" | "find_first"
14931 | "first_elem" | "flatten_once" | "fold_left" | "from_digits" | "from_pairs"
14932 | "group_by_size" | "hash_from_list"
14933 | "hash_merge_deep" | "hash_to_list" | "hash_zip" | "head_n" | "histogram_bins"
14934 | "index_of_elem" | "init_list" | "interleave_lists" | "last_elem" | "least_common"
14935 | "list_compact" | "list_eq" | "list_flatten_deep" | "max_list" | "mean_list"
14936 | "min_list" | "mode_list" | "most_common" | "partition_two" | "prefix_sums"
14937 | "prepend" | "product_list" | "remove_at" | "remove_elem" | "remove_first_elem"
14938 | "repeat_elem" | "running_max" | "running_min" | "sample_one" | "scan_left"
14939 | "second_elem" | "span" | "suffix_sums" | "sum_list" | "tail_n" | "take_every"
14940 | "third_elem" | "to_array" | "to_pairs" | "trimmed_mean" | "unique_count_of"
14941 | "wrap_index" | "digits_of"
14942 | "all_match" | "any_match" | "is_between" | "is_blank_or_nil" | "is_divisible_by"
14944 | "is_email" | "is_even" | "is_falsy" | "is_fibonacci" | "is_hex_color"
14945 | "is_in_range" | "is_ipv4" | "is_multiple_of" | "is_negative" | "is_nil"
14946 | "is_nonzero" | "is_odd" | "is_perfect_square" | "is_positive" | "is_power_of"
14947 | "is_prefix" | "is_present" | "is_strictly_decreasing" | "is_strictly_increasing"
14948 | "is_suffix" | "is_triangular" | "is_truthy" | "is_url" | "is_whole" | "is_zero"
14949 | "count_digits" | "count_letters" | "count_lower" | "count_match"
14951 | "count_punctuation" | "count_spaces" | "count_upper" | "defined_count"
14952 | "empty_count" | "falsy_count" | "nonempty_count" | "numeric_count"
14953 | "truthy_count" | "undef_count"
14954 | "assert_type" | "between" | "clamp_each" | "die_if" | "die_unless"
14956 | "join_colons" | "join_commas" | "join_dashes" | "join_dots" | "join_lines"
14957 | "join_pipes" | "join_slashes" | "join_spaces" | "join_tabs" | "measure"
14958 | "max_float" | "min_float" | "noop_val" | "nop" | "pass" | "pred" | "succ"
14959 | "tap_debug" | "to_bool" | "to_float" | "to_int" | "to_string" | "void"
14960 | "range_exclusive" | "range_inclusive"
14961 | "aliquot_sum" | "autocorrelation" | "bell_number" | "cagr" | "coeff_of_variation"
14963 | "collatz_length" | "collatz_sequence" | "convolution"
14964 | "depreciation_double" | "depreciation_linear" | "discount" | "divisors"
14965 | "epsilon" | "euler_number" | "exponential_moving_average"
14966 | "f64_max" | "f64_min" | "fft_magnitude" | "goldbach" | "i64_max" | "i64_min"
14967 | "kurtosis" | "linear_regression" | "look_and_say" | "lucas" | "luhn_check"
14968 | "mean_absolute_error" | "mean_squared_error" | "median_absolute_deviation"
14969 | "minkowski_distance" | "moving_average" | "multinomial" | "neg_inf" | "npv"
14970 | "num_divisors" | "partition_number" | "pascals_triangle" | "skewness"
14971 | "standard_error" | "subfactorial" | "sum_divisors" | "totient_sum"
14972 | "tribonacci" | "weighted_mean" | "winsorize"
14973 | "chi_square_stat" | "describe" | "five_number_summary"
14975 | "gini" | "gini_coefficient" | "lorenz_curve" | "outliers_iqr"
14976 | "percentile_rank" | "quartiles" | "sample_stddev" | "sample_variance"
14977 | "spearman_correlation" | "t_test_one_sample" | "t_test_two_sample"
14978 | "z_score" | "z_scores"
14979 | "abundant_numbers" | "deficient_numbers" | "is_abundant" | "is_deficient"
14981 | "is_pentagonal" | "is_perfect" | "is_smith" | "next_prime" | "nth_prime"
14982 | "pentagonal_number" | "perfect_numbers" | "prev_prime" | "prime_factors"
14983 | "prime_pi" | "primes_up_to" | "triangular_number" | "twin_primes"
14984 | "area_circle" | "area_ellipse" | "area_rectangle" | "area_trapezoid" | "area_triangle"
14986 | "bearing" | "circumference" | "cone_volume" | "cylinder_volume" | "heron_area"
14987 | "midpoint" | "perimeter_rectangle" | "perimeter_triangle" | "point_distance"
14988 | "polygon_area" | "slope" | "sphere_surface" | "sphere_volume" | "triangle_hypotenuse"
14989 | "angle_between" | "arc_length" | "bounding_box" | "centroid"
14991 | "circle_from_three_points" | "circ3" | "convex_hull" | "ellipse_perimeter" | "ellper"
14992 | "frustum_volume" | "haversine_distance" | "line_intersection"
14993 | "point_in_polygon" | "pip" | "polygon_perimeter" | "polyper" | "pyramid_volume"
14994 | "reflect_point" | "scale_point" | "sector_area"
14995 | "torus_surface" | "torus_volume" | "translate_point"
14996 | "vector_angle" | "vector_cross" | "vector_dot" | "vector_magnitude" | "vector_normalize"
14997 | "avogadro_number" | "boltzmann_constant" | "electron_mass" | "elementary_charge"
14999 | "gravitational_constant" | "phi" | "pi" | "PI" | "planck_constant"
15000 | "proton_mass" | "sol" | "tau" | "TAU" | "E"
15001 | "bac_estimate" | "bmi" | "break_even" | "margin" | "markup" | "roi" | "tax" | "tip"
15003 | "amortization_schedule" | "black_scholes_call" | "black_scholes_put"
15005 | "bond_price" | "bond_yield" | "capm" | "continuous_compound" | "ccomp"
15006 | "discounted_payback" | "duration" | "irr"
15007 | "max_drawdown" | "mdd" | "modified_duration" | "mod_dur" | "nper" | "num_periods" | "payback_period"
15008 | "pmt" | "pv" | "rule_of_72" | "sharpe_ratio" | "sortino_ratio"
15009 | "wacc" | "xirr"
15010 | "acronym" | "atbash" | "bigrams" | "camel_to_snake" | "char_frequencies"
15012 | "chunk_string" | "collapse_whitespace" | "dedent_text" | "indent_text"
15013 | "initials" | "leetspeak" | "mask_string" | "ngrams" | "pig_latin"
15014 | "remove_consonants" | "remove_vowels" | "reverse_each_word" | "snake_to_camel"
15015 | "sort_words" | "string_distance" | "string_multiply" | "strip_html"
15016 | "trigrams" | "unique_words" | "word_frequencies" | "zalgo"
15017 | "braille_encode" | "double_metaphone" | "metaphone" | "morse_decode"
15019 | "morse_encode" | "nato_phonetic" | "phonetic_digit" | "subscript" | "superscript"
15020 | "to_emoji_num"
15021 | "int_to_roman" | "roman_add" | "roman_numeral_list" | "roman_to_int"
15023 | "base_convert" | "binary_to_gray" | "gray_code_sequence" | "gray_to_binary"
15025 | "ansi_256" | "ansi_truecolor" | "color_blend" | "color_complement"
15027 | "color_darken" | "color_distance" | "color_grayscale" | "color_invert"
15028 | "color_lighten" | "hsl_to_rgb" | "hsv_to_rgb" | "random_color"
15029 | "rgb_to_hsl" | "rgb_to_hsv"
15030 | "matrix_flatten" | "matrix_from_rows" | "matrix_hadamard" | "matrix_inverse"
15032 | "matrix_map" | "matrix_max" | "matrix_min" | "matrix_power" | "matrix_sum"
15033 | "matrix_transpose"
15034 | "binary_insert" | "bucket" | "clamp_array" | "group_consecutive_by"
15036 | "histogram" | "merge_sorted" | "next_permutation" | "normalize_array"
15037 | "normalize_range" | "peak_detect" | "range_compress" | "range_expand"
15038 | "reservoir_sample" | "run_length_decode_str" | "run_length_encode_str"
15039 | "zero_crossings"
15040 | "apply_window" | "bandpass_filter" | "cross_correlation" | "dft"
15042 | "downsample" | "decimate" | "energy" | "envelope" | "hilbert_env" | "highpass_filter" | "idft"
15043 | "lowpass_filter" | "median_filter" | "normalize_signal" | "phase_spectrum"
15044 | "power_spectrum" | "psd" | "resample" | "spectral_centroid" | "spectrogram" | "stft" | "upsample" | "interpolate"
15045 | "window_blackman" | "window_hamming" | "window_hann" | "window_kaiser"
15046 | "is_anagram" | "is_balanced_parens" | "is_control" | "is_numeric_string"
15048 | "is_pangram" | "is_printable" | "is_valid_cidr" | "is_valid_cron"
15049 | "is_valid_hex_color" | "is_valid_latitude" | "is_valid_longitude" | "is_valid_mime"
15050 | "eval_rpn" | "fizzbuzz" | "game_of_life_step" | "mandelbrot_char"
15052 | "sierpinski" | "tower_of_hanoi" | "truth_table"
15053 | "byte_size" | "degrees_to_compass" | "to_string_val" | "type_of"
15055 | "quadratic_roots" | "quadratic_discriminant" | "arithmetic_series"
15057 | "geometric_series" | "stirling_approx"
15058 | "double_factorial" | "rising_factorial" | "falling_factorial"
15059 | "gamma_approx" | "erf_approx" | "normal_pdf" | "normal_cdf"
15060 | "poisson_pmf" | "exponential_pdf" | "inverse_lerp"
15061 | "map_range"
15062 | "momentum" | "impulse" | "work" | "power_phys" | "torque" | "angular_velocity"
15064 | "centripetal_force" | "escape_velocity" | "orbital_velocity" | "orbital_period"
15065 | "gravitational_force" | "coulomb_force" | "electric_field" | "capacitance"
15066 | "capacitor_energy" | "inductor_energy" | "resonant_frequency"
15067 | "rc_time_constant" | "rl_time_constant" | "impedance_rlc"
15068 | "relativistic_mass" | "lorentz_factor" | "time_dilation" | "length_contraction"
15069 | "relativistic_energy" | "rest_energy" | "de_broglie_wavelength"
15070 | "photon_energy" | "photon_energy_wavelength" | "schwarzschild_radius"
15071 | "stefan_boltzmann" | "wien_displacement" | "ideal_gas_pressure" | "ideal_gas_volume"
15072 | "projectile_range" | "projectile_max_height" | "projectile_time"
15073 | "spring_force" | "spring_energy" | "pendulum_period" | "doppler_frequency"
15074 | "decibel_ratio" | "snells_law" | "brewster_angle" | "critical_angle"
15075 | "lens_power" | "thin_lens" | "magnification_lens"
15076 | "euler_mascheroni" | "apery_constant" | "feigenbaum_delta" | "feigenbaum_alpha"
15078 | "catalan_constant" | "khinchin_constant" | "glaisher_constant"
15079 | "plastic_number" | "silver_ratio" | "supergolden_ratio"
15080 | "vacuum_permittivity" | "vacuum_permeability" | "coulomb_constant"
15082 | "fine_structure_constant" | "rydberg_constant" | "bohr_radius"
15083 | "bohr_magneton" | "nuclear_magneton" | "stefan_boltzmann_constant"
15084 | "wien_constant" | "gas_constant" | "faraday_constant" | "neutron_mass"
15085 | "atomic_mass_unit" | "earth_mass" | "earth_radius" | "sun_mass" | "sun_radius"
15086 | "astronomical_unit" | "light_year" | "parsec" | "hubble_constant"
15087 | "planck_length" | "planck_time" | "planck_mass" | "planck_temperature"
15088 | "matrix_solve" | "msolve" | "solve"
15090 | "matrix_lu" | "mlu" | "matrix_qr" | "mqr"
15091 | "matrix_eigenvalues" | "meig" | "eigenvalues" | "eig"
15092 | "matrix_norm" | "mnorm" | "matrix_cond" | "mcond" | "cond"
15093 | "matrix_pinv" | "mpinv" | "pinv"
15094 | "matrix_cholesky" | "mchol" | "cholesky"
15095 | "matrix_det_general" | "mdetg" | "det"
15096 | "welch_ttest" | "welcht" | "paired_ttest" | "pairedt"
15098 | "cohen_d" | "cohend" | "anova_oneway" | "anova" | "anova1"
15099 | "spearman_corr" | "rho" | "kendall_tau" | "kendall" | "ktau"
15100 | "confidence_interval" | "ci"
15101 | "beta_pdf" | "betapdf" | "gamma_pdf" | "gammapdf"
15103 | "chi2_pdf" | "chi2pdf" | "chi_squared_pdf"
15104 | "t_pdf" | "tpdf" | "student_pdf"
15105 | "f_pdf" | "fpdf" | "fisher_pdf"
15106 | "lognormal_pdf" | "lnormpdf" | "weibull_pdf" | "weibpdf"
15107 | "cauchy_pdf" | "cauchypdf" | "laplace_pdf" | "laplacepdf"
15108 | "pareto_pdf" | "paretopdf"
15109 | "lagrange_interp" | "lagrange" | "linterp"
15111 | "cubic_spline" | "cspline" | "spline"
15112 | "poly_eval" | "polyval" | "polynomial_fit" | "polyfit"
15113 | "trapz" | "trapezoid" | "simpson" | "simps"
15115 | "numerical_diff" | "numdiff" | "diff_array"
15116 | "cumtrapz" | "cumulative_trapz"
15117 | "bisection" | "bisect" | "newton_method" | "newton" | "newton_raphson"
15119 | "golden_section" | "golden" | "gss"
15120 | "rk4" | "runge_kutta" | "rk4_ode" | "euler_ode" | "euler_method"
15122 | "dijkstra" | "shortest_path" | "bellman_ford" | "bellmanford"
15124 | "floyd_warshall" | "floydwarshall" | "apsp"
15125 | "prim_mst" | "mst" | "prim"
15126 | "cot" | "sec" | "csc" | "acot" | "asec" | "acsc" | "sinc" | "versin" | "versine"
15128 | "leaky_relu" | "lrelu" | "elu" | "selu" | "gelu"
15130 | "silu" | "swish" | "mish" | "softplus"
15131 | "hard_sigmoid" | "hardsigmoid" | "hard_swish" | "hardswish"
15132 | "bessel_j0" | "j0" | "bessel_j1" | "j1"
15134 | "lambert_w" | "lambertw" | "productlog"
15135 | "bessel_j" | "bessel_y" | "bessel_i" | "bessel_k"
15137 | "hankel_h1" | "hankel_h2" | "bessel_j_zero"
15138 | "airy_ai" | "airy_bi" | "airy_ai_prime" | "airy_bi_prime"
15139 | "spherical_bessel_j" | "spherical_bessel_y"
15140 | "struve_h" | "struve_l" | "kelvin_ber" | "kelvin_bei"
15141 | "legendre_p" | "legendre_q" | "assoc_legendre_p"
15143 | "hermite_h" | "hermite_he" | "laguerre_l" | "assoc_laguerre_l"
15144 | "jacobi_p" | "gegenbauer_c" | "chebyshev_t" | "chebyshev_u"
15145 | "spherical_harmonic_y" | "zernike_r"
15146 | "elliptic_k" | "elliptic_e" | "elliptic_pi" | "elliptic_f"
15148 | "elliptic_e_inc" | "elliptic_pi_inc"
15149 | "carlson_rf" | "carlson_rd" | "carlson_rj"
15150 | "jacobi_sn" | "jacobi_cn" | "jacobi_dn" | "jacobi_am"
15151 | "elliptic_theta"
15152 | "weierstrass_p" | "weierstrass_zeta" | "weierstrass_sigma"
15153 | "zeta" | "riemann_zeta" | "hurwitz_zeta"
15155 | "polylog" | "dilog" | "lerch_phi"
15156 | "riemann_siegel_z" | "riemann_siegel_theta"
15157 | "dirichlet_eta" | "dirichlet_beta"
15158 | "hypergeometric_2f1" | "hyper_2f1"
15160 | "hypergeometric_1f1" | "hyper_1f1" | "kummer_m"
15161 | "hypergeometric_0f1" | "hyper_0f1"
15162 | "hypergeometric_pfq" | "hyper_pfq"
15163 | "hypergeometric_u" | "tricomi_u"
15164 | "dedekind_eta" | "klein_j" | "klein_invariant_j"
15166 | "modular_lambda" | "ramanujan_tau"
15167 | "sin_integral" | "si_int" | "cos_integral" | "ci_int"
15169 | "sinh_integral" | "shi_int" | "cosh_integral" | "chi_int"
15170 | "exp_integral_e" | "ei_n" | "exp_integral_ei" | "ei_int"
15171 | "log_integral" | "li_int" | "fresnel_s" | "fresnel_c"
15172 | "jacobi_symbol" | "kronecker_symbol"
15174 | "primitive_root" | "multiplicative_order"
15175 | "mangoldt_lambda" | "von_mangoldt" | "carmichael_lambda"
15176 | "squares_r" | "thue_morse" | "rudin_shapiro"
15177 | "farey_sequence" | "farey"
15178 | "frobenius_number" | "frobenius_solve" | "stern_brocot"
15179 | "stirling_s1" | "stirling_first" | "bell_polynomial_b" | "bell_y"
15181 | "clebsch_gordan" | "three_j_symbol" | "wigner_3j"
15182 | "six_j_symbol" | "wigner_6j" | "nine_j_symbol" | "wigner_9j"
15183 | "debruijn_sequence" | "debruijn" | "wigner_d"
15184 | "q_pochhammer" | "q_factorial" | "q_binomial"
15186 | "q_hypergeometric_pfq"
15187 | "mittag_leffler_e" | "mittag_leffler"
15188 | "coulomb_wave_f" | "coulomb_wave_g"
15189 | "inverse_erf" | "erfinv" | "inverse_erfc" | "erfcinv"
15191 | "inverse_gamma_regularized" | "gamma_lr_inv"
15192 | "inverse_beta_regularized" | "beta_reg_inv"
15193 | "inverse_jacobi_sn"
15194 | "dirac_delta" | "heaviside_theta" | "heaviside"
15196 | "unit_box" | "unit_triangle"
15197 | "square_wave" | "triangle_wave" | "sawtooth_wave" | "dirac_comb"
15198 | "liouville_lambda" | "jordan_totient" | "ramanujan_sum"
15200 | "cyclotomic_polynomial" | "cyclotomic" | "legendre_symbol"
15201 | "pythagorean_triple_q" | "gen_pythagorean_triple"
15202 | "sophie_germain_q" | "mersenne_q"
15203 | "lucas_lehmer_test" | "lucas_lehmer"
15204 | "continued_fraction" | "from_continued_fraction" | "convergents"
15205 | "best_rational_approximation" | "best_rational"
15206 | "motzkin_number" | "motzkin"
15208 | "narayana_number" | "narayana"
15209 | "delannoy_number" | "delannoy"
15210 | "schroder_number" | "schroder" | "large_schroder"
15211 | "small_schroder_number" | "small_schroder"
15212 | "eulerian_number"
15213 | "bernoulli_polynomial" | "euler_polynomial"
15214 | "pell_number" | "pell" | "pell_lucas_number" | "pell_lucas"
15215 | "perrin_number" | "perrin" | "padovan_number" | "padovan"
15216 | "kronecker_product" | "tensor_product" | "tensor_contract"
15218 | "matrix_rank" | "mrank"
15219 | "companion_matrix" | "companion"
15220 | "characteristic_polynomial" | "charpoly"
15221 | "singular_values" | "svals"
15222 | "nullspace" | "null_space" | "kernel"
15223 | "polynomial_gcd" | "polygcd"
15225 | "polynomial_quotient" | "polyquot"
15226 | "polynomial_remainder" | "polyrem"
15227 | "polynomial_resultant" | "resultant"
15228 | "polynomial_discriminant" | "discriminant"
15229 | "polynomial_roots" | "polyroots"
15230 | "gumbel_pdf" | "gumbel_cdf" | "gumbel_quantile"
15232 | "frechet_pdf" | "frechet_cdf" | "frechet_quantile"
15233 | "logistic_pdf" | "logistic_cdf" | "logistic_quantile"
15234 | "rayleigh_pdf" | "rayleigh_cdf" | "rayleigh_quantile"
15235 | "inverse_gamma_pdf" | "inverse_gamma_cdf" | "inverse_gamma_quantile"
15236 | "kumaraswamy_pdf" | "kumaraswamy_cdf" | "kumaraswamy_quantile"
15237 | "mathieu_a" | "mathieu_characteristic_a"
15239 | "mathieu_ce" | "mathieu_se"
15240 | "heun_g"
15242 | "haar_transform" | "haar" | "haar_inverse" | "ihaar"
15244 | "daubechies_db4" | "db4" | "daubechies_db4_inverse" | "idb4"
15245 | "topo_sort_adj"
15247 | "scc_tarjan" | "tarjan_scc" | "strongly_connected"
15248 | "bipartite_q" | "is_bipartite"
15249 | "max_flow_edmonds_karp" | "max_flow" | "edmonds_karp"
15250 | "min_cut" | "eccentricity"
15251 | "graph_diameter" | "graph_radius"
15252 | "stieltjes_constant" | "stieltjes"
15254 | "gauss_sum" | "kloosterman_sum"
15255 | "eta_quotient" | "root_approximant"
15256 | "numerical_gradient" | "ngrad"
15258 | "numerical_jacobian" | "njac"
15259 | "numerical_hessian" | "nhess"
15260 | "numerical_divergence" | "ndiv"
15261 | "numerical_curl" | "ncurl"
15262 | "numerical_laplacian" | "nlap"
15263 | "nelder_mead" | "simplex_min"
15265 | "gradient_descent" | "gd_min"
15266 | "bfgs_minimize" | "bfgs"
15267 | "levenberg_marquardt" | "lev_marq" | "lm_min"
15268 | "conjugate_gradient" | "cg_solve"
15269 | "least_squares" | "lstsq"
15270 | "romberg" | "romberg_int"
15272 | "gauss_legendre_quad" | "glquad" | "gl_quad"
15273 | "monte_carlo_integrate" | "mc_int"
15274 | "adaptive_simpson" | "asimp"
15275 | "lu_decompose" | "ludec"
15277 | "qr_decompose" | "qrdec"
15278 | "householder_reflector" | "householder"
15279 | "givens_rotation" | "givens"
15280 | "forward_substitute" | "fwdsub"
15281 | "back_substitute" | "backsub"
15282 | "hessenberg_reduce" | "hessen"
15283 | "poly_derivative" | "polyder"
15285 | "poly_integrate" | "polyint"
15286 | "poly_compose" | "poly_eval_horner" | "horner"
15287 | "pade_approximant" | "pade"
15288 | "quat_mul" | "quat_conj" | "quat_norm" | "quat_inv"
15290 | "quat_from_axis_angle" | "axis_angle_to_quat"
15291 | "quat_to_axis_angle"
15292 | "quat_to_matrix" | "quat_from_matrix" | "matrix_to_quat"
15293 | "quat_slerp" | "slerp"
15294 | "euler_zyx_to_matrix" | "matrix_to_euler_zyx"
15295 | "rotate_3d_vec"
15296 | "kl_divergence" | "kl_div"
15298 | "js_divergence" | "js_div"
15299 | "mutual_information" | "mi"
15300 | "cross_entropy_arr" | "cross_entropy_dist"
15301 | "renyi_entropy" | "tsallis_entropy"
15302 | "pauli_x" | "pauli_y" | "pauli_z"
15304 | "pauli_id" | "pauli_i" | "pauli_identity"
15305 | "ket_bra" | "density_matrix" | "expectation_value" | "expval"
15306 | "commutator" | "anticommutator"
15307 | "partial_trace" | "ptrace"
15308 | "von_neumann_entropy" | "vn_entropy"
15309 | "bose_einstein" | "fermi_dirac"
15311 | "maxwell_boltzmann_speed" | "mb_speed"
15312 | "partition_function" | "z_partition"
15313 | "helmholtz_free_energy" | "free_energy_f"
15314 | "boltzmann_factor"
15315 | "einstein_specific_heat" | "einstein_cv"
15316 | "fresnel_reflection_te" | "fresnel_reflection_tm"
15318 | "fresnel_transmission_te" | "fresnel_transmission_tm"
15319 | "abcd_thin_lens" | "abcd_free_space"
15320 | "gaussian_beam_q"
15321 | "kepler_solve"
15323 | "true_to_eccentric" | "eccentric_to_mean"
15324 | "julian_date" | "j_date"
15325 | "jd_to_gregorian" | "jd_to_date"
15326 | "sidereal_time_gmst" | "gmst"
15327 | "vis_viva" | "orbital_period_kepler"
15328 | "orbital_elements_to_state" | "elem_to_state"
15329 | "kalman_step" | "kalman_filter"
15331 | "exponential_smoothing" | "exp_smooth"
15332 | "holt_winters" | "arma_yw_fit" | "ar_yw"
15333 | "pagerank" | "betweenness_centrality" | "closeness_centrality"
15335 | "eigenvector_centrality" | "degree_centrality" | "triangle_count"
15336 | "rgumbel" | "rfrechet" | "rrayleigh"
15338 | "rlogistic" | "rkumaraswamy" | "rinverse_gamma" | "rinvgamma"
15339 | "graham_scan" | "convex_hull_2d"
15341 | "line_line_intersect_2d" | "ll_intersect_2d"
15342 | "point_segment_distance" | "p_seg_dist"
15343 | "forward_diff" | "fdiff"
15345 | "forward_diff_grad" | "fdiff_grad"
15346 | "bartlett_test" | "levene_test"
15348 | "fishers_exact_test_2x2" | "fishers_exact"
15349 | "mcnemar_test"
15350 | "runs_test" | "wald_wolfowitz"
15351 | "friedman_test" | "kruskal_wallis_test" | "kruskal"
15352 | "sign_test"
15353 | "anderson_darling_normality" | "ad_normality"
15354 | "jarque_bera_test" | "jb_test"
15355 | "ljung_box_test" | "ljung_box"
15356 | "durbin_watson_stat" | "durbin_watson"
15357 | "mahalanobis_distance" | "mahalanobis_dist"
15359 | "cosine_distance" | "canberra_distance"
15360 | "bray_curtis_distance" | "bray_curtis"
15361 | "l1_distance"
15362 | "chi_squared_distance"
15363 | "multivariate_normal_pdf" | "mvn_pdf"
15365 | "multivariate_normal_sample" | "rmvn"
15366 | "dirichlet_pdf" | "dirichlet_sample" | "rdirichlet"
15367 | "skellam_pmf"
15368 | "inverse_gaussian_pdf" | "wald_pdf"
15369 | "inverse_gaussian_cdf" | "wald_cdf"
15370 | "inverse_gaussian_sample" | "rwald"
15371 | "non_central_chi2_pdf" | "ncchi2_pdf"
15372 | "matrix_exp" | "expm" | "matrix_log" | "logm"
15374 | "matrix_sqrt" | "sqrtm" | "matrix_sin" | "sinm"
15375 | "matrix_cos" | "cosm"
15376 | "rk45_dormand_prince" | "rk45" | "dopri5"
15378 | "midpoint_step" | "ode_midpoint"
15379 | "heun_step" | "ode_heun"
15380 | "verlet_step" | "ode_verlet"
15381 | "logistic_regression" | "logit_fit"
15383 | "poisson_regression"
15384 | "ridge_regression" | "ridge"
15385 | "lasso_coord" | "lasso"
15386 | "bootstrap_mean_ci" | "boot_mean_ci"
15388 | "jackknife_estimate" | "jackknife"
15389 | "permutation_test_diff" | "perm_test_diff"
15390 | "acf_at_lag" | "diff_op" | "lag_op"
15392 | "decompose_classical" | "decompose_ts"
15393 | "combinations_list" | "permutations_list"
15395 | "cyclic_permutations" | "subsets_of_size"
15396 | "longest_increasing_subseq" | "lis"
15398 | "knapsack_01" | "knapsack"
15399 | "subset_sum_target" | "subset_sum"
15400 | "coin_change_min" | "coin_change_minimum"
15401 | "edit_distance_levenshtein" | "edit_distance"
15402 | "one_hot_encode" | "onehot" | "label_encode"
15404 | "categorical_cross_entropy" | "cce"
15405 | "classification_metrics" | "binary_metrics"
15406 | "roc_auc" | "auroc"
15407 | "gaussian_blur_kernel" | "sobel_x" | "sobel_y"
15409 | "prewitt_x" | "prewitt_y"
15410 | "laplacian_of_gaussian" | "log_kernel"
15411 | "brownian_path" | "wiener_path"
15413 | "geometric_brownian_path" | "gbm_path"
15414 | "poisson_process" | "random_walk_1d"
15415 | "lempel_ziv_complexity" | "lz_complexity"
15417 | "huffman_code_lengths" | "huffman"
15418 | "shannon_entropy_rate" | "block_entropy_rate"
15419 | "planck_blackbody" | "blackbody"
15421 | "rayleigh_jeans" | "compton_shift"
15422 | "rydberg_energy"
15423 | "hydrogen_radial_wavefunction" | "h_rad_psi"
15424 | "integer_log" | "ilog"
15426 | "aks_primality" | "aks"
15427 | "elliptic_curve_add" | "ec_add"
15428 | "berlekamp_massey" | "bm_lfsr"
15429 | "bezout_coefficients" | "bezout" | "extended_euclid"
15430 | "factor_quadratic" | "complete_square"
15432 | "partial_fraction_simple" | "partial_fraction"
15433 | "gauss_chebyshev_quad" | "gc_quad"
15435 | "gauss_hermite_quad" | "gh_quad"
15436 | "gauss_laguerre_quad" | "glag_quad"
15437 | "clenshaw_curtis_quad" | "cc_quad"
15438 | "tanh_sinh_quad" | "ts_quad"
15439 | "gauss_legendre_2d" | "gl_2d"
15440 | "monte_carlo_2d" | "mc_2d"
15441 | "simulated_annealing" | "sa_min"
15443 | "simplex_lp" | "lp_simplex"
15444 | "particle_swarm" | "pso_min"
15445 | "gev_pdf" | "gev_cdf" | "gev_sample" | "rgev"
15447 | "gen_pareto_pdf" | "gen_pareto_cdf"
15448 | "gen_pareto_sample" | "rgenpareto"
15449 | "skew_normal_pdf" | "skew_normal_cdf"
15450 | "mixture_normal_pdf"
15451 | "categorical_sample" | "rcat"
15452 | "multinomial_pmf" | "multinomial_sample" | "rmultinom"
15453 | "truncated_normal_pdf"
15454 | "truncated_normal_sample" | "rtnorm"
15455 | "dbscan" | "gmm_em_1d" | "gmm_1d"
15457 | "silhouette_score"
15458 | "davies_bouldin_index" | "db_index"
15459 | "calinski_harabasz_index" | "ch_index"
15460 | "mds_2d" | "pcoa_2d" | "mean_shift"
15461 | "batch_norm" | "layer_norm"
15463 | "dropout_mask"
15464 | "max_pool_1d" | "avg_pool_1d"
15465 | "attention_softmax" | "positional_encoding"
15466 | "glorot_init" | "xavier_init"
15467 | "he_init" | "kaiming_init"
15468 | "adam_step" | "rmsprop_step"
15469 | "ewma" | "ccf" | "periodogram"
15471 | "welch_psd" | "welch"
15472 | "lag_features"
15473 | "median_filter_2d"
15475 | "threshold_otsu" | "otsu"
15476 | "histogram_equalize" | "hist_eq"
15477 | "erode_2d" | "dilate_2d"
15478 | "mse_loss" | "mae_loss" | "huber_loss"
15480 | "vincenty_distance" | "vincenty"
15482 | "mercator_project"
15483 | "destination_from_bearing" | "dest_bearing"
15484 | "recaman" | "recaman_seq"
15486 | "sylvester" | "sylvester_seq"
15487 | "happy_q" | "is_happy"
15488 | "amicable_pair_q"
15489 | "aliquot_sequence"
15490 | "magic_constant"
15491 | "clustering_coefficient_local" | "cc_local"
15493 | "clustering_coefficient_global" | "cc_global"
15494 | "assortativity" | "common_neighbors" | "jaccard_neighbors"
15495 | "adamic_adar"
15496 | "preferential_attachment_score" | "pa_score"
15497 | "triangle_3d_normal" | "triangle_3d_area"
15499 | "tetrahedron_volume"
15500 | "plane_from_3_points" | "plane_from_pts"
15501 | "point_to_plane_distance" | "pt_plane_dist"
15502 | "ray_triangle_intersect" | "moller_trumbore"
15503 | "ray_sphere_intersect" | "aabb_overlap"
15504 | "gauss_seidel"
15506 | "jacobi_iteration" | "jacobi_solve"
15507 | "sor_solve" | "sor"
15508 | "thomas_tridiag_solve" | "thomas"
15509 | "richardson_extrapolation" | "richardson"
15510 | "finite_difference_5pt" | "fd5pt"
15511 | "tonelli_shanks_sqrt" | "tonelli_shanks"
15513 | "baby_step_giant_step" | "bsgs"
15514 | "pollard_rho_factor" | "pollard_rho"
15515 | "modular_lcm" | "mlcm"
15516 | "crt_general" | "crt_arbitrary"
15517 | "van_der_waals_p" | "vdw_pressure"
15519 | "nernst_equation" | "nernst"
15520 | "arrhenius_rate" | "arrhenius"
15521 | "reduced_mass"
15522 | "ph_to_concentration" | "ph_to_h"
15523 | "metropolis_hastings" | "mh_sampler"
15525 | "gibbs_sampler_step" | "gibbs_step"
15526 | "euler_maruyama" | "em_sde"
15527 | "milstein" | "milstein_sde"
15528 | "ornstein_uhlenbeck_path" | "ou_path"
15529 | "hmm_forward" | "hmm_viterbi" | "hmm_backward"
15530 | "kaplan_meier" | "km_estimator" | "log_rank_test"
15532 | "needleman_wunsch" | "nw_align"
15533 | "smith_waterman" | "sw_align"
15534 | "gibbs_free_energy" | "delta_g"
15536 | "henderson_hasselbalch" | "hh_eq"
15537 | "radioactive_decay"
15538 | "half_life_to_constant" | "hl_to_lambda"
15539 | "pid_step"
15541 | "transfer_function_eval" | "tf_eval"
15542 | "bode_magnitude_db" | "bode_mag_db"
15543 | "bode_phase_deg"
15544 | "lqr_2x2"
15545 | "nash_eq_2x2" | "nash_2x2"
15547 | "shapley_value" | "expected_utility"
15548 | "hungarian_assignment" | "hungarian"
15550 | "tsp_nearest_neighbor" | "tsp_nn"
15551 | "vertex_cover_2approx" | "vc_2approx"
15552 | "heat_eq_1d" | "wave_eq_1d"
15554 | "laplace_2d_jacobi" | "laplace_jacobi"
15555 | "beta_binomial_update"
15557 | "normal_normal_update"
15558 | "gamma_poisson_update"
15559 | "dirichlet_multinomial_update"
15560 | "hadamard_gate" | "h_gate"
15562 | "cnot_gate" | "cx_gate"
15563 | "swap_gate" | "cz_gate"
15564 | "qft_matrix" | "phase_gate"
15565 | "s_gate" | "t_gate"
15566 | "bezier_eval"
15568 | "catmull_rom_eval" | "cmr_eval"
15569 | "cubic_hermite_eval" | "ch_eval"
15570 | "bspline_basis" | "nik_basis"
15571 | "freq_to_midi" | "midi_to_freq"
15573 | "equal_temperament_freq"
15574 | "cents_difference" | "cents_diff"
15575 | "redshift_z" | "hubble_distance" | "luminosity_distance"
15577 | "reynolds_number" | "mach_number"
15579 | "prandtl_number" | "bernoulli_velocity"
15580 | "negative_binomial_pmf" | "nb_pmf"
15582 | "hypergeometric_pmf"
15583 | "beta_binomial_pmf" | "bb_pmf"
15584 | "von_mises_pdf" | "vmf_pdf"
15585 | "erdos_renyi_random" | "erdos_renyi"
15587 | "barabasi_albert_random" | "barabasi_albert"
15588 | "watts_strogatz_random" | "watts_strogatz"
15589 | "rgb_to_lab" | "lab_to_rgb"
15591 | "kelvin_to_rgb" | "color_temp_rgb"
15592 | "bell_triangle" | "surjection_count"
15594 | "distinct_partition_count" | "q_partition"
15595 | "fibonacci_q" | "is_fib_number"
15596 | "bonferroni_correction" | "bonferroni"
15598 | "benjamini_hochberg" | "bh_fdr"
15599 | "tukey_hsd"
15600 | "hellinger_distance"
15601 | "wasserstein_1d" | "earth_movers_1d"
15602 | "chi_squared_divergence"
15603 | "beta_geometric_pmf"
15604 | "generalized_gamma_pdf" | "gengamma_pdf"
15605 | "zip_pmf" | "zero_inflated_poisson_pmf"
15606 | "stefan_boltzmann_luminosity" | "stellar_luminosity"
15607 | "photon_momentum" | "photon_energy_ev"
15608 | "dipole_radiation_power" | "larmor_power"
15609 | "parallax_to_distance" | "hawking_temperature"
15610 | "roche_limit" | "apparent_magnitude" | "distance_modulus"
15611 | "beer_lambert" | "absorbance"
15612 | "rate_law_n"
15613 | "freezing_point_depression" | "fpd"
15614 | "mixed_nash_2x2" | "minimax_2x2"
15615 | "barycentric_coords_2d" | "barycentric_2d"
15617 | "bresenham_line" | "bilinear_interp_2d"
15618 | "point_in_polygon_2d"
15619 | "hilbert_transform" | "cepstrum"
15620 | "butterworth_lowpass_coeffs" | "butter_lp"
15621 | "savitzky_golay_coeffs" | "sg_coeffs"
15622 | "savitzky_golay_filter" | "sg_filter"
15623 | "canny_edge_intensity" | "canny_intensity"
15624 | "bilateral_filter_basic" | "bilateral_filter"
15625 | "kmeans_pp_init" | "kpp_init"
15626 | "elbow_score" | "wcss"
15627 | "young_tableaux_count" | "syt_count"
15628 | "euler_alt_permutation" | "euler_zigzag"
15629 | "genocchi_number" | "lattice_paths_count"
15630 | "tetration"
15631 | "ackermann_limited" | "ackermann"
15632 | "perfect_power_q" | "b_smooth_q"
15633 | "k_core"
15635 | "rich_club_coefficient" | "rich_club"
15636 | "rsa_basic_encrypt" | "rsa_enc_int"
15637 | "rsa_basic_decrypt" | "rsa_dec_int"
15638 | "dh_shared_secret"
15639 | "bell_state_phi_plus" | "bell_phi_plus"
15640 | "bell_state_psi_minus" | "bell_psi_minus"
15641 | "density_matrix_purity" | "rho_purity"
15642 | "concurrence_2qubit"
15643 | "point_in_circle"
15644 | "circle_circle_intersect_2d"
15645 | "polygon_centroid"
15646 | "sutherland_hodgman_clip" | "sh_clip"
15647 | "kalman_rts_smoother" | "rts_smoother"
15648 | "gc_content" | "codon_to_aa"
15650 | "reverse_complement_dna" | "rev_comp_dna"
15651 | "hamming_dna"
15652 | "blosum62_pair_score" | "blosum62"
15653 | "kmer_count"
15654 | "great_circle_bearing" | "gc_bearing"
15656 | "midpoint_lat_lon" | "mid_geo"
15657 | "utm_zone_for"
15658 | "area_polygon_lat_lon" | "geo_polygon_area"
15659 | "crr_binomial_option" | "crr_option"
15661 | "bond_price_clean"
15662 | "bond_yield_to_maturity" | "bond_ytm"
15663 | "modified_duration_bond"
15664 | "convexity_bond" | "bond_convexity"
15665 | "ssim" | "psnr" | "mssim"
15667 | "db_spl_from_pa" | "db_spl"
15669 | "a_weighting_factor" | "a_weight"
15670 | "octave_band_center" | "octave_center"
15671 | "semitone_ratio"
15672 | "hardy_weinberg"
15674 | "expected_heterozygosity" | "het_e"
15675 | "fst_simple"
15676 | "allele_frequencies"
15677 | "sir_step" | "sir_r0" | "doubling_time"
15679 | "theil_index"
15681 | "herfindahl_hirschman" | "hhi"
15682 | "atkinson_index"
15683 | "lorenz_curve_points"
15684 | "iota_range" | "iota"
15686 | "reshape_array" | "reshape"
15687 | "grade_up" | "grade_asc"
15688 | "grade_down" | "grade_desc"
15689 | "plasma_frequency" | "omega_p"
15691 | "debye_length" | "lambda_d"
15692 | "cyclotron_frequency" | "omega_c"
15693 | "larmor_radius" | "gyroradius"
15694 | "jaro_winkler_similarity" | "jaro_winkler"
15696 | "metaphone_simple"
15697 | "elo_rating_update" | "elo"
15699 | "glicko_rating_update" | "glicko"
15700 | "dice_sum_pmf"
15701 | "cohens_d" | "effect_size_d"
15703 | "cliff_delta"
15704 | "vargha_delaney_a12" | "a12"
15705 | "step_response_2nd_order" | "step_2nd"
15707 | "overshoot_2nd_order" | "overshoot_pct"
15708 | "frobenius_norm"
15710 | "spectral_norm" | "operator_norm_2"
15711 | "trace_matrix" | "tr_mat"
15712 | "homophily_index" | "homophily"
15714 | "dyad_census" | "triad_census"
15715 | "sigmoid_inverse" | "logit"
15717 | "partition_at" | "drop_at" | "insert_at_idx"
15719 | "replace_at_index" | "set_at"
15720 | "swap_indices" | "nth_largest" | "nth_smallest"
15721 | "position_of_all_matching" | "positions_of_all"
15722 | "string_take_first" | "string_take_last"
15723 | "string_drop_first" | "string_drop_last"
15724 | "pluralize_simple"
15725 | "singularize_simple" | "singularize"
15726 | "capitalize_words" | "title_words"
15727 | "format_table_simple" | "ascii_table"
15728 | "days_between" | "weeks_between"
15729 | "months_between" | "years_between"
15730 | "first_of_month" | "last_of_month"
15731 | "day_of_week_iso" | "iso_dow"
15732 | "easter_sunday" | "chinese_zodiac"
15733 | "iso_week_number" | "iso_week"
15734 | "relative_luminance" | "wcag_luminance"
15735 | "contrast_ratio_wcag" | "wcag_contrast"
15736 | "delta_e_76" | "delta_e"
15737 | "color_blend_t" | "lerp_color"
15738 | "chord_to_freqs" | "scale_to_intervals"
15739 | "interval_semitones"
15740 | "transpose_freq_semitones" | "transpose_semi"
15741 | "bpm_to_period" | "midi_to_pitch_class"
15742 | "key_signature_for" | "circle_of_fifths_step"
15743 | "moon_phase" | "equation_of_time"
15744 | "solar_declination" | "sidereal_day_period" | "ecliptic_obliquity"
15745 | "permutation_order"
15746 | "permutation_parity" | "perm_sign"
15747 | "identity_permutation"
15748 | "permutation_compose" | "perm_mul"
15749 | "flesch_reading_ease" | "flesch_kincaid_grade"
15750 | "gunning_fog"
15751 | "automated_readability_index" | "ari"
15752 | "lix"
15753 | "adjusted_r_squared" | "adj_r2"
15754 | "aic" | "bic"
15755 | "residuals_compute" | "compute_residuals"
15756 | "composition_count" | "weak_composition_count"
15757 | "necklace_count" | "bracelet_count"
15758 | "multiset_permutations_count" | "multinomial_count"
15759 | "pearson_hash_byte" | "pearson_hash"
15760 | "xorshift32_step" | "lcg_next_u32"
15761 | "fisher_yates_shuffle"
15762 | "tetrahedral_number" | "square_pyramidal_number"
15764 | "octahedral_number" | "pentagonal_pyramidal_number"
15765 | "cake_number" | "cuban_number" | "centered_hexagonal_number"
15766 | "carmichael_q" | "is_carmichael"
15767 | "sphenic_q" | "is_sphenic"
15768 | "seven_smooth_q" | "is_7_smooth"
15769 | "cartesian_product_n" | "cart_n"
15770 | "multiset_union" | "multiset_intersection" | "multiset_difference"
15771 | "polynomial_roots_dk" | "durand_kerner"
15772 | "lin_bairstow_step" | "bairstow"
15773 | "heap_sift_down"
15774 | "fenwick_build" | "bit_build"
15775 | "fenwick_query" | "bit_query"
15776 | "segment_tree_sum" | "seg_sum"
15777 | "kmp_failure" | "kmp"
15778 | "z_array" | "z_func"
15779 | "suffix_array_naive"
15780 | "manacher_radii" | "manacher"
15781 | "rabin_karp_hash" | "lcp_array"
15782 | "regex_escape_simple"
15783 | "horspool_search" | "bm_horspool"
15784 | "lpt_schedule" | "lpt"
15785 | "johnsons_rule" | "johnson_2m"
15786 | "bit_reverse_32" | "bit_reverse"
15787 | "bin_to_gray" | "gray_to_bin"
15788 | "swap_bits_pos" | "swap_bits"
15789 | "hamming_weight" | "popcnt"
15790 | "hamming_distance_int" | "hamdist_int"
15791 | "internal_rate_of_return"
15792 | "modified_irr" | "mirr"
15793 | "payback_period_simple" | "payback_simple"
15794 | "rfc3339_format" | "rfc3339"
15795 | "rfc3339_parse"
15796 | "iso_ordinal_date" | "ordinal_date"
15797 | "lazy_caterer" | "central_polygonal"
15799 | "centered_square" | "centered_triangular" | "centered_pentagonal"
15800 | "star_number" | "dodecahedral_number" | "icosahedral_number"
15801 | "pronic_number" | "squared_triangular"
15802 | "woodall_number" | "cullen_number"
15803 | "repunit" | "repdigit" | "kaprekar_routine_step"
15804 | "smith_q"
15805 | "keith_q" | "is_keith"
15806 | "armstrong_q" | "is_armstrong"
15807 | "fnv1a_hash" | "djb2_hash"
15808 | "jenkins_one_at_a_time" | "jenkins_oat"
15809 | "murmurhash3_x32"
15810 | "adler32_hash" | "crc16_ccitt"
15811 | "vec_dot"
15812 | "l1_norm" | "l2_norm" | "vec_l2"
15813 | "linf_norm" | "max_norm" | "lp_norm"
15814 | "unit_vector"
15815 | "vector_project" | "proj" | "vector_reject"
15816 | "orthogonalize_vectors" | "gram_schmidt"
15817 | "outer_product" | "vec_outer"
15818 | "matrix_diagonal" | "mdiagvec"
15819 | "matrix_anti_diagonal"
15820 | "matrix_symmetric_q" | "matrix_orthogonal_q"
15821 | "geometric_mean_arr" | "harmonic_mean_arr"
15822 | "quadratic_mean_arr" | "lehmer_mean"
15823 | "running_mean" | "running_variance"
15824 | "outlier_iqr_q" | "z_score_robust"
15825 | "geometric_sequence" | "arithmetic_sequence"
15826 | "log_sum_exp" | "lse"
15827 | "log_sigmoid" | "log1p_exp"
15828 | "string_chars"
15829 | "string_words_count" | "word_count_simple"
15830 | "string_lines_count" | "line_count_simple"
15831 | "string_intersperse" | "string_replicate"
15832 | "string_uniq_chars" | "string_letter_frequency"
15833 | "anagram_q" | "is_anagram_q"
15834 | "string_take_while" | "string_drop_while"
15835 | "string_split_at_first" | "string_partition_at_word"
15836 | "relativistic_kinetic"
15838 | "lorentz_factor_v" | "doppler_relativistic"
15839 | "drag_force_quadratic" | "terminal_velocity"
15840 | "carnot_efficiency" | "otto_efficiency"
15841 | "brayton_efficiency" | "diesel_efficiency"
15842 | "specific_heat_const_v" | "speed_of_sound_ideal"
15843 | "kepler_period_au" | "synodic_period"
15844 | "hill_radius" | "jeans_length"
15845 | "chandrasekhar_mass" | "eddington_luminosity"
15846 | "schwarzschild_radius_m" | "gravity_at_radius"
15847 | "gravitational_pe"
15848 | "freefall_time" | "pendulum_freq" | "spring_period"
15849 | "centripetal_accel" | "lens_focal_length"
15850 | "avogadros_number" | "boltzmann_const"
15851 | "planck_const_h" | "gas_constant_r"
15852 | "concentration_dilute" | "partial_pressure"
15853 | "mole_fraction" | "molarity" | "molality"
15854 | "normality_chem" | "ionic_strength"
15855 | "titration_volume"
15856 | "atomic_radius_pm" | "de_broglie_wavelength_kg"
15857 | "lotka_volterra_step"
15858 | "michaelis_menten" | "hill_equation"
15859 | "lineweaver_burk" | "eadie_hofstee_y"
15860 | "arrhenius_temp_q10"
15861 | "body_surface_area_dubois" | "bsa_dubois"
15862 | "bmr_harris_benedict_male" | "bmr_harris_benedict_female"
15863 | "max_heart_rate" | "target_heart_rate"
15864 | "vo2_max_estimate" | "pulse_pressure"
15865 | "mean_arterial_pressure" | "map_bp"
15866 | "dew_point_magnus" | "heat_index_celsius"
15867 | "wind_chill_celsius" | "pressure_altitude_m"
15868 | "density_altitude_m" | "saturation_vapor_pressure"
15869 | "humidex" | "utci_simple"
15870 | "resistance_parallel" | "r_parallel"
15871 | "resistance_series" | "r_series"
15872 | "capacitance_parallel" | "c_parallel"
15873 | "capacitance_series" | "c_series"
15874 | "inductance_parallel" | "l_parallel"
15875 | "inductance_series" | "l_series"
15876 | "voltage_divider" | "current_divider"
15877 | "lc_resonant" | "q_factor_rlc"
15878 | "skin_depth" | "wire_resistance"
15879 | "motor_torque" | "efficiency_ratio"
15880 | "dB_voltage" | "db_voltage"
15881 | "dB_power" | "db_power"
15882 | "bfs_distances" | "dfs_preorder" | "connected_components"
15884 | "graph_is_tree" | "graph_density"
15885 | "graph_average_degree" | "graph_max_degree" | "graph_min_degree"
15886 | "graph_complement"
15887 | "in_degree_directed" | "out_degree_directed"
15888 | "graph_eccentricity_all" | "is_connected"
15889 | "articulation_points" | "bridges_edges"
15890 | "eulerian_path_q" | "hamiltonian_brute"
15891 | "string_to_charcodes" | "charcodes_to_string"
15892 | "string_xor"
15893 | "string_camel_to_snake" | "string_snake_to_camel"
15894 | "string_kebab_to_snake" | "string_snake_to_kebab"
15895 | "palindromic_q" | "substring_count"
15896 | "string_truncate_ellipsis" | "string_expand_tabs"
15897 | "string_normalize_spaces"
15898 | "days_in_year" | "quarter_of_year"
15899 | "zeller_day_of_week" | "age_from_birthdate"
15900 | "business_days_between" | "unix_epoch_to_iso"
15901 | "loan_payment_pmt" | "loan_balance"
15902 | "amortization_total_interest"
15903 | "apr_to_apy" | "apy_to_apr"
15904 | "compound_interest_periods" | "simple_interest_compute"
15905
15906 | "perpetuity_value" | "growing_perpetuity"
15907 | "annuity_present_value" | "annuity_future_value"
15908 | "capm_expected_return"
15909 | "treynor_ratio"
15910 | "jensens_alpha" | "information_ratio"
15911 | "friction_factor_laminar" | "swamee_jain_factor"
15912 | "pipe_pressure_drop" | "orifice_velocity"
15913 | "chezy_velocity" | "manning_velocity"
15914 | "froude_number" | "weber_number" | "grashof_number"
15915 | "nusselt_dittus_boelter"
15916 | "mollweide_project" | "robinson_project" | "sinusoidal_project"
15918 | "equirectangular_project" | "lambert_azimuthal_project" | "albers_conic_project"
15919 | "geohash_encode" | "geohash_decode" | "geohash_neighbor" | "geohash_bbox"
15920 | "gabor_kernel" | "unsharp_mask_kernel" | "emboss_kernel"
15921 | "box_blur_kernel" | "motion_blur_kernel" | "sharpen_kernel"
15922 | "edge_detect_kernel" | "sobel_diagonal_kernel" | "haar_2d_step"
15923 | "db4_coeffs" | "db6_coeffs" | "sym4_coeffs" | "coif1_coeffs"
15924 | "aes_sbox_byte" | "aes_inv_sbox_byte"
15925 | "chacha20_qround" | "xtea_round" | "speck_round" | "simon_round"
15926 | "kepler_hyperbolic" | "hohmann_dv1" | "hohmann_dv2" | "hohmann_total"
15927 | "bielliptic_total" | "lambert_simple"
15928 | "horizon_distance" | "solar_zenith_angle" | "air_mass_kasten"
15929 | "solar_constant" | "julian_centuries_j2000"
15930 | "mean_solar_longitude" | "mean_solar_anomaly" | "lst_to_solar"
15931 | "ra_dec_to_az_alt" | "ecliptic_to_equatorial" | "equatorial_to_galactic"
15932 | "orbital_eccentricity" | "semi_major_axis"
15933 | "specific_orbital_energy" | "specific_angular_momentum"
15934 | "toffoli_gate" | "ccx_gate" | "fredkin_gate" | "cswap_gate"
15935 | "iswap_gate" | "sqrt_swap_gate"
15936 | "rx_gate" | "ry_gate" | "rz_gate"
15937 | "ghz_state_n" | "w_state_n"
15938 | "depolarizing_channel" | "dephasing_channel" | "amplitude_damping_channel"
15939 | "quantum_fidelity_pure" | "trace_distance"
15940 | "bell_inequality_chsh" | "pauli_decomposition_2x2"
15941 | "quantum_relative_entropy" | "qft_4_real"
15942 | "bwt_encode" | "bwt_decode" | "mtf_encode" | "mtf_decode"
15943
15944 | "lyndon_factorize" | "christoffel_word" | "sturmian_word"
15945 | "z_function_alt" | "period_of_string" | "borders_of_string"
15946 | "thue_morse_string" | "fibonacci_word"
15947 | "mann_kendall_tau" | "theil_sen_slope" | "hodges_lehmann"
15948 | "huber_m_estimator" | "winsorized_variance_arr"
15949 | "bowley_skewness" | "pearson_skewness_2"
15950 | "concordance_correlation" | "quantile_p"
15951 | "label_propagation_step" | "modularity_q"
15952 | "clique_count_3" | "local_efficiency" | "global_efficiency"
15953 | "diameter_unweighted"
15954 | "aitken_delta_squared" | "wynn_epsilon"
15955 | "shanks_transform" | "levin_t_transform"
15956 | "harmonic_seq_sum" | "alternating_seq_sum"
15957 | "sparse_csr_build" | "sparse_csr_mul_vec" | "sparse_density"
15959 | "lower_triangular_q" | "upper_triangular_q"
15960 | "diagonal_dominance_q" | "matrix_zero_q" | "matrix_identity_q"
15961 | "matrix_random_uniform" | "matrix_random_normal"
15962 | "andrew_monotone_chain" | "polygon_area_signed"
15963 | "polygon_convex_q" | "iou_2d_axis_aligned" | "hausdorff_distance_2d"
15964 | "minkowski_sum_simple" | "circle_3_points"
15965 | "polygon_winding_number" | "segment_length"
15966 | "segments_parallel_q" | "segments_perpendicular_q"
15967 | "burr_xii_pdf" | "burr_xii_cdf" | "dagum_pdf" | "lomax_pdf"
15968 | "birnbaum_saunders_pdf" | "tukey_lambda_quantile"
15969 | "half_cauchy_pdf" | "half_logistic_pdf" | "reciprocal_pdf"
15970 | "levy_pdf" | "voigt_profile_simple"
15971 | "gompertz_pdf" | "inverse_weibull_pdf"
15972 | "log_gamma_simple" | "inverse_chi2_pdf"
15973 | "poly1305_block_step" | "x25519_field_mul" | "curve25519_mul_simple"
15974 | "secp256k1_y_recover" | "hmac_step_xor"
15975 | "pkcs7_pad" | "pkcs7_unpad" | "xor_byte_string"
15976 | "atbash_cipher"
15977 | "vigenere_encrypt" | "vigenere_decrypt" | "xor_brute_keylen"
15978 | "arima_diff" | "seasonal_diff"
15979 | "garch_step" | "egarch_step"
15980 | "realized_volatility" | "max_drawdown_arr"
15981 | "calmar_ratio" | "omega_ratio" | "kelly_criterion"
15982 | "var_historical" | "cvar_historical"
15983 | "graph_degree_distribution" | "graph_count_edges"
15984 | "graph_bipartite_match_simple" | "graph_count_triangles"
15985 | "graph_avg_clustering" | "graph_transitivity"
15986 | "graph_max_clique_brute" | "graph_independent_set_brute"
15987 | "graph_count_paths_length_k" | "graph_pagerank_simple"
15988 | "boole_rule" | "boole_int"
15990 | "gauss_legendre_5" | "gl5"
15991 | "gauss_kronrod_15" | "gk15"
15992
15993 | "midpoint_rule"
15994 | "adams_bashforth_4" | "ab4"
15995 | "heun_method" | "rk45_cash_karp" | "rkck"
15996 | "milne_pc" | "milne"
15997 | "modified_midpoint_ode" | "modmidpoint"
15998 | "backward_euler" | "implicit_euler"
15999 | "crank_nicolson_ode" | "cn_ode"
16000 | "brent_root" | "brent" | "ridders_root" | "ridders"
16001 | "steffensen_root" | "steffensen" | "halley_root" | "halley"
16002 | "householder_root" | "muller_root" | "muller"
16003 | "regula_falsi" | "false_position"
16004 | "secant_root" | "secant"
16005 | "anderson_step" | "aberth_step" | "inverse_quad_interp"
16006 | "lm_step" | "gradient_descent_step"
16007 | "nesterov_step" | "adagrad_step"
16008 | "cg_beta_pr" | "cg_beta_fr" | "bfgs_h_update_1d"
16009 | "wolfe_strong_q" | "dogleg_step"
16010 | "nelder_mead_reflect" | "nelder_mead_expand" | "nelder_mead_contract"
16011 | "sa_accept_prob" | "sa_boltzmann_temp" | "sa_cauchy_temp"
16012 | "sa_geometric_temp" | "acceptance_target"
16013 | "bs_call" | "blackscholes_call" | "bs_put" | "blackscholes_put"
16015 | "bs_theta_call" | "bs_rho_call"
16016 | "bachelier_call" | "black76_call"
16017 | "crr_american_call" | "crr_american_put" | "jr_european_call"
16018 | "trinomial_call" | "heston_price_simple" | "sabr_implied_vol"
16019 | "merton_jump_call" | "asian_call_mc" | "barrier_up_out_call"
16020 | "digital_call" | "lookback_call"
16021 | "macaulay_duration" | "forward_rate"
16022 | "discount_continuous" | "ytm_newton"
16023 | "vasicek_bond" | "cir_bond" | "hull_white_drift"
16024 | "cds_upfront" | "black_karasinski_drift" | "quanto_adjustment"
16025 | "fx_forward" | "garman_kohlhagen_call" | "margrabe" | "stulz_min_call"
16026 | "sharpe_annualized"
16027 | "jensen_alpha" | "modified_sharpe"
16028 | "ph_from_h" | "poh_from_oh" | "pka_from_ka"
16030 | "henderson_base"
16031 | "arrhenius_k" | "eyring_k"
16032 | "first_order_concentration" | "first_order_half_life"
16033 | "second_order_concentration" | "second_order_half_life"
16034 | "zero_order_concentration"
16035
16036 | "ideal_gas_n" | "redlich_kwong_p"
16037 | "compressibility_z"
16038 | "kc_from_rates" | "kp_from_kc" | "reaction_quotient" | "rxn_q"
16039 | "le_chatelier_dir"
16040 | "dg_from_k" | "k_from_dg" | "vant_hoff" | "clausius_clapeyron" | "antoine_p"
16041 | "emf_from_half_cells" | "faraday_mass_deposited"
16042 | "transmittance" | "ksp_from_concs"
16043 | "debye_huckel"
16044 | "cp_monatomic_ideal" | "cv_monatomic_ideal"
16045 | "heat_capacity_q" | "calorimeter_dt" | "enthalpy_reaction"
16046 | "avogadro_count" | "moles_from_mass"
16047 | "dilution_v2" | "raoult_law" | "bp_elevation" | "fp_depression"
16048 | "osmotic_pressure" | "rydberg_lambda" | "bohr_radius_n"
16049 | "bohr_energy_ev" | "photon_energy_freq" | "photon_energy_lambda"
16050 | "de_broglie"
16051 | "logistic_growth_step" | "logistic_growth_analytic"
16053 | "gompertz_growth_step" | "allee_growth_step"
16054 | "growth_rate_from_ratio"
16055 | "seir_step" | "seird_step" | "sis_step"
16056 | "r0_basic" | "rt_effective" | "herd_immunity_threshold" | "generation_time"
16057 | "inverse_simpson"
16058 | "pielou_evenness" | "margalef_richness" | "menhinick_richness"
16059 | "berger_parker" | "sorensen_dice"
16060 | "rao_quadratic_entropy"
16061 | "selection_step" | "nei_genetic_distance"
16062 | "effective_pop_size" | "carrying_capacity_from_data"
16063 | "petersen_estimator" | "chapman_estimator"
16064 | "lv_competition_step"
16065 | "holling_type1" | "holling_type2" | "holling_type3"
16066 | "leslie_step" | "net_reproductive_rate" | "generation_time_demo"
16067 | "finite_rate_lambda" | "kleibers_law" | "bergmann_adjust"
16068 | "q10" | "species_area" | "intrinsic_growth_rate"
16069 | "macarthur_wilson_immigration" | "macarthur_wilson_extinction"
16070 | "island_equilibrium"
16071 | "efield_point" | "epotential_point"
16073 | "capacitor_charge"
16074 | "ohm_voltage" | "power_vi" | "power_i2r"
16075
16076 | "capacitance_parallel_sum"
16077 | "bfield_wire" | "bfield_solenoid" | "lorentz_force_mag"
16078 | "faraday_emf"
16079 | "lc_frequency" | "lc_omega"
16080 | "rc_tau" | "rl_tau"
16081 | "poynting_magnitude" | "em_intensity" | "radiation_pressure"
16082 | "em_wavelength" | "em_frequency"
16083 | "snell_theta2"
16084 | "index_from_speed" | "fresnel_reflection_normal"
16085 | "fresnel_rs" | "fresnel_rp"
16086 | "lensmaker" | "thin_lens_v" | "mirror_equation_v"
16087 | "lens_magnification" | "diffraction_grating_angle"
16088 | "single_slit_min" | "rayleigh_resolution"
16089 | "lorentz_gamma"
16090 | "rel_momentum" | "rel_ke" | "rel_total_energy" | "rel_energy_pm"
16091 | "relativistic_doppler" | "rel_velocity_add"
16092
16093 | "wave_string_speed" | "sound_solid" | "sound_gas"
16094 | "doppler_classical" | "standing_wave_fundamental"
16095 | "open_pipe_harmonic" | "closed_pipe_harmonic"
16096 | "sound_db"
16097 | "alfven_speed"
16098 | "grav_time_dilation" | "grav_redshift"
16099 | "kosaraju_scc" | "bridges"
16101 | "max_flow_ek" | "min_cut_value" | "hopcroft_karp"
16102
16103 | "katz_centrality" | "hits_simple"
16104 | "pagerank_damped" | "cc_count" | "cc_labels"
16105 | "topological_sort_kahn" | "has_cycle_directed" | "has_cycle_undirected"
16106 | "diameter_bfs" | "radius_bfs"
16107 | "num_edges" | "k_coreness"
16108 | "greedy_coloring" | "chromatic_number_greedy"
16109 | "sum_degrees" | "avg_degree" | "max_degree"
16110 | "is_tree" | "girth"
16111 | "hamming_window" | "hann_window" | "blackman_window"
16113 | "blackman_harris_window" | "bartlett_window" | "welch_window"
16114 | "kaiser_window" | "tukey_window" | "gaussian_window"
16115 | "hilbert_envelope"
16116 | "biquad_step" | "biquad_lowpass_coeffs" | "biquad_highpass_coeffs"
16117 | "biquad_bandpass_coeffs" | "biquad_notch_coeffs" | "biquad_allpass_coeffs"
16118 | "biquad_peak_coeffs" | "biquad_lowshelf_coeffs" | "biquad_highshelf_coeffs"
16119 | "butterworth_prewarp" | "butterworth_order"
16120 | "fir_moving_average" | "fir_lowpass_design"
16121 | "spectrogram_simple"
16122 | "zero_pad" | "resample_nearest" | "resample_linear" | "quantize"
16123 | "mu_law_encode" | "mu_law_decode" | "a_law_encode" | "a_law_decode"
16124 | "chirp_linear"
16125 | "fnv1a_32" | "fnv1a_64" | "sdbm_hash"
16127 | "siphash24"
16128 | "pbkdf2_hmac_step" | "scrypt_round" | "bcrypt_cost_iters"
16129 | "argon2_block_mix" | "hkdf_expand_step"
16130 | "lfsr_galois_step" | "mt19937_temper" | "xorshift64" | "xorshift32"
16131 | "pcg32_step" | "lcg_numrec_step" | "splitmix64_step" | "wyhash_mix"
16132
16133 | "xor_cipher_byte"
16134 | "railfence_encrypt" | "beaufort" | "affine_encrypt" | "substitution_encrypt"
16135 | "letter_frequency" | "english_chi2" | "index_of_coincidence" | "kasiski_repeats"
16136 | "deterministic_prime" | "dh_shared" | "rsa_encrypt_simple"
16137 | "monobit_test" | "approximate_entropy"
16138 | "gini_impurity" | "entropy_bits" | "information_gain" | "gain_ratio"
16140 | "nb_gaussian_likelihood" | "nb_bernoulli_likelihood" | "nb_multinomial_log_likelihood"
16141 | "adaboost_alpha" | "hinge_loss" | "squared_hinge"
16142 | "logistic_loss"
16143 | "sigmoid_grad" | "tanh_grad"
16144 | "relu_grad"
16145 | "softsign" | "prelu" | "threshold_act"
16146 | "confusion_counts" | "mcc" | "f_beta" | "specificity"
16147 | "balanced_accuracy" | "cohen_kappa" | "brier_score" | "log_loss"
16148 | "tversky" | "mahalanobis_1d"
16149 | "one_hot" | "topk_indices"
16150 | "minmax_scale" | "zscore_norm" | "robust_scale"
16151 | "triangle_area_heron" | "triangle_area_pts"
16153 | "triangle_inradius" | "triangle_circumradius"
16154 | "regular_ngon_area" | "regular_ngon_inradius" | "regular_ngon_circumradius"
16155 | "n_ball_volume"
16156 | "cylinder_surface" | "cone_surface"
16157
16158 | "ellipsoid_volume" | "ellipsoid_surface_approx"
16159 | "dist_point_line_2d" | "dist_point_plane_3d" | "closest_pt_segment_2d"
16160 | "bbox_from_points"
16161 | "euclidean_distance_nd"
16162 | "hamming_distance_str"
16163 | "great_circle_law_of_cos"
16164 | "initial_bearing" | "midpoint_great_circle"
16165 | "shoelace_area" | "polygon_is_convex" | "convex_hull_jarvis"
16166 | "euler_characteristic" | "genus_from_euler"
16167 | "spherical_triangle_area" | "polygon_with_holes_area" | "picks_theorem"
16168 | "centroid_nd" | "covariance_matrix_pts" | "simplex_volume_3d"
16169 | "hyper2f1" | "hyper1f1" | "hyper0f1" | "pochhammer"
16171 | "mathieu_ce0" | "mathieu_se1" | "parabolic_d0" | "parabolic_d1"
16172 | "whittaker_m" | "struve_h0" | "struve_h1"
16173 | "lambert_w0" | "wright_omega"
16174 | "sinhc" | "cosh_minus1_over_x2"
16175 | "sine_integral_si" | "cosine_integral_ci" | "exp_integral_e1"
16176 | "dawson_function" | "owen_t"
16177 | "spherical_bessel_j0" | "spherical_bessel_j1"
16178 | "spherical_bessel_y0" | "spherical_bessel_y1"
16179 | "mod_sph_bessel_i0" | "mod_sph_bessel_i1" | "mod_sph_bessel_k0"
16180 | "coulomb_f0"
16181 | "polylog_li2" | "polylog_n"
16182
16183 | "ti2" | "clausen_cl2"
16184 | "bose_einstein_g" | "fermi_dirac_int"
16185 | "theta3" | "theta2"
16186 | "jacobi_sn_small_q" | "jacobi_cn_small_q" | "jacobi_dn_small_q"
16187 | "riemann_xi" | "bessel_jn_general" | "bessel_in_general"
16188 | "absolute_magnitude"
16190 | "pc_to_ly" | "ly_to_pc" | "pc_to_au" | "au_to_m"
16191 | "solar_mass_to_kg" | "solar_luminosity_to_w"
16192 | "hubble_distance_mpc" | "comoving_distance_approx" | "critical_density"
16193 | "et_freq_ratio" | "midi_to_hz" | "hz_to_midi" | "cents_between"
16194 | "just_intonation_ratio" | "pythagorean_ratio"
16195 | "beat_frequency" | "bpm_to_spb" | "note_name_to_midi"
16196 | "rgb_to_yiq" | "rgb_to_yuv601"
16197 | "srgb_to_xyz" | "xyz_to_lab" | "delta_e_94"
16198
16199
16200 | "feet_to_meters" | "meters_to_feet"
16201 | "lb_to_kg" | "kg_to_lb"
16202 | "mph_to_kmh" | "kmh_to_mph" | "mps_to_kmh" | "kmh_to_mps" | "knots_to_kmh"
16203 | "atm_to_pa" | "pa_to_atm" | "mmhg_to_pa"
16204 | "ev_to_joules" | "joules_to_ev" | "btu_to_joules" | "kwh_to_joules"
16205 | "bpm_to_midi_tick_us" | "iso226_phon_adjustment"
16206 | "db_to_amp" | "amp_to_db"
16207 | "roman_encode" | "roman_decode" | "number_to_english"
16208 | "hubble_lcdm" | "hubble_time" | "hubble_distance_si" | "critical_density_si"
16210 | "comoving_distance" | "angular_diameter_distance"
16211 | "lookback_time" | "age_at_z" | "scale_factor" | "redshift_from_a"
16212 | "omega_m_at_z" | "lcdm_eos" | "cpl_w" | "deceleration_q"
16213 | "schwarzschild_radius_kg" | "kerr_ergosphere_eq" | "kerr_horizon"
16214 | "bh_entropy" | "bh_evaporation_time"
16215 | "schwarzschild_isco" | "photon_sphere_radius"
16216 | "tidal_force" | "grav_dilation_factor" | "lense_thirring_omega"
16217 | "gw_strain_amplitude" | "chirp_mass" | "grav_binding_energy"
16218 | "roche_limit_rigid" | "roche_limit_fluid"
16219 | "lagrange_l1" | "sphere_of_influence"
16220 | "freefall_velocity_schwarzschild" | "einstein_ring_radius"
16221 | "microlensing_magnification" | "cosmic_distance_modulus_si"
16222 | "cmb_temperature" | "cmb_temperature_at_z"
16223 | "stefan_boltzmann_si" | "planck_spectral_radiance"
16224 | "schwarzschild_g_tt" | "schwarzschild_g_rr" | "kretschmann_schwarzschild"
16225 | "hill_velocity" | "vacuum_energy_density"
16226 | "sound_horizon_recomb" | "bao_scale_today" | "sigma8_default"
16227 | "lensing_convergence" | "sigma_crit"
16228 | "perihelion_precession" | "shapiro_delay" | "light_deflection_angle"
16229 | "tov_mass_limit"
16230 | "main_sequence_lifetime" | "schwarzschild_freefall_time"
16231 | "friedmann_density_total" | "cosmological_constant"
16232
16233 | "planck_energy"
16234 | "pure_state_density" | "purity"
16236 | "linear_entropy" | "quantum_mutual_info"
16237 | "eof_from_concurrence"
16238 | "bell_state_index" | "chsh_expectation" | "tsirelson_bound"
16239 | "pauli_real_part" | "pauli_y_imag"
16240 | "bloch_to_density_real" | "bloch_purity_check"
16241 | "fidelity_pure_real" | "l1_coherence" | "relative_entropy_coherence"
16242 | "kraus_apply" | "bit_flip_prob" | "phase_flip_prob"
16243 | "depolarizing_density_2x2" | "amplitude_damping_excited"
16244 | "quantum_fisher_info" | "cramer_rao_bound" | "squeezing_db" | "heisenberg_min"
16245 | "coherent_mean_photons" | "thermal_mean_photons" | "poisson_photon_pmf"
16246 | "bose_einstein_pmf" | "mandel_q" | "g2_zero"
16247 | "free_particle_energy" | "infinite_well_energy" | "harmonic_oscillator_energy"
16248 | "hydrogen_energy_n" | "stark_shift_linear"
16249 | "zeeman_energy" | "larmor_frequency" | "rabi_frequency"
16250 | "schrodinger_step_real" | "probability_density" | "state_norm" | "state_normalize"
16251 | "quantum_variance" | "spin_casimir"
16252 | "cg_simple" | "wigner_3j_bound" | "qho_ground_state"
16253 | "tunneling_prob" | "gamow_factor" | "compton_wavelength" | "uncertainty_position"
16254 | "berry_phase_spin_half" | "zeno_survival" | "decoherence_time"
16255 | "ramsey_visibility" | "fermi_golden_rule"
16256 | "needleman_wunsch_score" | "smith_waterman_score" | "pam250_score"
16258 | "tanimoto_bits" | "translate_dna" | "transcribe_dna_rna" | "reverse_transcribe"
16259 | "at_content" | "tm_wallace" | "tm_marmur" | "codon_adaptation_index"
16260 | "kmer_jaccard" | "sequence_shannon_info" | "pwm_score"
16261 | "msa_column_entropy" | "seq_logo_information"
16262 | "damerau_levenshtein" | "lcs_length"
16263 | "hirschberg_lcs_length" | "common_kmers"
16264 | "jukes_cantor_distance" | "kimura_2p_distance" | "felsenstein_step"
16265 | "branch_length_substitutions" | "num_unrooted_trees" | "bayes_posterior"
16266 | "hw_expected_counts" | "allele_frequency" | "ld_d" | "ld_r_squared"
16267 | "heterozygosity" | "ne_from_variance"
16268 | "expected_coverage" | "lander_waterman_gaps"
16269 | "bh_adjusted_p" | "zscore_count"
16270 | "go_enrichment_p" | "blosum45_score"
16271 | "henikoff_weight" | "hamming_protein" | "codon_usage_variance"
16272 | "dnds_ratio" | "mutation_rate" | "tajimas_d" | "wattersons_theta"
16273 | "coalescent_expected_time" | "coalescent_tree_length" | "nm_from_fst"
16274 | "bdf1_step" | "bdf2_step" | "bdf3_step" | "bdf4_step" | "bdf5_step" | "bdf6_step"
16276 | "ab1_step" | "ab2_step" | "ab3_step"
16277 | "am2_step" | "am3_step" | "am4_step"
16278 | "ros2_step" | "imex_euler_step" | "symplectic_euler_step"
16279 | "leapfrog_step" | "stormer_verlet_step"
16280 | "rk4_single" | "dopri5_combine" | "rkf45_error"
16281 | "lobatto_iiia_2" | "lobatto_iiic_3" | "gauss_irk_2_stage" | "magnus_1st"
16282 | "euler_lte" | "trapezoidal_lte" | "pi_step_size"
16283 | "stiffness_ratio" | "spectral_radius"
16284 | "heun_euler_step" | "bogacki_shampine_step" | "verner_8_combine"
16285 | "rk_combine" | "ab_coeff_sum"
16286 | "newmark_beta_step" | "wilson_theta_step"
16287 | "strang_split" | "lie_split"
16288 | "exp_euler_step" | "etd_rk2" | "dde_euler_step"
16289 | "em_step" | "milstein_step" | "heun_sde_step" | "stratonovich_correction"
16290 | "predictor_corrector" | "numerical_jacobian_col"
16291 | "cn_coefficient" | "imex_theta_split" | "bulirsch_stoer_step"
16292 | "cfl_number" | "diffusion_stability"
16293 | "lax_friedrichs_flux" | "lax_wendroff_flux"
16294 | "van_leer_limiter" | "minmod_limiter" | "superbee_limiter" | "mc_limiter"
16295 | "pollard_p_minus_1" | "fermat_factor"
16297 | "trial_smallest_factor" | "bsgs_discrete_log"
16298 | "mertens" | "liouville"
16299 | "is_b_smooth" | "primorial_n"
16300 | "pseudoprime_base2" | "strong_pseudoprime"
16301 | "aks_witness_count" | "qs_relation"
16302 | "index_calculus_naive" | "lll_2x2_step" | "coppersmith_bound"
16303 | "shor_period_prob" | "rsa_d_from_e" | "dh_secret"
16304 | "elgamal_encrypt" | "ecc_point_double" | "continued_fraction_sqrt"
16305 | "pell_fundamental" | "sum_two_squares" | "class_number_bound"
16306 | "smith_normal_2x2_step" | "regulator_naive"
16307 | "power_residue_check" | "wieferich_check" | "wilson_test"
16308 | "goldbach_pair" | "english_likeness" | "xor_break_singlebyte"
16309 | "bit_reverse_64"
16310 | "gf256_multiply" | "hash_combine"
16311 | "arch_lm_test" | "breusch_pagan_test" | "white_robust_se"
16313 | "newey_west_se" | "hansen_j_test" | "gmm_moment_condition"
16314 | "hausman_test" | "breusch_godfrey_test" | "box_pierce_test"
16315 | "adf_test_stat" | "pp_test_stat" | "kpss_test_stat"
16316 | "dickey_fuller_critical" | "engle_granger_step"
16317 | "johansen_trace_step" | "vecm_alpha_beta"
16318 | "panel_within_estimator" | "panel_between_estimator"
16319 | "panel_random_effects" | "arellano_bond_step"
16320 | "ols_estimator" | "ols_residual_variance" | "ols_r_squared"
16321 | "ols_adjusted_r2" | "akaike_info_crit" | "bayesian_info_crit"
16322 | "hannan_quinn_ic" | "f_statistic_pooled" | "breusch_pagan_lm"
16323 | "ramsey_reset_test" | "chow_test_stat" | "white_test_stat"
16324 | "goldfeld_quandt" | "wald_test_stat" | "score_test_stat"
16325 | "likelihood_ratio_test" | "two_sls_iv" | "iv_estimator"
16326 | "mle_normal_log_lik" | "mle_exponential_log_lik"
16327 | "mle_poisson_log_lik" | "gmm_moment_function"
16328 | "pooling_test_stat" | "heteroskedasticity_test"
16329 | "robust_se_huber_white" | "bootstrap_se_estimate"
16330 | "heckman_correction" | "tobit_log_likelihood"
16331 | "probit_log_likelihood" | "logit_log_likelihood"
16332 | "multinomial_logit_prob" | "ordered_probit_threshold"
16333 | "panel_var_step" | "impulse_response_step"
16334 | "variance_decomposition" | "granger_causality_chi2"
16335 | "cointegration_residual" | "error_correction_step"
16336 | "random_walk_innovation" | "random_walk_drift_step"
16337 | "ar_model_likelihood" | "ma_model_likelihood"
16338 | "arma_model_innovation"
16339 | "euler_char_complex" | "betti_zero" | "betti_one" | "betti_two"
16341 | "genus_surface" | "chern_first_2d" | "genus_curve_arith"
16342 | "genus_curve_geo" | "hodge_diamond_value" | "poincare_duality"
16343 | "fundamental_group_zn" | "homology_rank" | "cohomology_rank"
16344 | "homotopy_group_sphere_pi" | "mapping_class_torus"
16345 | "linking_number_two" | "writhe_polygon" | "torsion_coefficient"
16346 | "simplex_volume_n" | "simplicial_volume" | "nerve_complex_count"
16347 | "cech_zero_cohomology" | "de_rham_zero"
16348 | "poincare_polynomial_eval" | "chromatic_homology_rank"
16349 | "khovanov_q_grading" | "hochschild_zero" | "cyclic_homology_step"
16350 | "group_cohomology_dim" | "group_homology_dim"
16351 | "abelianization_quotient" | "free_group_rank_lower"
16352 | "nilpotency_class_lower" | "solvable_length_upper"
16353 | "schreier_index" | "todd_genus_eval" | "hirzebruch_signature"
16354 | "chern_simons_action" | "gauss_bonnet_total"
16355 | "seifert_genus_lower" | "alexander_polynomial_at_one"
16356 | "jones_polynomial_at_minus_one" | "jones_polynomial_at_i"
16357 | "homfly_evaluation" | "kauffman_bracket_eval"
16358 | "cabling_pair_signature" | "seifert_form_2x2"
16359 | "turaev_alexander_step" | "v_polynomial_eval"
16360 | "polynomial_jones_skein" | "delta_complex_count"
16361 | "poset_zeta_two" | "mobius_poset_two" | "mobius_function_pair"
16362 | "mobius_inversion_step" | "incidence_algebra_dim"
16363 | "quiver_path_count" | "representation_dim_step"
16364 | "weyl_group_order" | "root_system_count"
16365 | "cartan_determinant_a2" | "cartan_matrix_b2"
16366 | "killing_form_su2" | "casimir_eigenvalue_su2"
16367 | "universal_enveloping_dim" | "verma_character_step"
16368 | "plethystic_substitution_value" | "schur_polynomial_eval"
16369 | "hall_inner_product_two" | "plactic_class_size"
16370 | "robinson_schensted_pair" | "yamanouchi_word_count"
16371 | "rsk_size" | "character_su2" | "character_sun"
16372 | "quantum_dimension_su2" | "quantum_dimension_q"
16373 | "fusion_rule_su2_step" | "modular_data_s_value"
16374 | "modular_data_t_value" | "verlinde_count_step"
16375 | "quantum_invariant_eval" | "operad_count_two"
16376 | "moduli_dimension_curves" | "hodge_polynomial_eval"
16377 | "mirror_symmetry_check" | "gromov_witten_invariant"
16378 | "donaldson_invariant" | "seiberg_witten_value"
16379 | "floer_homology_rank" | "khovanov_rasmussen_s"
16380 | "ozsvath_szabo_tau" | "heegaard_genus_lower"
16381 | "fintushel_stern_step" | "bauer_furuta_step"
16382 | "geometric_intersection_number"
16383 | "algebraic_intersection_number"
16384 | "nernst_potential_full" | "electrode_potential_step"
16386 | "exchange_current_density" | "butler_volmer_current"
16387 | "tafel_anodic_current" | "tafel_cathodic_current"
16388 | "mass_transport_overpotential" | "limiting_current_density"
16389 | "diffusion_layer_thickness" | "faradaic_efficiency"
16390 | "coulombic_efficiency_cell" | "energy_efficiency_cell"
16391 | "voltaic_efficiency" | "charge_capacity_battery"
16392 | "energy_density_battery" | "power_density_battery"
16393 | "specific_capacity_active" | "columbic_capacity_lihalfcell"
16394 | "ragone_point" | "peukert_capacity" | "peukert_exponent_fit"
16395 | "shepherd_voltage_step" | "nernst_planck_flux"
16396 | "debye_length_electrolyte" | "debye_huckel_activity"
16397 | "gouy_chapman_potential" | "stern_layer_capacitance"
16398 | "double_layer_capacitance" | "helmholtz_capacitance"
16399 | "zeta_potential_estimate" | "electroosmotic_velocity"
16400 | "hagen_poiseuille_eo" | "diffuse_layer_thickness"
16401 | "poisson_boltzmann_step" | "linearized_pb_step"
16402 | "electrochem_impedance_z" | "randles_circuit_z"
16403 | "warburg_impedance" | "cole_cole_eis" | "nyquist_phase"
16404 | "charge_transfer_resistance" | "solution_resistance_estimate"
16405 | "ionic_conductivity_arrhenius" | "nernst_einstein_diffusivity"
16406 | "walden_product" | "kohlrausch_law"
16407 | "onsager_relation_two_species" | "trasatti_voltammetry_charge"
16408 | "randles_sevcik_peak" | "levich_current_rde"
16409 | "koutecky_levich_intercept" | "mott_schottky_capacitance"
16410 | "flat_band_potential" | "schottky_barrier_height"
16411 | "photocurrent_density" | "quantum_efficiency_photo"
16412 | "overall_efficiency_pec" | "fuel_cell_polarization"
16413 | "electrolyzer_voltage" | "faraday_efficiency_h2"
16414 | "overpotential_oer" | "overpotential_her"
16415 | "electrocrystallization_step" | "nucleation_rate_constant"
16416 | "metal_corrosion_rate" | "pourbaix_line_value"
16417 | "mixed_potential_step" | "electrochemiluminescence_yield"
16418 | "solid_electrolyte_capacity" | "ionic_liquid_viscosity_step"
16419 | "lithium_ion_diffusivity" | "soc_estimate_coulomb"
16420 | "soh_capacity_fade" | "ocv_lithium_ion_step"
16421 | "state_of_charge_kalman" | "thermal_runaway_threshold"
16422 | "joule_heating_battery" | "calorimetric_heat_battery"
16423 | "abuse_test_voltage" | "swelling_strain_step"
16424 | "sei_resistance_growth" | "binder_content_optimal"
16425 | "porosity_active_layer" | "tortuosity_estimate_bruggeman"
16426 | "electrolyte_decomposition_temp" | "gibbs_thomson_undercooling"
16427 | "nernst_diffusion_layer" | "diff_coeff_aqueous_estimate"
16428 | "salt_activity_coefficient" | "mean_activity_coeff_pitzer"
16429 | "osmotic_coefficient_pitzer" | "debye_huckel_screening_factor"
16430 | "ph_at_isoelectric" | "buffer_capacity_acid_base"
16431 | "henderson_hasselbalch_solve" | "titration_endpoint_index"
16432 | "tensor_contract_two" | "tensor_outer_two" | "tensor_trace_index"
16434 | "tensor_symmetrize_two" | "tensor_antisymmetrize_two"
16435 | "levi_civita_three" | "levi_civita_four"
16436 | "kronecker_three" | "kronecker_four"
16437 | "metric_minkowski_eta_step" | "metric_schwarzschild_step"
16438 | "metric_kerr_step_simple" | "metric_frw_lapse"
16439 | "christoffel_first_kind_step" | "christoffel_second_kind_step"
16440 | "riemann_tensor_step_zero" | "riemann_curvature_normal_form"
16441 | "ricci_tensor_step_zero" | "scalar_curvature_step"
16442 | "einstein_tensor_step" | "weyl_tensor_step_zero"
16443 | "schouten_tensor_step" | "geodesic_equation_step_zero"
16444 | "parallel_transport_step" | "covariant_derivative_step"
16445 | "christoffel_symbol_normalize" | "ricci_identity_step"
16446 | "bianchi_first_identity_check" | "bianchi_second_identity_check"
16447 | "killing_vector_lie_step" | "lie_derivative_scalar_step"
16448 | "lie_derivative_vector_step" | "exterior_derivative_one_form"
16449 | "hodge_star_one_form" | "codifferential_step"
16450 | "laplace_de_rham_step" | "volume_form_riemannian"
16451 | "hodge_inner_product_one" | "sectional_curvature_two_plane"
16452 | "gauss_codazzi_step" | "mainardi_codazzi_step"
16453 | "weingarten_map_step" | "shape_operator_eig"
16454 | "mean_curvature_step" | "gaussian_curvature_step"
16455 | "extrinsic_principal_curv" | "intrinsic_principal_curv"
16456 | "geodesic_curvature_step" | "darboux_frame_step"
16457 | "fermi_normal_step" | "synge_world_function"
16458 | "raychaudhuri_step" | "expansion_scalar_step"
16459 | "shear_tensor_step" | "twist_tensor_step"
16460 | "optical_scalars_step" | "peeling_step_psi4"
16461 | "ads_metric_step" | "de_sitter_metric_step"
16462 | "warped_product_step_zero" | "kaluza_klein_step"
16463 | "brans_dicke_step" | "horndeski_step"
16464 | "einstein_dilaton_step" | "gauss_bonnet_term_2d"
16465 | "chern_pontryagin_4d_step" | "adm_mass_step"
16466 | "komar_mass_step" | "bondi_mass_step"
16467 | "brown_york_quasilocal" | "isolated_horizon_charge"
16468 | "trapped_surface_check" | "apparent_horizon_step"
16469 | "event_horizon_check" | "cosmological_constant_term"
16470 | "de_sitter_radius_step" | "anti_de_sitter_radius_step"
16471 | "penrose_diagram_factor" | "conformal_compactification_step"
16472 | "schwarzschild_kruskal_step" | "gullstrand_painleve_step"
16473 | "kerr_newman_charge_term" | "boyer_lindquist_step"
16474 | "hartle_thorne_metric" | "oppenheimer_volkoff_step"
16475 | "post_newtonian_step" | "shapiro_delay_step"
16476 | "mercury_perihelion_advance"
16477 | "gravitational_wave_quadrupole"
16478 | "plus_polarization_amp" | "cross_polarization_amp"
16479 | "chirp_mass_inspiral_step" | "isco_radius_kerr_step"
16480 | "spin_orbit_coupling_term" | "spin_spin_coupling_term"
16481 | "hawking_area_increase" | "unruh_temperature_full"
16482 | "bekenstein_entropy_step" | "holographic_entanglement_step"
16483 | "ryu_takayanagi_step" | "swampland_distance_check"
16484 | "conditional_entropy_step" | "joint_entropy_step"
16486 | "relative_entropy_kl" | "mutual_information_step"
16487 | "chain_rule_entropy" | "fano_inequality_bound"
16488 | "data_processing_inequality" | "arithmetic_coding_interval"
16489 | "range_coding_step" | "golomb_rice_code"
16490 | "elias_gamma_code" | "elias_delta_code" | "exp_golomb_code"
16491 | "fibonacci_code" | "shannon_fano_elias_code"
16492 | "huffman_balanced_step" | "arithmetic_decode_interval"
16493 | "range_decode_step" | "universal_code_length"
16494 | "ziv_lempel_estimate" | "lz77_match_length"
16495 | "lz78_dictionary_growth" | "lzw_step_dict"
16496 | "ppm_predict_prob" | "deflate_huffman_lit"
16497 | "brotli_distance_code_count" | "zstd_window_size_log"
16498 | "mpeg_quant_value" | "jpeg_zig_zag_index"
16499 | "jpeg_dct_8x8_quant" | "hadamard_walsh_transform_step"
16500 | "karhunen_loeve_step" | "discrete_haar_step"
16501 | "db4_wavelet_step" | "biorthogonal_step"
16502 | "beylkin_wavelet_step" | "coiflet_wavelet_step"
16503 | "mallat_pyramid_step" | "threshold_soft_value"
16504 | "threshold_hard_value" | "median_filter_window"
16505 | "mean_filter_window" | "gaussian_filter_window"
16506 | "unsharp_mask_step" | "sobel_kernel_value"
16507 | "prewitt_kernel_value" | "roberts_kernel_value"
16508 | "laplacian_kernel_value" | "canny_threshold_step"
16509 | "hough_accumulator_step" | "ransac_iteration_count"
16510 | "optical_flow_lk_step" | "horn_schunck_step"
16511 | "kalman_predict_state" | "kalman_update_state"
16512 | "particle_filter_resample" | "unscented_sigma_point"
16513 | "ekf_jacobian_step" | "markov_decision_value"
16514 | "bellman_equation_step" | "q_learning_update"
16515 | "policy_iteration_step" | "value_iteration_step"
16516 | "sarsa_update" | "double_q_learning_step"
16517 | "ucb1_action_value" | "thompson_sample_beta"
16518 | "boltzmann_softmax_action" | "explore_exploit_epsilon"
16519 | "montecarlo_returns_step" | "td_zero_update"
16520 | "td_lambda_update" | "gradient_temporal_diff"
16521 | "deep_q_target" | "ddpg_critic_loss_step"
16522 | "ppo_clip_term" | "trpo_kl_constraint"
16523 | "a3c_advantage_step" | "ppo_advantage_step"
16524 | "gae_advantage_step" | "generalized_advantage"
16525 | "information_bottleneck_step" | "free_energy_principle"
16526 | "fisher_info_metric" | "kullback_jensen_div"
16527 | "hellinger_distance_step" | "total_variation_distance"
16528 | "bhattacharyya_coefficient" | "wasserstein_dist_emp"
16529 | "chisquare_metric" | "hellinger_kernel"
16530 | "jensen_shannon_div" | "renyi_divergence_step"
16531 | "amari_alpha_div" | "csiszar_phi_div"
16532 | "sinkhorn_iteration_step" | "sliced_wasserstein"
16533 | "gromov_wasserstein_step" | "spectral_signature_match"
16534 | "mfcc_coeff_step" | "chroma_feature_step"
16535 | "tsp_lower_bound_mst" | "tsp_held_karp_step"
16537 | "christofides_ratio_bound" | "two_opt_swap_delta"
16538 | "or_opt_delta" | "three_opt_delta" | "lin_kernighan_step"
16539 | "nearest_neighbor_tour_step" | "greedy_edge_tour"
16540 | "nearest_insertion_step" | "farthest_insertion_step"
16541 | "cheapest_insertion_step" | "max_flow_ford_fulkerson_step"
16542 | "edmonds_karp_step" | "dinic_blocking_flow"
16543 | "push_relabel_step" | "boykov_kolmogorov_step"
16544 | "mincut_stoer_wagner" | "gomory_hu_step"
16545 | "karger_contract_edge" | "karger_min_cut_count"
16546 | "maximum_bipartite_matching" | "hopcroft_karp_phase"
16547 | "blossom_match_step" | "weighted_match_kuhn_step"
16548 | "hungarian_method_step" | "ap_jonker_volgenant_step"
16549 | "assignment_lower_bound" | "job_shop_makespan_lower"
16550 | "flow_shop_johnson_step" | "parallel_machine_lpt"
16551 | "parallel_machine_spt" | "list_scheduling_step"
16552 | "graham_2approx_bound" | "chc_bound_makespan"
16553 | "bin_packing_first_fit" | "bin_packing_best_fit"
16554 | "bin_packing_next_fit" | "bin_packing_lower_bound_l1"
16555 | "multidim_packing_step" | "knapsack_01_dp_value"
16556 | "knapsack_unbounded_dp" | "knapsack_fractional_step"
16557 | "knapsack_branch_bound" | "knapsack_lp_relaxation"
16558 | "multi_knapsack_step" | "quadratic_assignment_step"
16559 | "qap_lower_bound" | "graph_coloring_dsatur_step"
16560 | "graph_coloring_welsh_powell"
16561 | "graph_coloring_brooks_bound" | "graph_coloring_lp_bound"
16562 | "fractional_chromatic_lower" | "list_coloring_step"
16563 | "edge_coloring_vizing_step" | "clique_number_lower"
16564 | "independence_number_upper" | "vertex_cover_lp_round"
16565 | "dominating_set_greedy_step" | "dominating_set_lp_bound"
16566 | "set_cover_greedy_step" | "set_cover_lp_round"
16567 | "hitting_set_greedy" | "weighted_set_cover_step"
16568 | "matroid_greedy_step" | "matroid_intersection_step"
16569 | "submodular_greedy_step" | "submodular_curvature_bound"
16570 | "nemhauser_wolsey_bound" | "lp_relax_round"
16571 | "branch_and_bound_step" | "cutting_plane_step"
16572 | "gomory_cut_step" | "chvatal_gomory_cut"
16573 | "mixed_integer_round_up" | "mixed_integer_round_down"
16574 | "sos_constraint_check" | "column_generation_step"
16575 | "benders_decomposition_step" | "dantzig_wolfe_step"
16576 | "lagrangian_relax_step" | "lagrangian_dual_step"
16577 | "subgradient_step_size" | "nonlinear_dual_step"
16578 | "augmented_lagrangian_step" | "admm_primal_step"
16579 | "admm_dual_step" | "proximal_gradient_step"
16580 | "nesterov_accelerate_step" | "fista_step" | "ista_step"
16581 | "mirror_descent_step" | "frank_wolfe_step"
16582 | "conditional_gradient_step" | "greedy_set_cover_round"
16583 | "local_search_swap_step" | "tabu_search_move_score"
16584 | "simulated_annealing_step" | "genetic_crossover_one_point"
16585 | "mutation_bit_flip_prob" | "roulette_wheel_select_index"
16586 | "stefan_boltzmann_radiation" | "emissivity_grey_body"
16588 | "albedo_blackbody_balance" | "solar_constant_at_distance"
16589 | "total_solar_irradiance_step" | "absorbed_short_wave"
16590 | "emitted_long_wave" | "clausius_clapeyron_full"
16591 | "relative_humidity_step" | "dewpoint_temperature_full"
16592 | "wet_bulb_potential" | "virtual_temperature_full"
16593 | "density_altitude_full" | "geopotential_height_full"
16594 | "geometric_height_full" | "adiabatic_lapse_rate_dry"
16595 | "adiabatic_lapse_rate_moist" | "brunt_vaisala_full"
16596 | "richardson_number_step" | "gradient_richardson_full"
16597 | "flux_richardson_full" | "turbulent_kinetic_energy_step"
16598 | "mixing_length_prandtl" | "monin_obukhov_length"
16599 | "similarity_function_phi" | "log_law_wind_profile"
16600 | "power_law_wind_profile" | "ekman_layer_depth"
16601 | "ekman_pumping_step" | "geostrophic_wind_step"
16602 | "gradient_wind_step" | "thermal_wind_step"
16603 | "quasi_geostrophic_omega" | "omega_equation_step"
16604 | "potential_temperature_step" | "equivalent_potential_temp"
16605 | "saturation_equivalent_pt" | "ipv_potential_vorticity"
16606 | "ertel_pv_step" | "absolute_vorticity_step"
16607 | "relative_vorticity_step" | "divergence_omega_step"
16608 | "streamfunction_step" | "velocity_potential_step"
16609 | "helmholtz_decomp_step" | "courant_friedrichs_lewy"
16610 | "peclet_number_step" | "prandtl_number_step"
16611 | "reynolds_full_number" | "schmidt_number_step"
16612 | "sherwood_number_step" | "nusselt_full_number"
16613 | "grashof_number_step" | "rayleigh_number_step"
16614 | "weber_number_step" | "froude_number_step"
16615 | "strouhal_full" | "mach_full_step"
16616 | "biot_number_step" | "fourier_number_step"
16617 | "turbulence_intensity_step" | "hurst_exponent_estimate"
16618 | "detrended_fluct_alpha" | "power_spectrum_slope"
16619 | "spectral_kappa_minus53" | "batchelor_scale_step"
16620 | "kolmogorov_microscale" | "taylor_microscale_step"
16621 | "integral_length_scale" | "turbulent_dissipation_eps"
16622 | "isotropic_relation_check" | "sst_anomaly_step"
16623 | "enso_index_step" | "amo_index_step" | "nao_index_step"
16624 | "soi_oscillation_index" | "pdo_index_step" | "mjo_phase_step"
16625 | "walker_circulation_step" | "hadley_cell_max_lat"
16626 | "ferrel_cell_step" | "itcz_position_lat" | "trade_wind_speed"
16627 | "westerlies_jet_speed" | "polar_vortex_radius"
16628 | "arctic_oscillation_step" | "indian_monsoon_index"
16629 | "african_monsoon_index" | "qbo_oscillation_step"
16630 | "solar_cycle_phase" | "sunspot_relative_number"
16631 | "geomagnetic_kp_index" | "ozone_dobson_total"
16632 | "chlorine_radical_decay" | "montreal_protocol_track"
16633 | "co2_growth_rate_step" | "methane_growth_rate"
16634 | "aerosol_optical_depth" | "ice_age_milankovitch"
16635 | "greenhouse_forcing_step"
16636 | "game_two_player_value" | "nash_equilibrium_pair"
16638 | "mixed_strategy_value" | "zero_sum_minmax"
16639 | "saddle_point_check" | "correlated_equilibrium_value"
16640 | "shapley_value_two_step" | "banzhaf_index_two"
16641 | "nucleolus_lp_step" | "core_membership_check"
16642 | "imputation_efficient_check" | "imputation_individual_rational"
16643 | "prisoners_dilemma_payoff" | "matching_pennies_payoff"
16644 | "chicken_game_payoff" | "stag_hunt_payoff"
16645 | "battle_sexes_payoff" | "public_goods_game_payoff"
16646 | "tragedy_commons_metric" | "ultimatum_acceptance_prob"
16647 | "dictator_game_share" | "trust_game_repayment"
16648 | "cooperative_game_value" | "characteristic_function"
16649 | "bargaining_set_check" | "kalai_smorodinsky_step"
16650 | "nash_bargaining_solution" | "egalitarian_solution"
16651 | "utilitarian_solution" | "social_welfare_sum"
16652 | "arrow_impossibility_check" | "gibbard_satterthwaite_check"
16653 | "borda_count_step" | "condorcet_winner_check"
16654 | "plurality_winner_step" | "kemeny_score_step"
16655 | "dodgson_swap_count" | "coombs_runoff_step"
16656 | "single_transferable_vote" | "range_voting_score"
16657 | "approval_voting_max" | "schulze_method_step"
16658 | "copeland_score_step" | "black_method_winner"
16659 | "median_voter_step" | "hotelling_location_step"
16660 | "arrow_pareto_check" | "fair_division_envy_free"
16661 | "proportional_share" | "maximin_share"
16662 | "egalitarian_split" | "nash_social_welfare"
16663 | "divisible_goods_proportional" | "indivisible_envy_free_check"
16664 | "adjusted_winner_pct" | "sealed_bid_first_price"
16665 | "sealed_bid_second_price" | "english_auction_step"
16666 | "dutch_auction_step" | "all_pay_auction_step"
16667 | "vcg_payment_step" | "revenue_equivalence_check"
16668 | "truthful_mechanism_check" | "incentive_compatibility_check"
16669 | "mechanism_design_obj" | "double_auction_step"
16670 | "combinatorial_auction_step" | "posted_price_offer_accept"
16671 | "matching_market_step" | "deferred_acceptance_step"
16672 | "boston_mechanism_step" | "top_trading_cycles_step"
16673 | "school_choice_match" | "roommate_match_step"
16674 | "network_formation_step" | "coordination_game_payoff"
16675 | "evolutionary_stable_strategy" | "replicator_dynamics_step"
16676 | "hawk_dove_payoff" | "fictitious_play_step"
16677 | "best_response_dynamic" | "quantal_response_logit"
16678 | "level_k_step" | "cognitive_hierarchy_step"
16679 | "sequential_eq_check" | "subgame_perfect_eq"
16680 | "stackelberg_step" | "cournot_quantity_step"
16681 | "bertrand_price_step" | "hotelling_price_step"
16682 | "collusion_payoff_step" | "folk_theorem_value"
16683 | "repeated_game_avg_payoff" | "discount_factor_step"
16684 | "trigger_strategy_payoff" | "grim_trigger_step"
16685 | "tit_for_tat_step" | "prisoners_repeated_eq"
16686 | "mertens_zamir_step" | "ex_post_value_check"
16687 | "ex_ante_value_check" | "common_knowledge_iterations"
16688 | "cas_simplify_term" | "cas_expand_two_terms"
16690 | "cas_factor_quadratic" | "cas_partial_fraction_simple"
16691 | "cas_polynomial_gcd_step" | "cas_polynomial_div_step"
16692 | "cas_lagrange_interpolate" | "cas_chebyshev_eval"
16693 | "cas_legendre_eval" | "cas_hermite_eval"
16694 | "cas_laguerre_eval" | "cas_jacobi_eval"
16695 | "cas_gegenbauer_eval" | "cas_taylor_coefficient"
16696 | "cas_padé_diagonal" | "cas_continued_fraction_step"
16697 | "cas_resultant_two" | "cas_subresultant_two"
16698 | "cas_groebner_lt_step" | "cas_buchberger_step"
16699 | "cas_macaulay_matrix_step" | "cas_modular_inverse"
16700 | "cas_extended_euclid_step" | "cas_smith_normal_step"
16701 | "cas_hermite_normal_step" | "cas_radical_simplify"
16702 | "cas_minimal_polynomial" | "cas_gcd_polynomial_step"
16703 | "cas_resultant_x_y" | "cas_solve_linear"
16704 | "cas_solve_quadratic" | "cas_solve_cubic"
16705 | "cas_solve_quartic" | "cas_solve_polynomial_n"
16706 | "cas_root_isolate_step" | "cas_sturm_sequence_step"
16707 | "cas_descartes_rule_count" | "cas_companion_matrix_root"
16708 | "cas_polynomial_roots_kahan"
16709 | "cas_eigenvalue_inverse_iteration" | "cas_qr_iteration_step"
16710 | "cas_jacobi_eigen_step" | "cas_lanczos_iteration_step"
16711 | "cas_arnoldi_iteration_step" | "cas_givens_rotation_apply"
16712 | "cas_householder_reflection" | "cas_modified_gram_schmidt"
16713 | "cas_classical_gram_schmidt" | "cas_rank_revealing_qr"
16714 | "cas_pivoted_lu_step" | "cas_block_lu_step"
16715 | "cas_cholesky_step" | "cas_modified_cholesky"
16716 | "cas_ldlt_step" | "cas_bunch_kaufman_step"
16717 | "cas_woodbury_identity" | "cas_matrix_pencil_step"
16718 | "cas_generalized_eigen" | "cas_singular_value_step"
16719 | "cas_truncated_svd_value" | "cas_pseudoinverse_step"
16720 | "cas_polar_decomposition" | "cas_schur_decomposition_step"
16721 | "cas_quasi_triangular" | "cas_riccati_continuous_step"
16722 | "cas_riccati_discrete_step" | "cas_lyapunov_continuous_step"
16723 | "cas_lyapunov_discrete_step" | "cas_sylvester_equation_step"
16724 | "cas_kronecker_product_step" | "cas_vec_operator_step"
16725 | "cas_matrix_function_step" | "cas_matrix_log_step"
16726 | "cas_matrix_exp_pade" | "cas_matrix_sqrt_step"
16727 | "cas_drazin_inverse_step" | "cas_moore_penrose_step"
16728 | "cas_least_squares_solve" | "cas_total_least_squares"
16729 | "cas_constrained_ls_step" | "cas_truncated_lsq"
16730 | "cas_regularized_lsq_tikhonov" | "cas_basis_pursuit_step"
16731 | "cas_lasso_soft_threshold" | "cas_elastic_net_step"
16732 | "cas_omp_step" | "cas_iht_iteration"
16733 | "cas_cosamp_step" | "cas_admm_lasso_step"
16734 | "cas_proximal_l1_step" | "cas_proximal_l2_step"
16735 | "cas_proximal_l_inf_step" | "cas_indicator_simplex_proj"
16736 | "cas_proj_l1_ball" | "cas_proj_l2_ball"
16737 | "cas_proj_box" | "cas_proj_psd_cone"
16738 | "cas_proj_soc_step" | "cas_proj_exp_cone"
16739 | "cas_dykstra_step" | "cas_alternating_projection"
16740 | "cas_polya_enumeration_step" | "cas_burnside_count_step"
16741 | "ml_relu_step" | "ml_leaky_relu_step" | "ml_elu_step"
16743 | "ml_selu_step" | "ml_gelu_step" | "ml_swish_step"
16744 | "ml_mish_step" | "ml_softplus_step" | "ml_softsign_step"
16745 | "ml_hard_sigmoid" | "ml_hard_tanh" | "ml_prelu_step"
16746 | "ml_celu_step" | "ml_silu_step" | "ml_logsumexp_step"
16747 | "ml_log_softmax_step" | "ml_log_sigmoid"
16748 | "ml_glu_step" | "ml_geglu_step" | "ml_swiglu_step"
16749 | "ml_attention_score_step" | "ml_scaled_dot_product"
16750 | "ml_multihead_avg" | "ml_softmax_temperature"
16751 | "ml_dropout_mask_prob" | "ml_layer_norm_step"
16752 | "ml_batch_norm_step" | "ml_group_norm_step"
16753 | "ml_rms_norm_step" | "ml_instance_norm_step"
16754 | "ml_weight_norm_step" | "ml_spectral_norm_step"
16755 | "ml_l2_normalize_step" | "ml_huber_loss_step"
16756 | "ml_smooth_l1_loss" | "ml_focal_loss_step"
16757 | "ml_dice_loss_step" | "ml_iou_loss_step"
16758 | "ml_giou_loss_step" | "ml_diou_loss_step"
16759 | "ml_ciou_loss_step" | "ml_contrastive_loss"
16760 | "ml_triplet_loss_step" | "ml_arcface_loss_step"
16761 | "ml_center_loss_step" | "ml_kl_divergence_loss"
16762 | "ml_cross_entropy_loss" | "ml_binary_cross_entropy"
16763 | "ml_label_smoothing" | "ml_mixup_lambda"
16764 | "ml_cutmix_box_iou" | "ml_random_erasing_step"
16765 | "ml_cosine_lr_schedule" | "ml_warmup_lr_step"
16766 | "ml_step_lr_schedule" | "ml_exponential_lr"
16767 | "ml_polynomial_lr" | "ml_one_cycle_lr"
16768 | "ml_inverse_sqrt_lr" | "ml_cyclic_lr_step"
16769 | "ml_sgd_step" | "ml_momentum_step"
16770 | "ml_nesterov_momentum" | "ml_adagrad_step"
16771 | "ml_rmsprop_step" | "ml_adam_step"
16772 | "ml_adamw_step" | "ml_adamax_step"
16773 | "ml_nadam_step" | "ml_radam_step"
16774 | "ml_lookahead_step" | "ml_lamb_step"
16775 | "ml_lars_step" | "ml_yogi_step"
16776 | "ml_amsgrad_step" | "ml_adabelief_step"
16777 | "ml_shampoo_step" | "ml_lion_step"
16778 | "ml_sophia_step" | "ml_gradient_clip_norm"
16779 | "ml_gradient_clip_value" | "ml_gradient_accumulate"
16780 | "ml_gradient_centralize" | "ml_weight_decay_step"
16781 | "ml_he_init_value" | "ml_xavier_init_value"
16782 | "ml_glorot_init_value" | "ml_orthogonal_init"
16783 | "ml_truncnormal_init" | "ml_kaiming_init"
16784 | "ml_lecun_init_value" | "ml_zero_init"
16785 | "ml_constant_init" | "ml_uniform_init"
16786 | "ml_one_hot_index" | "ml_label_to_id"
16787 | "ml_id_to_label_step" | "ml_token_logit_top_k"
16788 | "ml_topk_argmax" | "ml_nucleus_sample_p"
16789 | "ml_temperature_decay" | "ml_repetition_penalty"
16790 | "ml_eos_logit_boost"
16791 | "nlp_bm25_score" | "nlp_tf_idf_step" | "nlp_okapi_score"
16793 | "nlp_word_freq_value" | "nlp_doc_freq_step"
16794 | "nlp_inverse_doc_freq" | "nlp_cosine_similarity_two"
16795 | "nlp_jaccard_similarity_two" | "nlp_overlap_coefficient"
16796 | "nlp_dice_coefficient_two" | "nlp_simpson_coefficient"
16797 | "nlp_levenshtein_dist" | "nlp_damerau_levenshtein"
16798 | "nlp_jaro_distance" | "nlp_jaro_winkler"
16799 | "nlp_hamming_distance" | "nlp_lcs_length" | "nlp_lcs_ratio"
16800 | "nlp_meteor_score" | "nlp_bleu_score_n"
16801 | "nlp_rouge_score_n" | "nlp_chrf_score" | "nlp_ter_score"
16802 | "nlp_wer_score" | "nlp_cer_score" | "nlp_perplexity_value"
16803 | "nlp_bits_per_character" | "nlp_char_ngram_count"
16804 | "nlp_word_ngram_count" | "nlp_skip_gram_count"
16805 | "nlp_byte_pair_merge_step" | "nlp_wordpiece_score"
16806 | "nlp_unigram_lm_score" | "nlp_kneser_ney_step"
16807 | "nlp_witten_bell_step" | "nlp_good_turing_count"
16808 | "nlp_laplace_smoothing" | "nlp_lidstone_smoothing"
16809 | "nlp_jelinek_mercer" | "nlp_dirichlet_smoothing"
16810 | "nlp_query_likelihood_step" | "nlp_kl_lm_div"
16811 | "nlp_pmi_score" | "nlp_npmi_score"
16812 | "nlp_chi2_collocation" | "nlp_loglikelihood_collocation"
16813 | "nlp_t_score_collocation" | "nlp_dunning_log_likelihood"
16814 | "nlp_lda_alpha_step" | "nlp_lda_beta_step"
16815 | "nlp_lda_topic_dist" | "nlp_plsa_step"
16816 | "nlp_word2vec_skipgram_loss" | "nlp_word2vec_cbow_loss"
16817 | "nlp_glove_loss_step" | "nlp_fasttext_subword_count"
16818 | "nlp_byte_level_bpe_step" | "nlp_sentencepiece_score"
16819 | "nlp_unigram_subword_loss" | "nlp_subword_regularization"
16820 | "nlp_pointwise_attn_score" | "nlp_relative_position_bias"
16821 | "nlp_alibi_position_bias" | "nlp_rope_rotary_angle"
16822 | "nlp_rope_apply_step" | "nlp_position_encoding_sin"
16823 | "nlp_position_encoding_cos" | "nlp_pe_freq_band"
16824 | "nlp_max_seq_len_check" | "nlp_token_drop_rate"
16825 | "nlp_byte_frequency" | "nlp_char_frequency"
16826 | "nlp_punct_ratio" | "nlp_uppercase_ratio"
16827 | "nlp_digit_ratio" | "nlp_emoji_ratio"
16828 | "nlp_url_count" | "nlp_email_count" | "nlp_phone_count"
16829 | "nlp_hashtag_count" | "nlp_mention_count"
16830 | "nlp_token_overlap_two" | "nlp_word_mover_dist"
16831 | "nlp_sif_weight_step" | "nlp_doc_embedding_avg"
16832 | "nlp_attention_pool_step" | "nlp_max_pool_step"
16833 | "nlp_avg_pool_step" | "nlp_sum_pool_step"
16834 | "nlp_self_attn_compute_step" | "nlp_cross_attn_compute_step"
16835 | "nlp_window_attn_step" | "nlp_strided_attn_step"
16836 | "nlp_block_attn_step" | "nlp_sliding_window_step"
16837 | "nlp_local_attn_step" | "nlp_dilated_attn_step"
16838 | "nlp_global_attn_step" | "nlp_sparse_attn_score"
16839 | "nlp_linformer_step" | "nlp_performer_step"
16840 | "nlp_reformer_step" | "nlp_longformer_step"
16841 | "nlp_bigbird_step" | "nlp_routing_attn_step"
16842 | "gfx_perspective_proj_x" | "gfx_perspective_proj_y"
16844 | "gfx_orthographic_proj" | "gfx_view_matrix_step"
16845 | "gfx_lookat_forward" | "gfx_lookat_right" | "gfx_lookat_up"
16846 | "gfx_quat_to_axis_angle" | "gfx_axis_angle_to_quat"
16847 | "gfx_quat_slerp_step" | "gfx_quat_nlerp_step"
16848 | "gfx_quat_dot_two" | "gfx_quat_inverse_step"
16849 | "gfx_quat_to_euler_pitch" | "gfx_quat_to_euler_yaw"
16850 | "gfx_quat_to_euler_roll" | "gfx_euler_to_quat_x"
16851 | "gfx_euler_to_quat_y" | "gfx_euler_to_quat_z"
16852 | "gfx_euler_to_quat_w" | "gfx_rotation_matrix_xx"
16853 | "gfx_rotation_matrix_yy" | "gfx_rotation_matrix_zz"
16854 | "gfx_translation_matrix_step" | "gfx_scale_matrix_step"
16855 | "gfx_shear_matrix_xy" | "gfx_homogeneous_divide"
16856 | "gfx_screen_space_x" | "gfx_screen_space_y"
16857 | "gfx_ndc_to_screen_x" | "gfx_ndc_to_screen_y"
16858 | "gfx_screen_to_ndc_x" | "gfx_screen_to_ndc_y"
16859 | "gfx_clip_polygon_step" | "gfx_sutherland_hodgman"
16860 | "gfx_cohen_sutherland_code" | "gfx_liang_barsky_t"
16861 | "gfx_bresenham_step_x" | "gfx_bresenham_step_y"
16862 | "gfx_xiaolin_wu_intensity" | "gfx_aabb_intersect_check"
16863 | "gfx_obb_overlap_step" | "gfx_sphere_intersect_t"
16864 | "gfx_ray_triangle_t" | "gfx_ray_plane_t" | "gfx_ray_box_t"
16865 | "gfx_ray_sphere_t" | "gfx_ray_disk_t"
16866 | "gfx_ray_cylinder_t" | "gfx_ray_cone_t"
16867 | "gfx_ray_ellipsoid_t" | "gfx_ray_torus_t_approx"
16868 | "gfx_barycentric_alpha" | "gfx_barycentric_beta"
16869 | "gfx_barycentric_gamma" | "gfx_phong_diffuse_step"
16870 | "gfx_phong_specular_step" | "gfx_phong_ambient_step"
16871 | "gfx_blinn_specular_step" | "gfx_lambert_term"
16872 | "gfx_oren_nayar_term" | "gfx_cook_torrance_d_ggx"
16873 | "gfx_cook_torrance_g_smith" | "gfx_cook_torrance_f_schlick"
16874 | "gfx_disney_principled_d" | "gfx_microfacet_brdf_step"
16875 | "gfx_subsurface_scattering_term" | "gfx_translucent_falloff"
16876 | "gfx_normal_distribution_ggx"
16877 | "gfx_geometric_attenuation_smith"
16878 | "gfx_fresnel_dielectric_step" | "gfx_fresnel_conductor_step"
16879 | "gfx_index_of_refraction" | "gfx_snells_law_angle"
16880 | "gfx_total_internal_reflection" | "gfx_refract_direction_x"
16881 | "gfx_reflect_direction_x" | "gfx_environment_map_uv_u"
16882 | "gfx_environment_map_uv_v" | "gfx_cube_map_face_index"
16883 | "gfx_octahedral_encode_x" | "gfx_octahedral_encode_y"
16884 | "gfx_spherical_harmonic_y00" | "gfx_spherical_harmonic_y10"
16885 | "gfx_spherical_harmonic_y11" | "gfx_spherical_harmonic_y20"
16886 | "gfx_zonal_harmonic_step" | "gfx_irradiance_sh_eval"
16887 | "gfx_radiance_sh_eval" | "gfx_skybox_uv_u" | "gfx_skybox_uv_v"
16888 | "gfx_tonemap_reinhard" | "gfx_tonemap_aces"
16889 | "gfx_tonemap_uncharted2" | "gfx_tonemap_filmic"
16890 | "gfx_gamma_correct_step" | "gfx_srgb_to_linear"
16891 | "gfx_linear_to_srgb" | "gfx_dither_bayer_4x4"
16892 | "gfx_dither_floyd_steinberg" | "gfx_oklab_l_step"
16893 | "gfx_oklab_a_step" | "gfx_oklab_b_step"
16894 | "gfx_oklch_chroma" | "gfx_oklch_hue"
16895 | "gfx_pcg_hash_step" | "gfx_xorshift_step"
16896 | "gfx_halton_step" | "gfx_sobol_step"
16897 | "gfx_van_der_corput" | "gfx_low_discrepancy_step"
16898 | "gfx_blue_noise_value" | "gfx_perlin_noise_step"
16899 | "gfx_simplex_noise_step" | "gfx_fbm_noise_step"
16900 | "gfx_worley_noise_step" | "gfx_voronoi_distance"
16901 | "gfx_curl_noise_step" | "gfx_gradient_noise_step"
16902 | "gfx_value_noise_step" | "gfx_signed_distance_box"
16903 | "gfx_signed_distance_sphere" | "gfx_signed_distance_capsule"
16904 | "db_b_tree_split" | "db_b_tree_merge"
16906 | "db_lsm_compaction_step" | "db_skiplist_height_pick"
16907 | "db_bloom_filter_bit_index" | "db_cuckoo_filter_fingerprint"
16908 | "db_quotient_filter_canonical" | "db_count_min_sketch_bin"
16909 | "db_hyperloglog_register_max" | "db_min_hash_value"
16910 | "db_simhash_bit" | "db_consistent_hash_index"
16911 | "db_rendezvous_hash_score" | "db_jump_hash_bucket"
16912 | "db_maglev_hash_step" | "db_lru_cache_eviction_age"
16913 | "db_lfu_cache_decay" | "db_arc_cache_score"
16914 | "db_clock_cache_hand" | "db_tinylfu_admit_score"
16915 | "db_w_tinylfu_freq" | "db_buffer_pool_score"
16916 | "db_query_plan_cost_step" | "db_join_selectivity_step"
16917 | "db_index_seek_cost" | "db_seq_scan_cost"
16918 | "db_index_scan_cost" | "db_sort_cost_estimate"
16919 | "db_hash_join_cost" | "db_merge_join_cost"
16920 | "db_nested_loop_cost" | "db_query_cardinality"
16921 | "db_histogram_bucket_index" | "db_quantile_estimate_p99"
16922 | "db_t_digest_centroid" | "db_kll_quantile_step"
16923 | "db_dd_sketch_bin" | "db_reservoir_sample_index"
16924 | "db_chao_estimator_step" | "db_jaccard_minhash_estimate"
16925 | "db_distinct_estimate_lpc" | "db_distinct_estimate_hll"
16926 | "db_throttle_token_step" | "db_leaky_bucket_step"
16927 | "db_token_bucket_step" | "db_circuit_breaker_step"
16928 | "db_two_phase_commit_step" | "db_three_phase_commit_step"
16929 | "db_paxos_propose_id" | "db_raft_term_advance"
16930 | "db_raft_log_match_check" | "db_zab_epoch_step"
16931 | "db_chubby_lease_step" | "db_logical_clock_step"
16932 | "db_lamport_timestamp" | "db_vector_clock_merge"
16933 | "db_hybrid_logical_clock" | "db_crdt_g_counter_merge"
16934 | "db_crdt_pn_counter_merge" | "db_crdt_lww_register_merge"
16935 | "db_crdt_set_or_merge" | "db_consensus_quorum_size"
16936 | "db_replication_lag_step" | "db_partitions_for_n"
16937 | "db_consistent_lookup_id" | "db_chord_finger_index"
16938 | "db_kademlia_xor_distance" | "db_pastry_routing_step"
16939 | "db_dht_replicate_factor" | "db_partition_failure_check"
16940 | "db_byzantine_quorum_size" | "db_pbft_view_change"
16941 | "db_honey_badger_step" | "db_avalanche_query_step"
16942 | "db_quorum_intersection_check" | "db_anti_entropy_step"
16943 | "db_merkle_node_hash" | "db_merkle_path_verify"
16944 | "db_gossip_fanout_step" | "db_anti_entropy_pull_step"
16945 | "db_split_brain_check" | "db_clock_skew_estimate"
16946 | "db_freshness_score" | "db_read_repair_step"
16947 | "db_hinted_handoff_step" | "db_compaction_score"
16948 | "db_levelled_compaction_step" | "db_size_tiered_compaction"
16949 | "db_universal_compaction_step" | "db_write_amplification"
16950 | "db_read_amplification" | "db_space_amplification"
16951 | "db_block_cache_hit_rate" | "db_page_cache_eviction_age"
16952 | "db_wal_fsync_cost" | "db_group_commit_count"
16953 | "db_replica_lag_threshold" | "db_synchronous_commit_check"
16954 | "db_async_commit_check" | "db_eventual_consistency_check"
16955 | "db_strong_consistency_check" | "db_linearizability_check"
16956 | "db_causal_consistency_check"
16957 | "net_tcp_cwnd_step" | "net_tcp_ssthresh_update"
16959 | "net_tcp_reno_step" | "net_tcp_cubic_step"
16960 | "net_tcp_bbr_step" | "net_tcp_vegas_step"
16961 | "net_tcp_westwood_step" | "net_tcp_compound_step"
16962 | "net_tcp_dctcp_step" | "net_tcp_yeah_step"
16963 | "net_tcp_htcp_step" | "net_tcp_hybla_step"
16964 | "net_tcp_illinois_step" | "net_tcp_lp_step"
16965 | "net_tcp_scalable_step" | "net_tcp_veno_step"
16966 | "net_aiad_step" | "net_aimd_step"
16967 | "net_miad_step" | "net_mimd_step"
16968 | "net_aqm_red_drop_prob" | "net_aqm_codel_target"
16969 | "net_aqm_pie_drop_rate" | "net_aqm_fq_codel_step"
16970 | "net_aqm_blue_step" | "net_aqm_choke_step"
16971 | "net_aqm_sfq_step" | "net_aqm_drr_step"
16972 | "net_aqm_wrr_step" | "net_token_rate_limit"
16973 | "net_traffic_shaper_step" | "net_priority_queue_index"
16974 | "net_packet_loss_estimate" | "net_jitter_estimate"
16975 | "net_latency_avg" | "net_rtt_smoothed"
16976 | "net_rtt_variation" | "net_rto_compute"
16977 | "net_bandwidth_delay_product" | "net_path_capacity_kleinrock"
16978 | "net_loss_rate_to_throughput" | "net_throughput_padhye"
16979 | "net_throughput_mathis" | "net_throughput_response"
16980 | "net_router_buffer_size" | "net_drop_tail_check"
16981 | "net_burst_size_compute" | "net_packet_pacing_step"
16982 | "net_link_capacity_share" | "net_proportional_fair_share"
16983 | "net_max_min_fair_step" | "net_alpha_fair_step"
16984 | "net_kelly_pricing_step" | "net_network_utility_max"
16985 | "net_lyapunov_drift_plus_penalty" | "net_backpressure_step"
16986 | "net_max_weight_match" | "net_qcsma_propose"
16987 | "net_csma_back_off" | "net_alohanet_throughput"
16988 | "net_slotted_aloha_throughput" | "net_csma_efficiency"
16989 | "net_token_ring_efficiency" | "net_polling_efficiency"
16990 | "net_radio_path_loss" | "net_friis_received_power"
16991 | "net_two_ray_ground_loss" | "net_okumura_hata_loss"
16992 | "net_log_distance_path" | "net_shadowing_normal"
16993 | "net_rician_k_factor" | "net_rayleigh_envelope"
16994 | "net_doppler_shift" | "net_capacity_shannon"
16995 | "net_mimo_capacity_step" | "net_zero_forcing_beam"
16996 | "net_mmse_beam_step" | "net_water_filling_power"
16997 | "net_amc_threshold_index" | "net_harq_combining_gain"
16998 | "net_turbo_decode_iter" | "net_ldpc_iteration_step"
16999 | "net_polar_decode_step" | "net_viterbi_step"
17000 | "net_bcjr_step" | "net_outage_probability"
17001 | "net_diversity_gain" | "net_array_gain"
17002 | "net_multiplexing_gain" | "net_coding_gain"
17003 | "net_pruning_gain" | "net_macro_diversity_step"
17004 | "net_micro_diversity_step" | "net_handoff_threshold"
17005 | "net_call_admission_check" | "net_blocking_probability"
17006 | "net_erlang_b_formula" | "net_erlang_c_formula"
17007 | "net_engset_formula" | "net_little_law_l"
17008 | "net_throughput_law" | "net_response_time_law"
17009 | "net_utilization_law" | "net_forced_flow_law"
17010 | "os_priority_aging_step" | "os_mlfq_demote_step"
17012 | "os_mlfq_promote_step" | "os_round_robin_quantum"
17013 | "os_completely_fair_vruntime" | "os_lottery_ticket_count"
17014 | "os_stride_pass_step" | "os_eevdf_eligible"
17015 | "os_cfs_load_balance_step" | "os_eas_energy_estimate"
17016 | "os_smt_threading_share" | "os_numa_node_distance"
17017 | "os_cpu_affinity_score" | "os_thread_migration_cost"
17018 | "os_load_average_decay" | "os_runqueue_depth"
17019 | "os_io_scheduler_deadline" | "os_io_scheduler_cfq_step"
17020 | "os_io_scheduler_noop_step" | "os_io_scheduler_bfq_step"
17021 | "os_io_scheduler_kyber_step" | "os_io_scheduler_mq_deadline"
17022 | "os_anticipation_window" | "os_elevator_step"
17023 | "os_disk_seek_time" | "os_disk_rotational_lat"
17024 | "os_disk_transfer_time" | "os_pre_fetch_window"
17025 | "os_buffer_cache_pages" | "os_dirty_page_threshold"
17026 | "os_writeback_step" | "os_swappiness_factor"
17027 | "os_kswapd_wake_threshold" | "os_oom_score_step"
17028 | "os_page_replacement_lru" | "os_page_replacement_clock"
17029 | "os_page_replacement_2q" | "os_working_set_size"
17030 | "os_thrashing_threshold" | "os_demand_paging_step"
17031 | "os_copy_on_write_check" | "os_zero_page_optimization"
17032 | "os_huge_page_threshold" | "os_transparent_hugepage"
17033 | "os_kasan_shadow_offset" | "os_kfence_check"
17034 | "os_kfence_alloc_index" | "os_slub_object_size_round"
17035 | "os_slab_color_offset" | "os_per_cpu_cache_size"
17036 | "os_buddy_order_pick" | "os_compact_memory_step"
17037 | "os_kvm_vmcs_field_offset" | "os_apic_irq_priority"
17038 | "os_msi_x_vector_count" | "os_iommu_domain_step"
17039 | "os_pci_bus_address" | "os_acpi_state_transition"
17040 | "os_cpufreq_governor_step" | "os_intel_pstate_target"
17041 | "os_amd_pstate_target" | "os_thermal_zone_trip"
17042 | "os_throttle_temperature" | "os_battery_capacity_pct"
17043 | "os_powertop_score" | "os_idle_state_select"
17044 | "os_c_state_residency" | "os_p_state_voltage"
17045 | "os_dvfs_step" | "os_voltage_scaling_step"
17046 | "os_frequency_scaling_step" | "os_inotify_event_count"
17047 | "os_epoll_ctl_count" | "os_io_uring_sqe_count"
17048 | "os_io_uring_cqe_count" | "os_kqueue_event_count"
17049 | "os_systemd_journal_size" | "os_dmesg_severity_level"
17050 | "os_audit_event_priority" | "os_apparmor_profile_active"
17051 | "os_selinux_context_match" | "os_smack_label_compare"
17052 | "os_capability_check" | "os_seccomp_filter_step"
17053 | "os_namespace_isolation" | "os_cgroup_v1_count"
17054 | "os_cgroup_v2_count" | "os_pid_max_value"
17055 | "os_thread_max_value" | "os_file_max_value"
17056 | "os_open_files_count" | "os_socket_max_value"
17057 | "os_inotify_max_watches" | "os_oom_kill_score"
17058 | "os_zswap_compress_ratio" | "os_zram_compress_ratio"
17059 | "os_swap_pressure_score" | "os_pressure_stall_step"
17060 | "os_psi_avg10_step" | "os_psi_avg60_step"
17061 | "os_psi_avg300_step" | "os_load_proc_avg"
17062 | "os_load_user_avg" | "os_load_iowait_avg"
17063 | "sec_argon2_memcost" | "sec_argon2_timecost"
17065 | "sec_argon2_parallelism" | "sec_argon2_block_step"
17066 | "sec_pbkdf2_iter" | "sec_scrypt_n_param"
17067 | "sec_scrypt_r_param" | "sec_scrypt_p_param"
17068 | "sec_balloon_hash_step" | "sec_yescrypt_step"
17069 | "sec_bcrypt_cost_factor" | "sec_bcrypt_round_step"
17070 | "sec_password_strength_zxcvbn" | "sec_haveibeenpwned_check"
17071 | "sec_diceware_word_index" | "sec_xkcd_passphrase_score"
17072 | "sec_passphrase_entropy" | "sec_chosen_charset_strength"
17073 | "sec_keystroke_timing_var" | "sec_2fa_totp_window"
17074 | "sec_totp_drift_check" | "sec_hotp_counter_step"
17075 | "sec_yubikey_otp_check" | "sec_webauthn_attestation_check"
17076 | "sec_fido2_assertion_check" | "sec_certificate_chain_depth"
17077 | "sec_revocation_ocsp_check" | "sec_crl_age_seconds"
17078 | "sec_pki_path_validate" | "sec_x509_subject_match"
17079 | "sec_san_match_count" | "sec_basic_constraints_ca"
17080 | "sec_pinning_compare" | "sec_certificate_transparency"
17081 | "sec_dane_tlsa_match" | "sec_hpkp_pin_match"
17082 | "sec_csp_directive_match" | "sec_csrf_token_match"
17083 | "sec_cors_origin_match" | "sec_xss_filter_score"
17084 | "sec_html_escape_check" | "sec_url_safe_encode_check"
17085 | "sec_path_traversal_detect" | "sec_sqli_pattern_score"
17086 | "sec_xxe_pattern_score" | "sec_xxe_dtd_check"
17087 | "sec_command_injection_score" | "sec_idor_check"
17088 | "sec_jwt_alg_safe" | "sec_jwt_kid_match"
17089 | "sec_jwt_signature_verify" | "sec_oauth2_state_validate"
17090 | "sec_oauth2_pkce_step" | "sec_oauth_nonce_check"
17091 | "sec_session_lifetime" | "sec_idle_timeout_step"
17092 | "sec_login_throttle_step" | "sec_account_lockout_step"
17093 | "sec_password_history_check" | "sec_complexity_policy_score"
17094 | "sec_dictionary_attack_check" | "sec_brute_force_attempts"
17095 | "sec_credential_stuffing_score" | "sec_kerberos_ticket_age"
17096 | "sec_kerberos_pac_check" | "sec_kerberos_pre_auth"
17097 | "sec_ldap_bind_step" | "sec_radius_auth_step"
17098 | "sec_diameter_avp_step" | "sec_saml_assertion_age"
17099 | "sec_oidc_id_token_age" | "sec_acme_dns_challenge"
17100 | "sec_dnssec_signature_check" | "sec_spf_pass_check"
17101 | "sec_dkim_signature_check" | "sec_dmarc_policy_check"
17102 | "sec_arc_chain_step" | "sec_smtp_ssl_check"
17103 | "sec_imap_starttls_check" | "sec_pop3_security_step"
17104 | "sec_tls_alert_severity" | "sec_tls13_handshake_step"
17105 | "sec_tls12_handshake_step" | "sec_tls11_deprecation_check"
17106 | "sec_ssl3_disabled_check" | "sec_cipher_suite_strength"
17107 | "sec_cbc_mac_block_count" | "sec_gcm_iv_unique_check"
17108 | "sec_chachapoly_nonce_check" | "sec_x25519_clamping_step"
17109 | "sec_ed25519_signature_step" | "sec_ed448_signature_step"
17110 | "sec_p384_curve_step" | "sec_secp256k1_step"
17111 | "sec_blake3_chunk_step" | "sec_keccak_round_step"
17112 | "sec_sha3_padding_step" | "sec_argon2_state_advance"
17113 | "sec_chacha20_quarterround" | "sec_aes_round_step"
17114 | "sec_aes_keyschedule_step" | "sec_des_round_step"
17115 | "sec_blowfish_round_step" | "sec_serpent_round_step"
17116 | "sec_twofish_round_step"
17117 | "fixed_from_gregorian" | "gregorian_from_fixed"
17119 | "fixed_from_julian" | "julian_from_fixed"
17120 | "iso_week_date" | "hebrew_leap_year"
17121 | "hebrew_year_length" | "fixed_from_hebrew"
17122 | "islamic_leap_year" | "fixed_from_islamic"
17123 | "persian_arithmetic_leap" | "fixed_from_persian"
17124 | "coptic_from_fixed" | "ethiopic_from_fixed"
17125 | "french_revolutionary_leap" | "fixed_from_french"
17126 | "chinese_year_zodiac" | "chinese_lunation_winter"
17127 | "hindu_solar_year" | "hindu_lunisolar_month"
17128 | "maya_long_count_from_fixed" | "mayan_haab_from_fixed"
17129 | "mayan_tzolkin_from_fixed" | "badi_year_from_fixed"
17130 | "bahai_from_fixed" | "easter_gregorian_year"
17131 | "easter_orthodox_year" | "easter_julian_year"
17132 | "day_of_week_zeller" | "iso_day_number"
17133 | "weekday_name_short" | "leap_year_gregorian"
17134
17135 | "dnorm" | "dt" | "df_dist" | "dchisq"
17137 | "glm" | "aov" | "shapiro_wilk" | "anderson_darling"
17138 | "kolmogorov_smirnov" | "spearmanr" | "kendalltau" | "pearsonr"
17139 | "mannwhitneyu" | "wilcoxon" | "kruskal_h"
17140
17141 | "iota_n" | "reduce_axis" | "scan_axis" | "fold_axis"
17143 | "rotate_axis" | "transpose_axis" | "reshape_dim"
17144 | "encode_base" | "decode_base" | "nub_list" | "nub_count"
17145 | "membership_idx" | "deal_n_k" | "roll_n"
17146 | "permute_idx" | "invert_perm"
17147
17148 | "julian_day" | "jd_to_calendar" | "tt_to_tdb"
17150 | "ra_dec_to_alt_az" | "alt_az_to_ra_dec"
17151 | "precession_iau2006" | "nutation_iau2000a"
17152 | "aberration_annual" | "proper_motion_apply"
17153 | "parallax_correction" | "sun_position_low" | "sun_distance_au"
17154 | "moon_position_low" | "moon_phase_age" | "lunation_index"
17155 | "eclipse_magnitude" | "saros_cycle" | "metonic_cycle"
17156 | "orbit_kepler3" | "orbital_period_au" | "orbit_eccentric_anomaly"
17157 | "escape_velocity_body" | "hill_sphere_radius" | "tisserand_param"
17158 | "tle_mean_motion" | "sgp4_propagate_step" | "airy_disk_radius"
17159 | "rayleigh_criterion" | "strehl_ratio" | "au_to_km"
17160
17161 | "elo_expected" | "elo_update" | "glicko_rating"
17163 | "trueskill_update" | "trueskill_match_quality"
17164 | "pythagorean_expectation" | "war_above_replacement"
17165 | "woba_weight" | "wrc_plus" | "ops_plus" | "era_plus"
17166 | "fip" | "xfip" | "siera" | "babip" | "wpa"
17167 | "win_probability" | "leverage_index" | "clutch_score"
17168 | "shooting_pct" | "save_pct" | "corsi_for" | "fenwick_for"
17169 | "goals_above_avg" | "tackle_efficiency" | "yards_per_attempt"
17170 | "qbr_metric" | "epa_per_play"
17171
17172 | "vlookup" | "hlookup" | "xlookup" | "index_match"
17174 | "indirect" | "choose" | "offset"
17175 | "sumif" | "countif" | "averageif"
17176 | "sumifs" | "countifs" | "averageifs"
17177 | "sumproduct" | "rank_eq" | "rank_avg" | "percentrank"
17178 | "quartile_inc" | "quartile_exc"
17179 | "xnpv" | "ppmt" | "ipmt" | "rate"
17180 | "macauley_duration" | "convexity" | "yield_to_maturity"
17181 | "accrued_interest" | "clean_price" | "dirty_price"
17182 | "coupon_count" | "skill_score" | "reliability_diagram"
17183 | "taylor_diagram_score"
17184
17185 | "geohash_neighbors" | "h3_index" | "h3_geo_to_h3"
17187 | "h3_h3_to_geo" | "h3_k_ring" | "h3_neighbor" | "h3_resolution"
17188 | "s2_cell_id" | "s2_cell_at_lat_lng" | "s2_cell_neighbors"
17189 | "utm_from_lat_lng" | "utm_to_lat_lng"
17190 | "mgrs_encode" | "mgrs_decode"
17191 | "lat_lng_to_xy_mercator" | "lat_lng_to_xy_lambert"
17192 | "haversine_dist" | "vincenty_dist" | "andoyer_dist"
17193 | "rhumb_line_bearing"
17194 | "destination_point" | "tile_xyz_to_lat_lng" | "lat_lng_to_tile_xyz"
17195 | "polygon_winding_order" | "point_in_polygon_ray"
17196 | "point_in_polygon_winding" | "segment_intersection"
17197 | "segment_distance_point" | "convex_hull_chan"
17198
17199 | "pid_anti_windup" | "pid_ziegler_nichols"
17201 | "smith_predictor_step" | "lqr_gain_continuous"
17202 | "lqr_gain_discrete" | "lqg_step" | "h_infinity_norm"
17203 | "bode_gain_margin" | "bode_phase_margin"
17204 | "nyquist_encirclement" | "nichols_chart_step"
17205 | "servo_position_velocity" | "servo_torque_step"
17206 | "imu_madgwick_step" | "imu_mahony_step" | "quaternion_from_imu"
17207 | "denavit_hartenberg_h" | "forward_kinematics_dh"
17208 | "inverse_kinematics_2link" | "jacobian_2dof"
17209 | "manipulability_yoshikawa" | "singularity_check_2link"
17210 | "path_dubins_lsl" | "path_dubins_rsr" | "path_reeds_shepp"
17211 | "rrt_extend" | "rrt_star_rewire" | "prm_node_connect"
17212
17213 | "life_expectancy_e0" | "force_of_mortality" | "select_ultimate"
17215 | "annuity_due_an" | "annuity_immediate_an"
17216 | "term_life_a_n_t" | "whole_life_a"
17217 | "endowment_pure_e" | "endowment_combined_a"
17218 | "premium_net" | "level_premium"
17219 | "reserve_prospective" | "reserve_retrospective"
17220 | "gross_premium_load" | "experience_factor"
17221 | "mortality_table_q" | "select_period_step"
17222 | "multi_decrement_q" | "multi_state_pij"
17223 | "credibility_buhlmann" | "loss_severity_lognormal"
17224 | "loss_frequency_poisson" | "ruin_probability_lundberg"
17225 | "cramer_lundberg_step" | "bornhuetter_ferguson"
17226 | "chain_ladder_step" | "ibnr_estimate" | "run_off_triangle_step"
17227
17228 | "r_naught_basic" | "r_effective_t" | "doubling_time_growth"
17230 | "sirs_step" | "seirs_step" | "susceptible_to_infected"
17231 | "attack_rate" | "vaccination_coverage_required"
17232 | "cfr_case_fatality" | "ifr_infection_fatality"
17233 | "dalys_disability_weight" | "qaly_lifetime" | "ylll_pml"
17234 | "rt_serial_interval" | "generation_time_step"
17235 | "gini_inequality_health" | "standardized_mortality_smr"
17236 | "indirect_age_adjusted" | "direct_age_adjusted"
17237 | "odds_ratio_2x2" | "risk_ratio_2x2" | "number_needed_to_treat"
17238 | "attributable_fraction_pop" | "preventive_fraction"
17239 | "contact_tracing_eff" | "cluster_attack_rate"
17240 | "transmission_pair_index"
17241
17242 | "tar_header_checksum" | "tar_pad_512" | "tar_member_record"
17244 | "zip_local_header" | "zip_central_dir" | "zip_eocd"
17245 | "gzip_member_step" | "gzip_crc32_init" | "gzip_isize"
17246 | "deflate_dynamic_huffman" | "deflate_static_block"
17247 | "lz4_block_step" | "lz4_match_offset"
17248 | "zstd_frame_header" | "brotli_huffman_table"
17249 | "brotli_meta_block" | "lzma_range_step"
17250 | "quoted_printable_encode" | "uuencode_step"
17251 | "modhex_encode" | "percent_encode_full"
17252 | "punycode_encode" | "idn_to_ascii" | "idn_to_unicode"
17253 | "msgpack_pack_int" | "msgpack_pack_str"
17254 | "cbor_encode_uint" | "cbor_encode_str"
17255
17256 | "molecular_weight_compound" | "molarity_dilution"
17258 | "gas_constant_value" | "eyring_rate" | "van_t_hoff_kp"
17259 | "henderson_buffer" | "titration_ph_endpoint"
17260 | "isoelectric_point_protein" | "ka_to_pka" | "pkb_to_kb"
17261 | "amphoteric_check" | "oxidation_number"
17262 | "half_reaction_balance" | "redox_potential_cell"
17263 | "electrolysis_mass" | "spectrophotometer_beer_lambert"
17264 | "epsilon_extinction" | "transmittance_to_a"
17265 | "crystal_field_ligand" | "jahn_teller_check"
17266 | "vsepr_geometry" | "lewis_dot_count"
17267 | "formal_charge" | "resonance_count"
17268 | "ramachandran_phi_psi" | "rg_radius_of_gyration"
17269 | "spectroscopic_factor" | "avogadro_constant"
17270
17271 | "cents_between_freqs" | "note_name_from_midi"
17273 | "interval_quality_size" | "scale_pitches_major"
17274 | "scale_pitches_minor" | "mode_pitches_dorian"
17275 | "mode_pitches_phrygian" | "mode_pitches_lydian"
17276 | "chord_root_inversion" | "chord_quality_classify"
17277 | "chord_voicing_close" | "key_signature_sharps"
17278 | "key_signature_flats" | "tempo_to_ms" | "beat_to_seconds"
17279 | "time_sig_subdivision" | "equal_tempered_freq"
17280 | "just_intonation_freq" | "pythagorean_freq"
17281 | "mean_tone_freq" | "werckmeister_iii" | "kirnberger_iii"
17282 | "dynamics_db_level" | "harmonics_partial"
17283
17284 | "moment_magnitude_mw" | "richter_local_ml"
17286 | "surface_wave_ms" | "body_wave_mb"
17287 | "gutenberg_richter_b" | "omori_aftershock"
17288 | "pga_attenuation" | "arias_intensity" | "shake_map_pga"
17289 | "liquefaction_potential_index" | "spt_n_correction"
17290 | "mineral_mohs_hardness" | "streak_color_index"
17291 | "specific_gravity_water" | "feldspar_classify"
17292 | "silicate_classify" | "igneous_qapf"
17293 | "metamorphic_grade" | "crustal_density_depth"
17294 | "pwave_velocity_depth" | "swave_velocity_depth"
17295 | "gradient_geothermal" | "heat_flow_radiogenic"
17296
17297 | "dgemm" | "sgemm" | "zgemm" | "cgemm"
17299 | "dgemv" | "sgemv" | "dtrsm" | "strsm"
17300 | "dgesv" | "dgetrf" | "dgeqrf" | "dgesvd"
17301 | "dsyevd" | "dpotrf" | "daxpy" | "ddot"
17302 | "dnrm2" | "dscal" | "dasum" | "idamax"
17303 | "dsyrk" | "dgerqf" | "dorgqr" | "dorglq"
17304 | "drot" | "drotg" | "dpbsv" | "dgbsv"
17305 | "dtbsv" | "dtrsv" | "ddrot" | "dgemm3m"
17306 | "dgels" | "dgelsd"
17307
17308 | "cnf_unit_propagate" | "cnf_pure_literal_elim"
17310 | "cnf_dpll_branch" | "dpll_clause_learning"
17311 | "two_watched_literals" | "walksat_step"
17312 | "resolution_step" | "subsumption_check"
17313 | "tableau_branch_close" | "sequent_left_intro"
17314 | "sequent_right_intro" | "nbe_normalize"
17315 | "church_numeral_n" | "encode_pair" | "encode_succ"
17316 | "simply_typed_check" | "hindley_milner_step"
17317 | "unification_robinson" | "bdd_apply" | "bdd_restrict"
17318 | "bdd_quantify" | "aig_simplify_step"
17319 | "smt_qf_lia_solve_step" | "smt_qf_uf_combine"
17320 | "model_checking_ctl" | "model_checking_ltl"
17321 | "bisimulation_step" | "coq_tactic_apply"
17322 | "coq_unify_term" | "refl_check" | "sym_check" | "trans_check"
17323
17324 | "nfa_to_dfa" | "subset_construction"
17326 | "dfa_minimize_hopcroft" | "regex_to_nfa_thompson"
17327 | "glushkov_construction" | "brzozowski_derivative"
17328 | "ll1_first_set" | "ll1_follow_set" | "ll1_predict_table"
17329 | "lr0_items_step" | "lalr_lookahead_compute"
17330 | "lr1_canonical_collection"
17331 | "earley_scan" | "earley_predict" | "earley_complete"
17332 | "packrat_parse_step" | "ascent_parser_step"
17333 | "pratt_parse_step" | "shunting_yard_step"
17334 | "regex_compile_thompson" | "regex_match_dfa"
17335 | "lex_keyword_classify"
17336 | "peg_seq" | "peg_choice" | "peg_repeat" | "peg_lookahead"
17337 | "dfa_simulate_step" | "bytecode_disasm_step"
17338 | "ssa_phi_insert" | "dom_tree_idom" | "dominance_frontier"
17339
17340 | "porter_stem_step" | "snowball_stem_english"
17342 | "snowball_stem_french" | "lemmatize_wordnet"
17343 | "lemmatize_lemmy" | "stem_lancaster"
17344 | "soundex_phonetic" | "metaphone_phonetic"
17345 | "caverphone_2" | "nysiis_phonetic"
17346 | "match_rating_codex" | "daitch_mokotoff"
17347 | "viterbi_pos_tag" | "forward_backward_pos"
17348 | "crf_log_likelihood" | "bigram_perplexity"
17349 | "trigram_perplexity" | "ner_bilou_decode"
17350 | "constituency_cyk" | "dependency_parse_eisner"
17351 | "transition_arc_eager" | "transition_arc_standard"
17352 | "word_alignment_ibm1" | "word_alignment_ibm2"
17353 | "lexicalized_parse" | "coreference_singleton"
17354 | "anaphora_distance" | "head_finding_collins"
17355 | "tree_kernel_collins"
17356
17357 | "btrim" | "translate" | "ascii"
17359 | "regexp_split" | "regexp_matches" | "regexp_replace"
17360 | "json_build_object" | "jsonb_set"
17361 | "json_array_length" | "json_extract_path"
17362 | "json_strip_nulls" | "jsonb_pretty"
17363 | "jsonb_path_query" | "json_each"
17364 | "jsonb_array_length" | "jsonb_object_keys"
17365 | "jsonb_typeof" | "array_to_jsonb"
17366 | "ts_match" | "ts_rank" | "ts_headline"
17367 | "substring_similarity" | "levenshtein_dist"
17368 | "word_similarity" | "strict_word_similarity"
17369 | "hstore_to_array" | "array_to_hstore"
17370 | "string_agg" | "array_agg"
17371 | "corr_agg" | "covar_pop" | "covar_samp"
17372 | "regr_slope" | "regr_intercept" | "regr_r2"
17373 | "percentile_cont" | "percentile_disc" | "mode_agg"
17374 | "array_to_string" | "array_position" | "array_positions"
17375 | "array_remove" | "array_replace"
17376 | "xmlforest" | "xmlagg"
17377
17378 | "zadd" | "zrem" | "zrangebyscore"
17380 | "zrank" | "zrevrank" | "zincrby"
17381 | "zcard" | "zcount" | "zlexcount"
17382 | "lpush" | "rpush" | "lrange" | "lrem"
17383 | "hset" | "hget" | "hgetall" | "hlen"
17384 | "hkeys" | "hvals" | "hmset" | "hincrby"
17385 | "sadd" | "srem" | "smembers"
17386 | "sinter" | "sunion" | "sdiff"
17387 | "scard" | "sismember" | "spop"
17388 | "setex" | "setnx" | "expire"
17389 | "ttl" | "pttl" | "persist"
17390 | "incr" | "decr" | "incrby" | "decrby"
17391 | "getset" | "mset" | "mget" | "renamenx"
17392 | "dbsize" | "type_redis" | "exists_key"
17393 | "strlen" | "getrange" | "setrange" | "append_redis"
17394 | "bitcount" | "bitop" | "bitpos"
17395 | "pfadd" | "pfcount"
17396 | "geoadd" | "geodist" | "geohash"
17397 | "xadd" | "xlen" | "xrange"
17398 | "object_encoding" | "debug_object" | "cluster_slots"
17399
17400 | "argpartition" | "bincount" | "nonzero_count"
17402 | "flatnonzero" | "searchsorted" | "digitize"
17403 | "histogram_bin_edges" | "unique_count"
17404 | "polyfit_rmse"
17405 | "ellipk" | "ellipe"
17406 | "hyp1f1" | "hyp2f1" | "mathieu_b"
17407 | "spherical_jn" | "spherical_yn"
17408 | "jv" | "yn" | "iv" | "kv"
17409 | "airyai" | "airybi"
17410 | "polygamma" | "trigamma" | "loggamma"
17411 | "factorial2" | "factorialk"
17412 | "owens_t" | "marcum_q" | "voigt_profile"
17413 | "chebyt" | "chebyu" | "sph_harm"
17414 | "wofz" | "erfcx" | "erfi" | "dawsn"
17415 | "interp1d"
17416 | "convolve_full" | "convolve_valid" | "correlate_full"
17417 | "kron_product"
17418 | "simpson_rule" | "romberg_quad" | "fixed_quad"
17419 | "ode45_step" | "ode_lsoda" | "solve_ivp_step"
17420 | "root_brentq" | "root_newton" | "root_secant"
17421 | "fmin_powell" | "fmin_cobyla"
17422
17423 | "cobb_douglas" | "ces_production"
17425 | "leontief_input" | "leontief_output"
17426 | "slutsky_decompose"
17427 | "marshallian_demand" | "hicksian_demand"
17428 | "expenditure_function" | "indirect_utility"
17429 | "gale_shapley_step" | "deferred_acceptance"
17430 | "top_trading_cycle" | "vcg_payment" | "myerson_optimal"
17431 | "gini_market" | "hhi_concentration"
17432 | "cournot_eq" | "stackelberg_eq" | "bertrand_eq"
17433 | "monopoly_lerner"
17434 | "consumer_surplus" | "producer_surplus"
17435 | "deadweight_loss" | "tax_incidence"
17436 | "pareto_efficiency" | "edgeworth_box_alloc"
17437 | "social_welfare_utilitarian"
17438 | "social_welfare_rawls" | "social_welfare_nash"
17439 | "arrow_independence"
17440 | "vickrey_auction" | "first_price_seal"
17441 | "english_auction" | "dutch_auction"
17442 | "core_coalition" | "stable_matching_count"
17443 | "gale_optimal" | "pareto_dominance"
17444 | "lerner_index"
17445 | "price_elasticity" | "supply_elasticity"
17446 | "income_elasticity" | "engel_curve" | "cross_elasticity"
17447 | "diff_in_diff" | "did_estimator" | "rdd_estimate"
17448 | "hann_w" | "hamming_w" | "blackman_w" | "barthann_w"
17450 | "nuttall_w" | "flattop_w" | "parzen_window" | "tukey_w"
17451 | "taylor_window" | "dpss_window" | "kaiserord_step"
17452 | "butter_lp_re" | "butter_hp_mag"
17453 | "cheby1_lp" | "cheby2_lp" | "ellip_lp" | "bessel_lp"
17454 | "notch_filter"
17455 | "sosfilt_step" | "lfilter_zi_init" | "filtfilt_pad"
17456 | "freqz_eval" | "freqs_eval" | "group_delay_eval"
17457 | "impulse_response_n"
17458 | "tf2zpk_step" | "zpk2tf_step" | "tf2sos_step"
17459 | "zpk2sos_step" | "sos2tf_step"
17460 | "bilinear_xform" | "bilinear_zpk_xform"
17461 | "firwin_lowpass" | "firwin_highpass"
17462 | "firwin_bandpass" | "firwin_bandstop"
17463 | "firwin2_freq" | "remez_design"
17464 | "stft_step" | "istft_step"
17465 | "cwt_morlet" | "ricker_wavelet" | "mexican_hat_wavelet"
17466 | "coherence_xy" | "csd_xy" | "welch_psd_avg"
17467 | "periodogram_basic" | "lombscargle_freq"
17468 | "hilbert_signal" | "envelope_amplitude"
17469 | "deconvolve_step" | "fftconvolve_step" | "oaconvolve_step"
17470 | "upfirdn_step" | "resample_poly_step" | "decimate_step"
17471 | "savgol_coef" | "detrend_linear"
17472 | "wiener_filter" | "medfilt_1d" | "peak_widths_at"
17473 | "dijkstra_relax" | "bellman_ford_relax"
17475 | "floyd_warshall_step" | "johnson_reweight"
17476 | "astar_search" | "bidirectional_dijkstra"
17477 | "yen_k_shortest" | "ida_star"
17478 | "bfs_count" | "dfs_postorder_done" | "topo_kahn_step"
17479 | "tarjan_scc_step" | "kosaraju_step"
17480 | "kruskal_step" | "prim_step" | "boruvka_step"
17481 | "reverse_delete_step"
17482 | "ford_fulkerson_step" | "edmonds_karp_bfs"
17483 | "dinic_step" | "push_relabel_relabel"
17484 | "stoer_wagner_step" | "karger_step"
17485 | "pagerank_iter" | "hits_authority" | "hits_hub"
17486 | "personalized_pagerank"
17487 | "centrality_degree" | "centrality_closeness"
17488 | "centrality_betweenness" | "centrality_eigenvector"
17489 | "centrality_katz" | "harmonic_centrality" | "load_centrality"
17490 | "clustering_coefficient" | "triangles_count" | "transitivity"
17491 | "modularity_score" | "louvain_gain"
17492 | "label_propagation" | "girvan_newman"
17493 | "articulation_point" | "bridge_edge"
17494 | "edge_connectivity" | "vertex_connectivity"
17495 | "biconnected_components"
17496 | "gx_diameter" | "gx_radius" | "gx_eccentricity"
17497 | "warshall_step"
17498 | "tsp_held_karp" | "tsp_nn_step" | "tsp_christofides"
17499 | "graph_coloring_greedy" | "welsh_powell"
17500 | "vf2_consistent" | "subgraph_isomorphism"
17501 | "hungarian_step" | "hopcroft_karp_step"
17502 | "bron_kerbosch"
17503 | "min_vertex_cover" | "max_independent_set"
17504 | "dominating_set_greedy" | "hamiltonian_path"
17505 | "min_steiner_tree" | "k_shortest_spanning"
17506 | "random_walk_hitting" | "simrank"
17507 | "df_groupby" | "df_aggregate" | "df_apply"
17509 | "df_transform" | "df_pivot" | "df_pivot_table"
17510 | "df_melt" | "df_stack" | "df_unstack"
17511 | "df_explode" | "df_get_dummies" | "df_crosstab"
17512 | "df_merge" | "df_join" | "df_concat"
17513 | "df_resample" | "df_rolling" | "df_expanding"
17514 | "df_ewm" | "df_shift" | "df_diff"
17515 | "df_pct_change" | "df_corr" | "df_cov"
17516 | "df_corrwith" | "df_describe" | "df_kurtosis"
17517 | "df_skew" | "df_sem" | "df_mad"
17518 | "df_dropna" | "df_fillna" | "df_interpolate"
17519 | "df_replace" | "df_isnull" | "df_notnull"
17520 | "df_sort_values" | "df_rank" | "df_quantile"
17521 | "df_value_counts" | "df_sample" | "df_nlargest"
17522 | "df_nsmallest" | "df_idxmax" | "df_idxmin"
17523 | "df_clip" | "df_round" | "df_to_datetime"
17524 | "df_to_timedelta" | "df_to_numeric" | "df_eval"
17525 | "df_query" | "df_filter" | "df_drop_duplicates"
17526 | "df_duplicated" | "df_set_index" | "df_reset_index"
17527 | "image_resize" | "image_grayscale" | "image_threshold"
17529 | "image_blur_gaussian" | "image_blur_box" | "image_sharpen"
17530 | "image_edge_canny" | "image_edge_sobel" | "image_edge_laplacian"
17531 | "image_dilate" | "image_erode" | "image_morphology_open"
17532 | "image_morphology_close" | "image_histogram" | "image_equalize"
17533 | "image_clahe" | "image_contrast" | "image_brightness"
17534 | "image_gamma" | "image_invert" | "image_sepia"
17535 | "image_posterize" | "image_solarize" | "convolve_2d"
17536 | "filter_median" | "filter_bilateral" | "filter_nlmeans"
17537 | "gabor_filter" | "hog_features" | "harris_corners"
17538 | "shi_tomasi_corners" | "sift_keypoints" | "orb_keypoints"
17539 | "surf_keypoints" | "template_match" | "face_detect_haar"
17540 | "watershed_segment" | "slic_superpixels" | "felzenszwalb_segment"
17541 | "graph_cut_segment" | "hough_lines" | "hough_circles"
17542 | "ransac_homography" | "optical_flow_lk" | "optical_flow_farneback"
17543 | "corner_subpix" | "image_rotate" | "image_flip_h"
17544 | "image_flip_v" | "image_emboss" | "image_motion_blur"
17545 | "arima_fit" | "arima_forecast" | "arma_order_select"
17547 | "sarimax_fit" | "garch_fit" | "ewma_smooth"
17548 | "holt_winters_additive" | "holt_winters_multiplicative" | "kalman_filter_step"
17549 | "kalman_smoother_step" | "var_fit" | "vecm_fit"
17550 | "johansen_test" | "phillips_perron" | "adfuller"
17551 | "kpss_test" | "breusch_godfrey" | "ljung_box_q"
17552 | "durbin_watson_d" | "granger_causality" | "cointegration_eg"
17553 | "seasonal_decompose" | "stl_decompose" | "acf_basis"
17554 | "pacf_basis" | "moving_average_filter" | "exp_smooth_simple"
17555 | "exp_smooth_double" | "markov_switching_ar" | "markov_switching_mr"
17556 | "arch_lm" | "state_space_kalman" | "ucm_unobserved_components"
17557 | "spectral_density_estimate" | "bayesian_step" | "pivoted_cholesky_var"
17558 | "sk_logistic_predict" | "sk_logistic_fit" | "sk_random_forest_fit"
17560 | "sk_gbt_fit" | "sk_xgb_fit" | "sk_lightgbm_fit"
17561 | "sk_svm_fit" | "sk_kmeans_fit" | "sk_dbscan_fit"
17562 | "sk_agglomerative_fit" | "sk_pca_fit" | "sk_tsne_fit"
17563 | "sk_umap_fit" | "sk_isolation_forest_fit" | "sk_lof_fit"
17564 | "sk_kfold_split" | "sk_stratified_kfold" | "sk_cross_val_score"
17565 | "sk_grid_search" | "sk_random_search" | "sk_bayes_search"
17566 | "sk_pipeline_fit" | "sk_standard_scaler" | "sk_min_max_scaler"
17567 | "sk_robust_scaler" | "sk_quantile_transform" | "sk_power_transform"
17568 | "sk_one_hot" | "sk_ordinal_encode" | "sk_label_encode"
17569 | "sk_tfidf" | "sk_count_vectorize" | "sk_silhouette"
17570 | "sk_calinski_harabasz" | "sk_davies_bouldin" | "sk_adjusted_rand"
17571 | "sk_mutual_info" | "sk_lda_topic" | "sk_nmf_topic"
17572 | "sk_word2vec_train" | "sk_doc2vec_train" | "sk_naive_bayes_predict"
17573 | "sk_knn_predict" | "sk_decision_tree_split"
17574 | "qubit_x" | "qubit_y" | "qubit_z"
17576 | "qubit_h" | "qubit_s" | "qubit_t"
17577 | "qubit_rx" | "qubit_ry" | "qubit_rz"
17578 | "qubit_u3" | "qubit_u2" | "qubit_u1"
17579 | "qubit_phase" | "qubit_cnot" | "qubit_cz"
17580 | "qubit_swap" | "qubit_ccx" | "qubit_measure"
17581 | "qubit_reset" | "bell_state" | "ghz_state"
17582 | "w_state" | "qft" | "inverse_qft"
17583 | "grover_iter" | "shor_period" | "vqe_step"
17584 | "qaoa_step" | "qpe_iteration" | "pauli_string_expect"
17585 | "circuit_depth" | "circuit_width" | "gate_decompose"
17586 | "ancilla_alloc" | "bloch_sphere_x" | "bloch_sphere_z"
17587 | "density_matrix_purity_q" | "entanglement_entropy" | "quantum_teleportation"
17588 | "superdense_coding" | "noise_model_depolarize"
17589 | "mirr_excel" | "accrint" | "cumipmt"
17591 | "cumprinc" | "dollarde" | "dollarfr"
17592 | "received" | "yieldmat" | "yielddisc"
17593 | "duration_macaulay" | "mduration" | "odddyield"
17594 | "disc_excel" | "effect" | "nominal"
17595 | "intrate" | "price_disc" | "cityhash64"
17596 | "farmhash_64" | "metro_hash_64" | "spookyhash_128"
17597 | "t1ha" | "highway_hash" | "fnv0_32"
17598 | "lose_lose"
17599 | "oat_hash" | "lz4_encode_block" | "snappy_encode"
17600 | "zstd_encode_step" | "brotli_encode_meta" | "lzma_encode_step"
17601 | "bz2_encode_step" | "lzo_encode_step" | "deflate_encode_huffman"
17602 | "lzw_encode" | "gzip_encode_step" | "uri_template_expand"
17603 | "uri_resolve" | "uri_normalize" | "percent_decode_url"
17604 | "url_encode_form" | "url_decode_form" | "punycode_decode_step"
17605 | "idn_normalize" | "url_origin" | "etag_validate"
17606 | "cache_control_parse" | "vary_match" | "content_negotiate"
17607 | "accept_lang_pick" | "range_header_parse" | "if_match_check"
17608 | "if_none_match_check" | "digest_auth_quote" | "www_auth_parse"
17609 | "iso8601_duration_parse" | "iso8601_duration_to_seconds" | "rrule_next_occurrence"
17611 | "cron_next_fire" | "date_round_iso" | "week_number_iso"
17612 | "fiscal_year_us" | "age_at_date" | "easter_western"
17613 | "easter_orthodox_year_2" | "chinese_new_year" | "solstice_winter"
17614 | "equinox_spring" | "rgb_to_oklab" | "oklab_to_rgb"
17615 | "rgb_to_cmyk" | "cmyk_to_rgb" | "rgb_to_xyz"
17616 | "xyz_to_rgb" | "rgb_to_yuv" | "yuv_to_rgb"
17617 | "luminance_relative" | "contrast_ratio" | "wcag_pass"
17618 | "color_temperature_kelvin" | "delta_e76" | "delta_e94"
17619 | "delta_e2000" | "color_blend_alpha" | "isbn10_check"
17620 | "isbn13_check" | "ean13_check" | "upc_check"
17621 | "eth_addr_check" | "btc_addr_check" | "ssn_check"
17622 | "vin_check" | "imei_check" | "iban_check"
17623 | "cusip_check" | "kde_silverman_bw" | "kde_scott_bw"
17624 | "kde_bandwidth_lscv" | "kde_epanechnikov" | "kde_gaussian_2d"
17625 | "kde_uniform" | "kde_triangular" | "kde_biweight"
17626 | "kde_triweight" | "kde_cosine" | "kde_logistic_kernel"
17627 | "mod_exp" | "modexp" | "powmod"
17629 | "mod_inv" | "modinv" | "chinese_remainder" | "crt"
17630 | "miller_rabin" | "millerrabin" | "is_probable_prime"
17631 | "derangements" | "stirling2" | "stirling_second"
17633 | "bernoulli_number" | "bernoulli" | "harmonic_number" | "harmonic"
17634 | "drag_force" | "fdrag" | "ideal_gas" | "pv_nrt"
17636 | "bs_delta" | "bsdelta" | "option_delta"
17638 | "bs_gamma" | "bsgamma" | "option_gamma"
17639 | "bs_vega" | "bsvega" | "option_vega"
17640 | "bs_theta" | "bstheta" | "option_theta"
17641 | "bs_rho" | "bsrho" | "option_rho"
17642 | "bond_duration" | "mac_duration"
17643 | "dct" | "idct" | "goertzel" | "chirp" | "chirp_signal"
17645 | "base85_encode" | "b85e" | "ascii85_encode" | "a85e"
17647 | "base85_decode" | "b85d" | "ascii85_decode" | "a85d"
17648 | "pnorm" | "pbinom" | "dbinom" | "ppois"
17650 | "punif" | "pexp" | "pweibull" | "plnorm" | "pcauchy"
17651 | "rbind" | "cbind"
17653 | "row_sums" | "rowSums" | "col_sums" | "colSums"
17654 | "row_means" | "rowMeans" | "col_means" | "colMeans"
17655 | "outer" | "crossprod" | "tcrossprod"
17656 | "nrow" | "ncol" | "prop_table" | "proptable"
17657 | "cummax" | "cummin" | "scale_vec" | "scale"
17659 | "which_fn" | "tabulate"
17660 | "duplicated" | "duped" | "rev_vec"
17661 | "seq_fn" | "rep_fn" | "rep"
17662 | "cut_bins" | "cut" | "find_interval" | "findInterval"
17663 | "ecdf_fn" | "ecdf" | "density_est" | "density"
17664 | "embed_ts" | "embed"
17665 | "shapiro_test" | "shapiro" | "ks_test" | "ks"
17667 | "wilcox_test" | "wilcox" | "mann_whitney"
17668 | "prop_test" | "proptest" | "binom_test" | "binomtest"
17669 | "sapply" | "tapply" | "do_call" | "docall"
17671 | "kmeans" | "prcomp" | "pca"
17673 | "pgamma" | "pbeta" | "pchisq" | "pt_cdf" | "pt" | "pf_cdf" | "pf"
17681 | "dgeom" | "dunif" | "dnbinom" | "dhyper"
17683 | "lowess" | "loess" | "approx_fn" | "approx"
17685 | "lm_fit" | "lm"
17687 | "qt_fn" | "qf_fn"
17689
17690 | "acf_fn" | "acf" | "pacf_fn" | "pacf"
17692 | "diff_lag" | "diff_ts" | "ts_filter" | "filter_ts"
17693 | "predict_lm" | "predict" | "confint_lm" | "confint"
17695 | "cor_matrix" | "cor_mat" | "cov_matrix" | "cov_mat"
17697 | "mahalanobis" | "mahal" | "dist_matrix" | "dist_mat"
17698 | "hclust" | "cutree" | "weighted_var" | "wvar" | "cov2cor"
17699 | "scatter_svg" | "scatter_plot" | "line_svg" | "line_plot"
17701 | "plot_svg" | "hist_svg" | "histogram_svg"
17702 | "boxplot_svg" | "box_plot" | "bar_svg" | "barchart_svg"
17703 | "pie_svg" | "pie_chart" | "heatmap_svg" | "heatmap"
17704 | "donut_svg" | "donut" | "area_svg" | "area_chart"
17705 | "hbar_svg" | "hbar" | "radar_svg" | "radar" | "spider"
17706 | "candlestick_svg" | "candlestick" | "ohlc"
17707 | "violin_svg" | "violin" | "cor_heatmap" | "cor_matrix_svg"
17708 | "stacked_bar_svg" | "stacked_bar"
17709 | "wordcloud_svg" | "wordcloud" | "wcloud"
17710 | "treemap_svg" | "treemap"
17711 | "pvw"
17712 | "cyber_city" | "cyber_grid" | "cyber_rain" | "matrix_rain"
17714 | "cyber_glitch" | "glitch_text" | "cyber_banner" | "neon_banner"
17715 | "cyber_circuit" | "cyber_skull" | "cyber_eye"
17716 | "ai" | "ai_agent" | "prompt" | "stream_prompt" | "stream_prompt_cb"
17718 | "tokens_of"
17719 | "ai_estimate" | "ai_cost" | "ai_history" | "ai_history_clear"
17720 | "ai_cache_clear" | "ai_cache_size"
17721 | "ai_mock_install" | "ai_mock_clear"
17722 | "ai_config_get" | "ai_config_set" | "ai_routing_get" | "ai_routing_set"
17723 | "ai_register_tool" | "ai_unregister_tool" | "ai_clear_tools" | "ai_tools_list"
17724 | "ai_filter" | "ai_map" | "ai_classify" | "ai_match" | "ai_sort" | "ai_dedupe"
17725 | "ai_extract" | "ai_summarize" | "ai_translate" | "ai_template"
17726 | "ai_session_new" | "ai_session_send" | "ai_session_history"
17727 | "ai_session_close" | "ai_session_reset"
17728 | "ai_session_export" | "ai_session_import"
17729 | "ai_memory_save" | "ai_memory_recall" | "ai_memory_forget"
17730 | "ai_memory_count" | "ai_memory_clear"
17731 | "ai_vision" | "ai_pdf" | "ai_grounded" | "ai_citations"
17732 | "ai_transcribe" | "ai_speak" | "ai_image" | "ai_image_edit" | "ai_image_variation"
17733 | "ai_models" | "ai_describe" | "ai_pricing" | "ai_dashboard"
17734 | "ai_moderate" | "ai_chunk" | "ai_warm" | "ai_compare"
17735 | "ai_last_thinking" | "ai_budget" | "ai_batch" | "ai_pmap"
17736 | "ai_file_upload" | "ai_file_list" | "ai_file_get" | "ai_file_delete"
17737 | "ai_file_anthropic_upload" | "ai_file_anthropic_list" | "ai_file_anthropic_delete"
17738 | "vec_cosine" | "vec_search" | "vec_topk"
17739 | "web_search_tool" | "fetch_url_tool" | "read_file_tool" | "run_code_tool"
17741 | "mcp_connect" | "mcp_close" | "mcp_tools" | "mcp_call"
17743 | "mcp_resource" | "mcp_resources" | "mcp_prompt" | "mcp_prompts"
17744 | "mcp_attach_to_ai" | "mcp_detach_from_ai" | "mcp_attached"
17745 | "mcp_server_start" | "mcp_serve_registered_tools"
17746 | "pty_spawn" | "pty_send" | "pty_read" | "pty_expect" | "pty_expect_table"
17748 | "pty_buffer" | "pty_alive" | "pty_eof" | "pty_close" | "pty_interact"
17749 | "pty_strip_ansi" | "pty_after_eof" | "pty_pending_events"
17750 | "stress_fp" | "stress_int" | "stress_cache" | "stress_branch"
17752 | "stress_sort" | "stress_alloc" | "stress_mmap" | "stress_disk"
17753 | "stress_iops" | "stress_net" | "stress_http" | "stress_dns"
17754 | "stress_fork" | "stress_thread" | "stress_aes" | "stress_compress"
17755 | "stress_regex" | "stress_json" | "stress_burst" | "stress_ramp"
17756 | "stress_oscillate" | "stress_all" | "stress_temp" | "stress_thermal_zones"
17757 | "stress_freq" | "stress_throttled" | "stress_load" | "stress_meminfo"
17758 | "stress_cores" | "stress_arm_kill_switch" | "stress_killed"
17759 | "stress_disarm_kill_switch"
17760 | "stress_metrics_record" | "stress_metrics_clear" | "stress_metrics_count"
17761 | "stress_metrics_export" | "stress_metrics_prometheus"
17762 | "stress_metrics_json" | "stress_metrics_csv" | "stress_metrics_watch"
17763 | "audit_log" | "audit_log_path"
17765 | "secrets_encrypt" | "secrets_decrypt" | "secrets_random_key" | "secrets_kdf"
17766 | "web_route" | "web_resources" | "web_root" | "web_routes_table"
17768 | "web_application_config" | "web_boot_application"
17769 | "web_render" | "web_render_partial" | "web_redirect"
17770 | "web_json" | "web_text" | "web_csv" | "web_markdown"
17771 | "web_params" | "web_request" | "web_set_header" | "web_status"
17772 | "web_before_action" | "web_after_action"
17773 | "web_session" | "web_session_set" | "web_session_get" | "web_session_clear"
17774 | "web_signed" | "web_unsigned"
17775 | "web_cookies" | "web_set_cookie"
17776 | "web_flash" | "web_flash_set" | "web_flash_get"
17777 | "web_validate" | "web_permit"
17778 | "web_password_hash" | "web_password_verify"
17779 | "web_token_for" | "web_token_consume" | "web_csrf_meta_tag"
17780 | "web_security_headers" | "web_can"
17781 | "web_h" | "web_truncate" | "web_pluralize" | "web_time_ago_in_words"
17782 | "web_image_tag" | "web_link_to" | "web_button_to"
17783 | "web_form_with" | "web_form_close"
17784 | "web_text_field" | "web_text_area" | "web_check_box"
17785 | "web_stylesheet_link_tag" | "web_javascript_link_tag"
17786 | "web_yield_content" | "web_content_for"
17787 | "web_etag" | "web_cache_get" | "web_cache_set"
17788 | "web_cache_delete" | "web_cache_clear"
17789 | "web_db_connect" | "web_db_execute" | "web_db_query"
17790 | "web_db_begin" | "web_db_commit" | "web_db_rollback"
17791 | "web_create_table" | "web_drop_table"
17792 | "web_add_column" | "web_remove_column"
17793 | "web_migrate" | "web_rollback"
17794 | "web_model_all" | "web_model_find" | "web_model_first" | "web_model_last"
17795 | "web_model_where" | "web_model_create" | "web_model_update"
17796 | "web_model_destroy" | "web_model_count" | "web_model_increment"
17797 | "web_model_paginate" | "web_model_search" | "web_model_soft_destroy"
17798 | "web_model_with"
17799 | "web_jobs_init" | "web_job_enqueue" | "web_job_dequeue"
17800 | "web_job_complete" | "web_job_fail"
17801 | "web_jobs_list" | "web_jobs_stats" | "web_job_purge"
17802 | "web_jsonapi_resource" | "web_jsonapi_collection" | "web_jsonapi_error"
17803 | "web_bearer_token" | "web_jwt_encode" | "web_jwt_decode"
17804 | "web_otp_secret" | "web_otp_generate" | "web_otp_verify"
17805 | "web_uuid" | "web_now" | "web_log" | "web_rate_limit"
17806 | "web_t" | "web_load_locale" | "web_openapi"
17807 | "web_faker_int" | "web_faker_email" | "web_faker_name"
17808 | "web_faker_sentence" | "web_faker_paragraph"
17809 | "check" | "check_no_interop" | "check_ni"
17816 | "test" | "test_no_interop" | "test_ni"
17817 | "andrew_monotone_hull" | "aperture_stop_to_fnumber" | "arpad_predict" | "bilateral_filter_2d"
17826 | "black_hat_transform" | "canny_edges_full" | "case_alternating" | "case_constant"
17827 | "case_dot" | "case_pascal" | "case_path" | "case_sentence"
17828 | "case_swap" | "case_title_proper" | "case_train" | "closing_2d"
17829 | "cohen_sutherland_clip" | "constants_au_meters" | "constants_avogadro_n" | "constants_bohr_radius"
17830 | "constants_boltzmann_k" | "constants_earth_mass" | "constants_earth_radius" | "constants_electron_charge"
17831 | "constants_electron_mass" | "constants_faraday" | "constants_gas_r" | "constants_gravitational_g"
17832 | "constants_lightyear_meters" | "constants_neutron_mass" | "constants_parsec_meters" | "constants_planck_h"
17833 | "constants_planck_hbar" | "constants_planck" | "constants_proton_mass" | "constants_rydberg"
17834 | "constants_solar_mass" | "constants_solar_radius" | "constants_speed_of_light" | "constants_stefan_boltzmann"
17835 | "contour_area" | "contour_centroid" | "contour_find" | "contour_perimeter"
17836 | "convex_hull_3d" | "convex_hull_3d_simple" | "crop_factor" | "delaunay_triangulate_2d" | "depth_of_field_far"
17837 | "depth_of_field_near" | "dh_compute_shared" | "dilation_2d" | "ec_point_add"
17838 | "ec_point_double" | "ed25519_keypair_simple" | "ed25519_sign_simple" | "ed25519_verify_simple"
17839 | "erosion_2d" | "exposure_value" | "field_of_view" | "focal_length_35mm_equiv"
17840 | "glicko_rd_update" | "glicko_volatility" | "graham_scan_hull" | "hu_moments"
17841 | "hyperfocal_distance" | "liang_barsky_clip" | "minkowski_sum_2d" | "moment_image"
17842 | "morphological_gradient" | "opening_2d" | "pagerank_tournament" | "polygon_inflate"
17843 | "polygon_offset" | "polygon_self_intersects" | "polygon_shrink" | "polygon_simple_check"
17844 | "polygon_winding" | "prewitt_x_kernel" | "prewitt_y_kernel" | "ranking_average"
17845 | "ranking_kendall_tau" | "ranking_spearman_rho" | "roberts_cross_kernel" | "rsa_keypair_simple"
17846 | "rsa_modular_exp" | "scharr_x_kernel" | "scharr_y_kernel" | "schnorr_sign_simple"
17847 | "schnorr_verify_simple" | "shutter_speed_reciprocal" | "sobel_magnitude" | "sunny_16_rule"
17848 | "swiss_pairing" | "top_hat_transform" | "tournament_score" | "trueskill_simple"
17849 | "unit_energy" | "unit_pressure" | "unit_temperature" | "unit_volume_metric_to_us"
17850 | "unit_volume_us_to_metric" | "voronoi_cell_2d" | "weiler_atherton_clip" | "zernike_radial"
17851
17852 | "a_star_grid" | "all_pass_filter" | "am_synth" | "bidirectional_bfs"
17853 | "buoyancy_force" | "center_of_mass_2d" | "center_of_mass_3d" | "centered_polygonal"
17854 | "chorus_simple" | "collision_response_2d" | "comb_filter" | "compositions_count"
17855 | "critical_damping" | "cube_number" | "damping_factor" | "decagonal_number"
17856 | "derangement_count" | "dodecahedral" | "elastic_collision_1d" | "exponential_search"
17857 | "fbm_noise_2d" | "fibonacci_matrix" | "fibonacci_nth_fast" | "fir_filter"
17858 | "flanger_simple" | "floyd_cycle_detect" | "fm_synth_2op" | "freeverb_lite"
17859 | "gnomonic_number" | "greedy_best_first" | "hash_2d_int" | "heptagonal_number"
17860 | "hexagonal_number" | "hyperfactorial" | "icosahedral" | "ida_star_search"
17861 | "inelastic_collision_1d" | "interpolation_search" | "lattice_paths" | "lift_force"
17862 | "lucas_nth" | "moment_of_inertia_cylinder" | "moment_of_inertia_disc" | "moment_of_inertia_rod"
17863 | "moment_of_inertia_sphere" | "mulberry32_next" | "multinomial_coefficient" | "narayana_cow"
17864 | "nonagonal_number" | "octagonal_number" | "partitions_count" | "pcg32_next"
17865 | "pell_nth" | "perlin_2d" | "perlin_3d" | "phaser_simple"
17866 | "plate_reverb_simple" | "poisson_brackets" | "primorial" | "projectile_position"
17867 | "projectile_velocity" | "ridge_noise_2d" | "ring_modulate" | "schroeder_reverb"
17868 | "simplex_2d" | "splitmix64_next" | "spring_oscillator_pos" | "square_pyramidal"
17869 | "super_factorial" | "ternary_search" | "tetrahedral" | "tetranacci"
17870 | "torque_arm" | "turbulence_noise_2d" | "value_noise_2d" | "wavetable_synth"
17871 | "worley_2d" | "xorshift32_next"
17872
17873 | "adaptive_threshold" | "bayes_factor" | "bayesian_beta_update" | "bayesian_normal_update"
17874 | "bm25_score" | "boltzmann_choose" | "braycurtis_dist" | "canberra_dist"
17875 | "canny_edges_simple" | "chebyshev_norm" | "cidr_to_range" | "ciede2000_color_distance"
17876 | "ciede76_color_distance" | "ciede94_color_distance" | "conv1d_apply" | "conv2d_apply"
17877 | "correlate2d" | "cosine_sim_sparse" | "credible_interval_beta" | "credible_interval_normal"
17878 | "dice_coeff" | "earth_mover_1d" | "epsilon_greedy_choose" | "fenwick_new"
17879 | "fenwick_query_prefix" | "fenwick_query_range" | "fenwick_update" | "gaussian_kernel"
17880 | "gradient_magnitude_2d" | "harris_response" | "integral_image" | "ip_subnet_split"
17881 | "ipv6_global_unicast" | "jaccard_sim" | "laplacian_kernel" | "lch_to_rgb"
17882 | "mahalanobis_sq" | "manhattan_norm" | "maximum_a_posteriori" | "minkowski_norm"
17883 | "non_max_suppression" | "oklch_to_rgb" | "otsu_threshold" | "overlap_coeff"
17884 | "posterior_predictive_beta" | "posterior_predictive_normal" | "prior_jeffreys_uniform" | "qlearning_step"
17885 | "range_to_cidr" | "rgb_to_lch" | "rgb_to_oklch" | "rl_discount_returns"
17886 | "rl_n_step_return" | "rl_td_error" | "sarsa_step" | "sliding_dot_product"
17887 | "sobel_x_kernel" | "sobel_y_kernel" | "softmax_choose" | "tanimoto_coeff"
17888 | "tfidf_compute" | "thompson_beta_choose" | "trie_count" | "trie_insert"
17889 | "trie_keys" | "trie_lookup" | "trie_new" | "trie_prefix_search"
17890 | "trie_remove" | "tversky_index" | "ucb1_choose" | "union_find_components"
17891 | "union_find_find" | "union_find_new" | "union_find_union" | "window_bartlett"
17892 | "window_blackman_harris" | "window_flat_top" | "window_gaussian" | "window_welch"
17893
17894 | "alphabeta_value" | "chem_arrhenius_k" | "chem_avogadro" | "chem_balance_check"
17895 | "chem_boiling_point_elevation" | "chem_buffer_capacity" | "chem_celsius_to_fahrenheit" | "chem_celsius_to_kelvin"
17896 | "chem_concentration_to_molarity" | "chem_dilution" | "chem_fahrenheit_to_celsius" | "chem_fahrenheit_to_kelvin"
17897 | "chem_formula_parse" | "chem_freezing_point_depression" | "chem_h_from_ph" | "chem_henderson_hasselbalch"
17898 | "chem_ideal_gas_volume" | "chem_isoelectric_estimate" | "chem_kelvin_to_celsius" | "chem_kelvin_to_fahrenheit"
17899 | "chem_kelvin_to_rankine" | "chem_molality" | "chem_molar_mass" | "chem_molarity_to_normality"
17900 | "chem_partial_pressure" | "chem_ph_from_h" | "chem_pka_lookup" | "chem_rankine_to_kelvin"
17901 | "conditional_entropy" | "edmonds_karp_max_flow" | "expectiminimax_value" | "ford_fulkerson_max_flow"
17902 | "job_schedule_ljf" | "job_schedule_spt" | "joint_entropy" | "js_divergence_distributions"
17903 | "kl_divergence_distributions" | "knapsack_fractional" | "knapsack_unbounded" | "lp_simplex_max"
17904 | "lp_simplex_min" | "matching_bipartite_greedy" | "matching_bipartite_hungarian" | "minimax_value"
17905 | "mixed_strategy_2x2" | "ml_attention_score" | "ml_batch_norm" | "ml_dot_product_attention"
17906 | "ml_dropout_mask" | "ml_elu_layer" | "ml_gelu_layer" | "ml_hinge_loss"
17907 | "ml_huber_loss" | "ml_kl_div_loss" | "ml_label_smooth" | "ml_layer_norm"
17908 | "ml_leaky_relu_layer" | "ml_mae_loss" | "ml_mish_layer" | "ml_mse_loss"
17909 | "ml_one_hot_encode" | "ml_position_encoding" | "ml_relu_layer" | "ml_self_attention"
17910 | "ml_sigmoid_layer" | "ml_softmax_layer" | "ml_softplus_layer" | "ml_swish_layer"
17911 | "ml_tanh_layer" | "ngram_perplexity" | "ngram_prob" | "ngram_top_k_next"
17912 | "ngram_train" | "payoff_matrix" | "relative_entropy" | "tsp_2opt"
17913 | "zero_sum_value"
17914
17915 | "aabb_contains_point" | "aabb_intersects" | "aabb_new" | "aabb_union"
17916 | "aabb_volume" | "backward_algorithm" | "blast_kmer_index" | "bmp_header_read"
17917 | "bootstrap_resample" | "codon_optimize" | "codon_to_amino_acid" | "codon_usage_table"
17918 | "dna_at_content" | "dna_complement" | "dna_gc_content" | "dna_kmer_count"
17919 | "dna_kmer_index" | "dna_melting_temp" | "dna_reverse_complement" | "dna_transcribe"
17920 | "dna_translate" | "elf_header_read" | "forward_algorithm" | "gif_header_read"
17921 | "ico_header_read"
17922 | "jpeg_markers" | "levenshtein_edit_path" | "mach_o_header_read" | "markov_stationary"
17923 | "markov_transition_matrix" | "mat4_determinant" | "mat4_identity" | "mat4_inverse"
17924 | "mat4_look_at" | "mat4_multiply" | "mat4_orthographic" | "mat4_perspective"
17925 | "mat4_rotate_axis" | "mat4_rotate_x" | "mat4_rotate_y" | "mat4_rotate_z"
17926 | "mat4_scale" | "mat4_translate" | "mat4_transpose" | "nw_score"
17927 | "permutation_test" | "plane_distance_to_point" | "plane_normalize" | "png_header_read"
17928 | "profile_hmm_score" | "protein_charge_at_ph" | "protein_hydrophobicity" | "protein_molecular_weight"
17929 | "protein_pI" | "quat_conjugate" | "quat_dot" | "quat_from_euler"
17930 | "quat_identity" | "quat_inverse" | "quat_multiply" | "quat_normalize"
17931 | "quat_to_euler" | "quat_to_mat4" | "ray_aabb_intersect" | "ray_plane_intersect_2"
17932 | "ray_plane_intersect" | "rna_gc_content" | "rna_hamming" | "rna_reverse_complement"
17933 | "rna_to_dna" | "sequence_identity_pct" | "sequence_similarity_pct" | "shuffle_resample"
17934 | "sphere_aabb_intersect" | "sphere_sphere_intersect" | "sw_score" | "tar_header_read"
17935 | "triangle_area_3d" | "triangle_normal" | "vec3_add" | "vec3_cross"
17936 | "vec3_distance" | "vec3_dot" | "vec3_length" | "vec3_lerp"
17937 | "vec3_normalize" | "vec3_project" | "vec3_reflect" | "vec3_refract"
17938 | "vec3_scale" | "vec3_sub" | "vec4_add" | "vec4_dot"
17939 | "vec4_length" | "vec4_scale" | "vec4_sub" | "viterbi_decode"
17940 | "wav_header_read" | "zip_central_directory" | "zip_local_file_header"
17941
17942 | "adler32_combine" | "ase_palette_extract" | "base58check_decode" | "base58check_encode"
17943 | "base91_decode" | "basE91_decode" | "base91_encode" | "basE91_encode"
17944 | "bwt_invert" | "bwt_transform" | "caverphone" | "caverphone2"
17945 | "crc10_atm" | "crc12_dect" | "crc24" | "crc32_bzip2"
17946 | "crc32_jamcrc" | "crc32_mpeg2" | "crc32_xfer" | "crc6_itu"
17947 | "crc64_ecma" | "crc64_xz" | "delta_decode" | "delta_encode"
17948 | "destination_lat_lon" | "double_metaphone_primary" | "double_metaphone_secondary" | "fletcher16"
17949 | "fletcher32" | "fletcher64" | "full_moon_julian" | "fuzzy_substring_match"
17950 | "gamma_correct" | "gamma_uncorrect" | "geomag_declination" | "huffman_decode"
17951 | "huffman_encode" | "julian_to_unix" | "lambert_project" | "lat_lon_to_utm"
17952 | "match_rating_compare" | "mercator_project_x" | "mercator_project_y" | "mercator_unproject_lat"
17953 | "mercator_unproject_lon" | "modified_julian_date" | "moon_age_days" | "moon_distance_km"
17954 | "new_moon_julian" | "nysiis" | "phonex" | "rgb_blend_color_burn"
17955 | "rgb_blend_color_dodge" | "rgb_blend_darken" | "rgb_blend_lighten" | "rgb_blend_multiply"
17956 | "rgb_blend_normal" | "rgb_blend_overlay" | "rgb_blend_screen" | "rle_compress"
17957 | "rle_decompress" | "season_of_year" | "sidereal_time_greenwich" | "sidereal_time_local"
17958 | "solar_noon_unix" | "soundex_v1" | "soundex_v2" | "unix_to_julian"
17959 | "utm_to_lat_lon" | "utm_zone" | "varint_decode" | "varint_encode"
17960 | "vincenty_bearing" | "z85_decode" | "z85_encode" | "zigzag_decode"
17961 | "zigzag_encode"
17962
17963 | "anova_one_way" | "binomial_test" | "bit_clz"
17964 | "bit_count_ones" | "bit_count_zeros" | "bit_ctz" | "bit_extract"
17965 | "bit_first_clear" | "bit_first_set" | "bit_insert" | "bit_last_clear"
17966 | "bit_last_set" | "bit_log2_int" | "bit_parity" | "bit_reverse_u16"
17967 | "bit_reverse_u32" | "bit_reverse_u64" | "bit_reverse_u8" | "bit_rotate_left"
17968 | "bit_rotate_right" | "bit_swap_bytes" | "chi_square_goodness_fit" | "chi_square_independence"
17969 | "chord_augmented" | "chord_diminished" | "chord_diminished7" | "chord_dominant7"
17970 | "chord_major" | "chord_major7" | "chord_minor" | "chord_minor7"
17971 | "crc16_xmodem" | "crc16" | "crc32_zlib" | "crc32c"
17972 | "crc8" | "detab" | "entab" | "fisher_exact_2x2"
17973 | "gray_code_decode" | "gray_code_encode" | "hmac_md5_hex" | "hmac_sha1_hex"
17974 | "hmac_sha256_hex" | "hmac_sha384_hex" | "hmac_sha512_hex" | "indent_block"
17975 | "interval_name" | "jenkins_hash" | "justify_center" | "justify_left"
17976 | "justify_right" | "kruskal_wallis" | "ks_test_one_sample" | "ks_test_two_sample"
17977 | "loose_hash" | "mann_whitney_u" | "midi_to_note_name" | "popcount_u32"
17978 | "popcount_u64" | "proportion_test" | "rank_data" | "scale_blues"
17979 | "scale_chromatic" | "scale_dorian" | "scale_harmonic_minor" | "scale_locrian"
17980 | "scale_lydian" | "scale_major" | "scale_melodic_minor" | "scale_minor"
17981 | "scale_mixolydian" | "scale_pentatonic" | "scale_phrygian" | "seconds_per_beat"
17982 | "strip_indent" | "t_test_paired" | "tempo_to_ms_per_beat" | "truncate_middle"
17983 | "unicode_codepoints" | "wilcoxon_signed_rank" | "word_wrap"
17984
17985 | "beta_function" | "beta_incomplete" | "date_add_days" | "date_add_months"
17986 | "date_add_years" | "date_business_days_between" | "date_day" | "date_dayofweek"
17987 | "date_dayofyear" | "date_days_in_month" | "date_diff_days" | "date_diff_hours"
17988 | "date_diff_minutes" | "date_diff_seconds" | "date_easter" | "date_first_of_month"
17989 | "date_hour" | "date_is_leap" | "date_is_weekend" | "date_iso_format"
17990 | "date_iso_week" | "date_last_of_month" | "date_minute" | "date_month"
17991 | "date_quarter" | "date_second" | "date_str_to_unix" | "date_unix_to_str"
17992 | "date_weekofyear" | "date_year" | "ei" | "expint"
17993 | "gamma_regularized_p" | "gamma_regularized_q" | "graph_articulation_points" | "graph_bellman_ford"
17994 | "graph_betweenness" | "graph_bfs" | "graph_bridges" | "graph_closeness"
17995 | "graph_clustering_coefficient" | "graph_color_greedy" | "graph_connected_components" | "graph_cycle_detect"
17996 | "graph_degree" | "graph_dfs" | "graph_dijkstra" | "graph_eccentricity"
17997 | "graph_eigenvector_centrality" | "graph_floyd_warshall" | "graph_from_edges" | "graph_has_path"
17998 | "graph_in_degree" | "graph_is_bipartite" | "graph_is_connected" | "graph_kosaraju"
17999 | "graph_kruskal_mst" | "graph_out_degree" | "graph_pagerank" | "graph_prim_mst"
18000 | "graph_shortest_path" | "graph_strongly_connected_components" | "graph_tarjan" | "graph_to_adj_list"
18001 | "graph_to_adj_matrix" | "graph_topological_sort" | "hypergeom_1f1" | "hypergeom_2f1"
18002 | "li" | "matrix_adjugate" | "matrix_cholesky_decompose" | "matrix_cofactor"
18003 | "matrix_cols" | "matrix_concat_h" | "matrix_concat_v" | "matrix_determinant"
18004 | "matrix_from_cols" | "matrix_get" | "matrix_kronecker" | "matrix_lu_decompose"
18005 | "matrix_minor" | "matrix_new" | "matrix_norm_frobenius" | "matrix_norm_l1"
18006 | "matrix_norm_linf" | "matrix_outer_product" | "matrix_qr_decompose" | "matrix_reshape"
18007 | "matrix_rows" | "matrix_set" | "matrix_submatrix" | "matrix_swap_cols"
18008 | "matrix_swap_rows" | "matrix_to_string" | "matrix_vec_mul" | "si"
18009 | "sun_rise_unix" | "sun_set_unix" | "zeta_riemann" | "zodiac_sign"
18010 | "add_seasonality" | "trapezoidal_integrate" | "simpson_integrate"
18012 | "ode_euler" | "fit_curve_least_squares"
18013 | "adf_test" | "adx" | "atr" | "bollinger_lower" | "bollinger_middle"
18014 | "bollinger_upper" | "break_even_price" | "break_even_qty" | "candlestick_pattern_doji"
18015 | "candlestick_pattern_engulfing" | "candlestick_pattern_evening_star" | "candlestick_pattern_hammer" | "candlestick_pattern_morning_star"
18016 | "candlestick_pattern_three_black_crows" | "candlestick_pattern_three_white_soldiers" | "cci"
18017 | "dema" | "diff_pct" | "diff_series"
18018 | "discount_pct" | "donchian_lower" | "donchian_upper" | "double_exponential_smoothing"
18019 | "duration_modified" | "ema" | "expanding_mean" | "expanding_sum"
18020 | "fibonacci_extension" | "fibonacci_retracement" | "finite_difference_central"
18021 | "finite_difference_forward" | "hma"
18022 | "hurst_exponent" | "interp_lagrange"
18023 | "interp_linear" | "kama"
18024 | "keltner_lower" | "keltner_upper" | "lag_series"
18025 | "loan_interest_total" | "loan_payment" | "loan_remaining" | "log_returns"
18026 | "macd_histogram" | "macd_signal" | "macd" | "markup_pct" | "net_present_value" | "obv" | "parabolic_sar" | "pivot_points" | "profit_margin_pct" | "remove_seasonality" | "resistance_level"
18027 | "roc" | "rolling_kurtosis" | "rolling_max"
18028 | "rolling_mean" | "rolling_median" | "rolling_min" | "rolling_skew"
18029 | "rolling_std" | "rolling_sum" | "rolling_var"
18030 | "rsi" | "shift_series" | "simple_returns" | "sma" | "stoch_rsi" | "support_level"
18031 | "tema" | "trend_line" | "treynor"
18032 | "trix" | "true_range" | "twap" | "ulcer_index"
18033 | "volatility_annualized" | "volatility_realized" | "vwap" | "williams_r"
18034 | "wma"
18035 => Some(name),
18036 _ => None,
18037 }
18038 }
18039
18040 fn is_reserved_hash_name(name: &str) -> bool {
18043 matches!(
18044 name,
18045 "b" | "pc"
18046 | "e"
18047 | "a"
18048 | "d"
18049 | "c"
18050 | "p"
18051 | "k"
18052 | "all"
18053 | "stryke::builtins"
18054 | "stryke::perl_compats"
18055 | "stryke::extensions"
18056 | "stryke::aliases"
18057 | "stryke::descriptions"
18058 | "stryke::categories"
18059 | "stryke::primaries"
18060 | "stryke::keywords"
18061 | "stryke::all"
18062 )
18063 }
18064
18065 const RESERVED_FUNCTION_NAMES: &'static [&'static str] = &[
18070 "y",
18071 "tr",
18072 "s",
18073 "m",
18074 "q",
18075 "qq",
18076 "qw",
18077 "qx",
18078 "qr",
18079 "if",
18080 "unless",
18081 "while",
18082 "until",
18083 "for",
18084 "foreach",
18085 "given",
18086 "when",
18087 "else",
18088 "elsif",
18089 "do",
18090 "eval",
18091 "return",
18092 "last",
18093 "next",
18094 "redo",
18095 "goto",
18096 "my",
18097 "our",
18098 "local",
18099 "state",
18100 "sub",
18101 "fn",
18102 "class",
18103 "struct",
18104 "enum",
18105 "trait",
18106 "use",
18107 "no",
18108 "require",
18109 "package",
18110 "BEGIN",
18111 "END",
18112 "CHECK",
18113 "INIT",
18114 "UNITCHECK",
18115 "and",
18116 "or",
18117 "not",
18118 "x",
18119 "eq",
18120 "ne",
18121 "lt",
18122 "gt",
18123 "le",
18124 "ge",
18125 "cmp",
18126 ];
18127
18128 fn check_udf_shadows_builtin(&self, name: &str, line: usize) -> StrykeResult<()> {
18129 if name.contains("::") {
18132 return Ok(());
18133 }
18134 if Self::RESERVED_FUNCTION_NAMES.contains(&name) {
18137 return Err(self.syntax_err(
18138 format!("`{name}` is a reserved word and cannot be used as a function name"),
18139 line,
18140 ));
18141 }
18142 if self.current_package != "main" {
18147 return Ok(());
18148 }
18149 if Self::is_known_bareword(name)
18153 || Self::is_try_builtin_name(name)
18154 || crate::list_builtins::is_list_builtin_name(name)
18155 {
18156 return Err(self.syntax_err(
18157 format!(
18158"`{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)"
18159 ),
18160 line,
18161 ));
18162 }
18163 Ok(())
18164 }
18165
18166 fn check_hash_shadows_reserved(&self, name: &str, line: usize) -> StrykeResult<()> {
18169 if Self::is_reserved_hash_name(name) {
18170 return Err(self.syntax_err(
18171 format!(
18172"`%{name}` is a stryke reserved hash and cannot be redefined (this is not Perl 5; pass --compat for Perl 5 mode)"
18173 ),
18174 line,
18175 ));
18176 }
18177 Ok(())
18178 }
18179
18180 fn validate_hash_assignment(&self, value: &Expr, line: usize) -> StrykeResult<()> {
18183 match &value.kind {
18184 ExprKind::Integer(_) | ExprKind::Float(_) => {
18185 return Err(self.syntax_err(
18186 "cannot assign scalar to hash — use %h = (key => value) or %h = %{$hashref}",
18187 line,
18188 ));
18189 }
18190 ExprKind::String(_) | ExprKind::InterpolatedString(_) | ExprKind::Bareword(_) => {
18191 return Err(self.syntax_err(
18192 "cannot assign string to hash — use %h = (key => value) or %h = %{$hashref}",
18193 line,
18194 ));
18195 }
18196 ExprKind::ArrayRef(_) => {
18197 return Err(self.syntax_err(
18198 "cannot assign arrayref to hash — use %h = @{$arrayref} for even-length list",
18199 line,
18200 ));
18201 }
18202 ExprKind::ScalarRef(inner) => {
18203 if matches!(inner.kind, ExprKind::ArrayVar(_)) {
18204 return Err(self.syntax_err(
18205 "cannot assign \\@array to hash — use %h = @array for even-length list",
18206 line,
18207 ));
18208 }
18209 if matches!(inner.kind, ExprKind::HashVar(_)) {
18210 return Err(self.syntax_err(
18211 "cannot assign \\%hash to hash — use %h = %other directly",
18212 line,
18213 ));
18214 }
18215 }
18216 ExprKind::HashRef(_) => {
18217 return Err(self.syntax_err(
18218 "cannot assign hashref to hash — use %h = %{$hashref} to dereference",
18219 line,
18220 ));
18221 }
18222 ExprKind::CodeRef { .. } => {
18223 return Err(self.syntax_err("cannot assign coderef to hash", line));
18224 }
18225 ExprKind::Undef => {
18226 return Err(
18227 self.syntax_err("cannot assign undef to hash — use %h = () to empty", line)
18228 );
18229 }
18230 ExprKind::List(items)
18231 if items.len() % 2 != 0
18232 && !items.iter().any(|e| {
18233 matches!(
18234 e.kind,
18235 ExprKind::ArrayVar(_)
18236 | ExprKind::HashVar(_)
18237 | ExprKind::FuncCall { .. }
18238 | ExprKind::Deref { .. }
18239 | ExprKind::ScalarVar(_)
18240 )
18241 }) =>
18242 {
18243 return Err(self.syntax_err(
18244 format!(
18245 "odd-length list ({} elements) in hash assignment — missing value for last key",
18246 items.len()
18247 ),
18248 line,
18249 ));
18250 }
18251 _ => {}
18252 }
18253 Ok(())
18254 }
18255
18256 fn validate_array_assignment(&self, value: &Expr, line: usize) -> StrykeResult<()> {
18261 if let ExprKind::Undef = &value.kind {
18262 return Err(
18263 self.syntax_err("cannot assign undef to array — use @a = () to empty", line)
18264 );
18265 }
18266 Ok(())
18267 }
18268
18269 fn validate_scalar_assignment(&self, value: &Expr, line: usize) -> StrykeResult<()> {
18272 if let ExprKind::List(items) = &value.kind {
18273 if items.len() > 1 {
18274 return Err(self.syntax_err(
18275 format!(
18276 "cannot assign {}-element list to scalar — Perl 5 silently takes last element; use ($x) = (list) or $x = $list[-1]",
18277 items.len()
18278 ),
18279 line,
18280 ));
18281 }
18282 }
18283 Ok(())
18284 }
18285
18286 fn validate_assignment(&self, target: &Expr, value: &Expr, line: usize) -> StrykeResult<()> {
18288 if crate::compat_mode() {
18289 return Ok(());
18290 }
18291 match &target.kind {
18292 ExprKind::HashVar(_) => self.validate_hash_assignment(value, line),
18293 ExprKind::ArrayVar(_) => self.validate_array_assignment(value, line),
18294 ExprKind::ScalarVar(_) => self.validate_scalar_assignment(value, line),
18295 _ => Ok(()),
18296 }
18297 }
18298
18299 fn parse_block_or_bareword_cmp_block(&mut self) -> StrykeResult<Block> {
18303 if matches!(self.peek(), Token::LBrace) {
18304 return self.parse_block();
18305 }
18306 let line = self.peek_line();
18307 if let Token::Ident(ref name) = self.peek().clone() {
18309 if matches!(
18310 self.peek_at(1),
18311 Token::Comma | Token::Semicolon | Token::RBrace | Token::Eof | Token::PipeForward
18312 ) {
18313 let name = name.clone();
18314 self.advance();
18315 let body = Expr {
18316 kind: ExprKind::FuncCall {
18317 name,
18318 args: vec![
18319 Expr {
18320 kind: ExprKind::ScalarVar("a".to_string()),
18321 line,
18322 },
18323 Expr {
18324 kind: ExprKind::ScalarVar("b".to_string()),
18325 line,
18326 },
18327 ],
18328 },
18329 line,
18330 };
18331 return Ok(vec![Statement::new(StmtKind::Expression(body), line)]);
18332 }
18333 }
18334 let expr = self.parse_assign_expr_stop_at_pipe()?;
18336 Ok(vec![Statement::new(StmtKind::Expression(expr), line)])
18337 }
18338
18339 fn parse_fan_optional_progress(
18341 &mut self,
18342 which: &'static str,
18343 ) -> StrykeResult<Option<Box<Expr>>> {
18344 let line = self.peek_line();
18345 if self.eat(&Token::Comma) {
18346 match self.peek() {
18347 Token::Ident(ref kw)
18348 if kw == "progress" && matches!(self.peek_at(1), Token::FatArrow) =>
18349 {
18350 self.advance();
18351 self.expect(&Token::FatArrow)?;
18352 return Ok(Some(Box::new(self.parse_assign_expr()?)));
18353 }
18354 _ => {
18355 return Err(self.syntax_err(
18356 format!("{which}: expected `progress => EXPR` after comma"),
18357 line,
18358 ));
18359 }
18360 }
18361 }
18362 if let Token::Ident(ref kw) = self.peek().clone() {
18363 if kw == "progress" && matches!(self.peek_at(1), Token::FatArrow) {
18364 self.advance();
18365 self.expect(&Token::FatArrow)?;
18366 return Ok(Some(Box::new(self.parse_assign_expr()?)));
18367 }
18368 }
18369 Ok(None)
18370 }
18371
18372 fn parse_assign_expr_list_optional_progress(&mut self) -> StrykeResult<(Expr, Option<Expr>)> {
18379 if self.in_pipe_rhs()
18385 && matches!(
18386 self.peek(),
18387 Token::Semicolon
18388 | Token::RBrace
18389 | Token::RParen
18390 | Token::Eof
18391 | Token::PipeForward
18392 | Token::Comma
18393 )
18394 {
18395 return Ok((self.pipe_placeholder_list(self.peek_line()), None));
18396 }
18397 let mut parts = vec![self.parse_assign_expr_stop_at_pipe()?];
18398 loop {
18399 if !self.eat(&Token::Comma) && !self.eat(&Token::FatArrow) {
18400 break;
18401 }
18402 if matches!(
18403 self.peek(),
18404 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof | Token::PipeForward
18405 ) {
18406 break;
18407 }
18408 if self.peek_is_postfix_stmt_modifier_keyword() {
18409 break;
18410 }
18411 if let Token::Ident(ref kw) = self.peek().clone() {
18412 if kw == "progress" && matches!(self.peek_at(1), Token::FatArrow) {
18413 self.advance();
18414 self.expect(&Token::FatArrow)?;
18415 let prog = self.parse_assign_expr_stop_at_pipe()?;
18416 return Ok((merge_expr_list(parts), Some(prog)));
18417 }
18418 }
18419 parts.push(self.parse_assign_expr_stop_at_pipe()?);
18420 }
18421 Ok((merge_expr_list(parts), None))
18422 }
18423
18424 fn parse_one_arg(&mut self) -> StrykeResult<Expr> {
18425 if matches!(self.peek(), Token::LParen) {
18426 self.advance();
18427 let expr = self.parse_expression()?;
18428 self.expect(&Token::RParen)?;
18429 Ok(expr)
18430 } else {
18431 self.parse_assign_expr_stop_at_pipe()
18432 }
18433 }
18434
18435 fn parse_named_unary_arg(&mut self) -> StrykeResult<Expr> {
18444 if matches!(self.peek(), Token::LParen) {
18445 self.advance();
18446 let expr = self.parse_expression()?;
18447 self.expect(&Token::RParen)?;
18448 Ok(expr)
18449 } else {
18450 self.parse_shift()
18451 }
18452 }
18453
18454 fn parse_one_arg_or_default(&mut self) -> StrykeResult<Expr> {
18455 let prev = self.prev_line();
18466 if self.peek_line() > prev {
18467 return Ok(Expr {
18468 kind: ExprKind::ScalarVar("_".into()),
18469 line: prev,
18470 });
18471 }
18472 if matches!(
18479 self.peek(),
18480 Token::Semicolon
18482 | Token::RBrace
18483 | Token::RParen
18484 | Token::RBracket
18485 | Token::Eof
18486 | Token::Comma
18487 | Token::FatArrow
18488 | Token::PipeForward
18489 | Token::Question
18491 | Token::Colon
18492 | Token::NumEq | Token::NumNe | Token::NumLt | Token::NumGt
18494 | Token::NumLe | Token::NumGe | Token::Spaceship
18495 | Token::StrEq | Token::StrNe | Token::StrLt | Token::StrGt
18496 | Token::StrLe | Token::StrGe | Token::StrCmp
18497 | Token::LogAnd | Token::LogOr | Token::LogNot
18499 | Token::LogAndWord | Token::LogOrWord | Token::LogNotWord
18500 | Token::DefinedOr
18501 | Token::Range | Token::RangeExclusive
18503 | Token::Assign | Token::PlusAssign | Token::MinusAssign
18505 | Token::MulAssign | Token::DivAssign | Token::ModAssign
18506 | Token::PowAssign | Token::DotAssign | Token::AndAssign
18507 | Token::OrAssign | Token::XorAssign | Token::DefinedOrAssign
18508 | Token::ShiftLeftAssign | Token::ShiftRightAssign
18509 | Token::BitAndAssign | Token::BitOrAssign
18510 ) {
18511 return Ok(Expr {
18512 kind: ExprKind::ScalarVar("_".into()),
18513 line: self.peek_line(),
18514 });
18515 }
18516 if matches!(self.peek(), Token::LParen) && matches!(self.peek_at(1), Token::RParen) {
18520 let line = self.peek_line();
18521 self.advance(); self.advance(); return Ok(Expr {
18524 kind: ExprKind::ScalarVar("_".into()),
18525 line,
18526 });
18527 }
18528 self.parse_named_unary_arg()
18533 }
18534
18535 fn parse_one_arg_or_argv(&mut self) -> StrykeResult<Expr> {
18537 let line = self.prev_line(); if matches!(self.peek(), Token::LParen) {
18539 self.advance();
18540 if matches!(self.peek(), Token::RParen) {
18541 self.advance();
18542 return Ok(Expr {
18543 kind: ExprKind::ArrayVar("_".into()),
18544 line: self.peek_line(),
18545 });
18546 }
18547 let expr = self.parse_expression()?;
18548 self.expect(&Token::RParen)?;
18549 return Ok(expr);
18550 }
18551 if matches!(
18553 self.peek(),
18554 Token::Semicolon
18555 | Token::RBrace
18556 | Token::RParen
18557 | Token::Eof
18558 | Token::Comma
18559 | Token::PipeForward
18560 ) || self.peek_line() > line
18561 {
18562 Ok(Expr {
18563 kind: ExprKind::ArrayVar("_".into()),
18564 line,
18565 })
18566 } else {
18567 self.parse_assign_expr()
18568 }
18569 }
18570
18571 fn parse_builtin_args(&mut self) -> StrykeResult<Vec<Expr>> {
18572 if matches!(self.peek(), Token::LParen) {
18573 self.advance();
18574 let args = self.parse_arg_list()?;
18575 self.expect(&Token::RParen)?;
18576 Ok(args)
18577 } else if self.suppress_parenless_call > 0 && matches!(self.peek(), Token::Ident(_)) {
18578 Ok(vec![])
18581 } else {
18582 self.parse_list_until_terminator()
18583 }
18584 }
18585
18586 #[inline]
18590 fn fat_arrow_autoquote(&self, name: &str, line: usize) -> Option<Expr> {
18591 if matches!(self.peek(), Token::FatArrow) {
18592 Some(Expr {
18593 kind: ExprKind::String(name.to_string()),
18594 line,
18595 })
18596 } else {
18597 None
18598 }
18599 }
18600
18601 fn parse_hash_subscript_key(&mut self) -> StrykeResult<Expr> {
18612 let line = self.peek_line();
18613 if let Token::Ident(ref k) = self.peek().clone() {
18614 if matches!(self.peek_at(1), Token::RBrace) && !Self::is_underscore_topic_slot(k) {
18615 let s = k.clone();
18616 self.advance();
18617 return Ok(Expr {
18618 kind: ExprKind::String(s),
18619 line,
18620 });
18621 }
18622 }
18623 if matches!(self.peek_at(1), Token::RBrace) {
18624 if let Some(s) = Self::operator_keyword_to_ident_str(self.peek()) {
18625 self.advance();
18626 return Ok(Expr {
18627 kind: ExprKind::String(s.to_string()),
18628 line,
18629 });
18630 }
18631 }
18632 self.parse_expression()
18633 }
18634
18635 #[inline]
18637 fn peek_is_glob_par_progress_kw(&self) -> bool {
18638 matches!(self.peek(), Token::Ident(ref kw) if kw == "progress")
18639 && matches!(self.peek_at(1), Token::FatArrow)
18640 }
18641
18642 fn parse_pattern_list_until_rparen_or_progress(&mut self) -> StrykeResult<Vec<Expr>> {
18644 let mut args = Vec::new();
18645 loop {
18646 if matches!(self.peek(), Token::RParen | Token::Eof) {
18647 break;
18648 }
18649 if self.peek_is_glob_par_progress_kw() {
18650 break;
18651 }
18652 args.push(self.parse_assign_expr()?);
18653 match self.peek() {
18654 Token::RParen => break,
18655 Token::Comma => {
18656 self.advance();
18657 if matches!(self.peek(), Token::RParen) {
18658 break;
18659 }
18660 if self.peek_is_glob_par_progress_kw() {
18661 break;
18662 }
18663 }
18664 _ => {
18665 return Err(self.syntax_err(
18666 "expected `,`, `)`, or `progress =>` after argument in `glob_par` / `par_sed`",
18667 self.peek_line(),
18668 ));
18669 }
18670 }
18671 }
18672 Ok(args)
18673 }
18674
18675 fn parse_pattern_list_glob_par_bare(&mut self) -> StrykeResult<Vec<Expr>> {
18677 let mut args = Vec::new();
18678 loop {
18679 if matches!(
18680 self.peek(),
18681 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof
18682 ) {
18683 break;
18684 }
18685 if self.peek_is_postfix_stmt_modifier_keyword() {
18686 break;
18687 }
18688 if self.peek_is_glob_par_progress_kw() {
18689 break;
18690 }
18691 args.push(self.parse_assign_expr()?);
18692 if !self.eat(&Token::Comma) {
18693 break;
18694 }
18695 if self.peek_is_glob_par_progress_kw() {
18696 break;
18697 }
18698 }
18699 Ok(args)
18700 }
18701
18702 fn parse_glob_par_or_par_sed_args(&mut self) -> StrykeResult<(Vec<Expr>, Option<Box<Expr>>)> {
18704 if matches!(self.peek(), Token::LParen) {
18705 self.advance();
18706 let args = self.parse_pattern_list_until_rparen_or_progress()?;
18707 let progress = if self.peek_is_glob_par_progress_kw() {
18708 self.advance();
18709 self.expect(&Token::FatArrow)?;
18710 Some(Box::new(self.parse_assign_expr()?))
18711 } else {
18712 None
18713 };
18714 self.expect(&Token::RParen)?;
18715 Ok((args, progress))
18716 } else {
18717 let args = self.parse_pattern_list_glob_par_bare()?;
18718 let progress = if self.peek_is_glob_par_progress_kw() {
18720 self.advance();
18721 self.expect(&Token::FatArrow)?;
18722 Some(Box::new(self.parse_assign_expr()?))
18723 } else {
18724 None
18725 };
18726 Ok((args, progress))
18727 }
18728 }
18729
18730 pub(crate) fn parse_arg_list(&mut self) -> StrykeResult<Vec<Expr>> {
18731 let mut args = Vec::new();
18732 let saved_no_pf = self.no_pipe_forward_depth;
18736 self.no_pipe_forward_depth = 0;
18737 while !matches!(
18738 self.peek(),
18739 Token::RParen | Token::RBracket | Token::RBrace | Token::Eof
18740 ) {
18741 let arg = match self.parse_assign_expr() {
18742 Ok(e) => e,
18743 Err(err) => {
18744 self.no_pipe_forward_depth = saved_no_pf;
18745 return Err(err);
18746 }
18747 };
18748 args.push(arg);
18749 if !self.eat(&Token::Comma) && !self.eat(&Token::FatArrow) {
18750 break;
18751 }
18752 }
18753 self.no_pipe_forward_depth = saved_no_pf;
18754 Ok(args)
18755 }
18756
18757 pub(crate) fn parse_slice_arg_list(&mut self, is_hash: bool) -> StrykeResult<Vec<Expr>> {
18766 let mut args = Vec::new();
18767 let saved_no_pf = self.no_pipe_forward_depth;
18768 self.no_pipe_forward_depth = 0;
18769 while !matches!(
18770 self.peek(),
18771 Token::RParen | Token::RBracket | Token::RBrace | Token::Eof
18772 ) {
18773 let arg = match self.parse_slice_arg(is_hash) {
18774 Ok(e) => e,
18775 Err(err) => {
18776 self.no_pipe_forward_depth = saved_no_pf;
18777 return Err(err);
18778 }
18779 };
18780 args.push(arg);
18781 if !self.eat(&Token::Comma) && !self.eat(&Token::FatArrow) {
18782 break;
18783 }
18784 }
18785 self.no_pipe_forward_depth = saved_no_pf;
18786 Ok(args)
18787 }
18788
18789 fn parse_slice_arg(&mut self, is_hash: bool) -> StrykeResult<Expr> {
18791 let line = self.peek_line();
18792
18793 if matches!(self.peek(), Token::Colon) {
18795 self.advance();
18796 return self.finish_slice_range(None, false, is_hash, line);
18797 }
18798 if matches!(self.peek(), Token::PackageSep) {
18799 self.advance();
18800 return self.finish_slice_range(None, true, is_hash, line);
18801 }
18802
18803 self.suppress_colon_range = self.suppress_colon_range.saturating_add(1);
18806 let result = self.parse_slice_endpoint(is_hash);
18807 self.suppress_colon_range = self.suppress_colon_range.saturating_sub(1);
18808 let from_expr = result?;
18809
18810 if matches!(self.peek(), Token::Colon) {
18812 self.advance();
18813 return self.finish_slice_range(Some(Box::new(from_expr)), false, is_hash, line);
18814 }
18815 if matches!(self.peek(), Token::PackageSep) {
18816 self.advance();
18817 return self.finish_slice_range(Some(Box::new(from_expr)), true, is_hash, line);
18818 }
18819
18820 Ok(from_expr)
18821 }
18822
18823 fn finish_slice_range(
18830 &mut self,
18831 from: Option<Box<Expr>>,
18832 double: bool,
18833 is_hash: bool,
18834 line: usize,
18835 ) -> StrykeResult<Expr> {
18836 let (to, step) = if double {
18837 let step_v = self.parse_slice_optional_endpoint(is_hash)?;
18839 (None, step_v)
18840 } else {
18841 let to_v = self.parse_slice_optional_endpoint(is_hash)?;
18843 let step_v = if matches!(self.peek(), Token::Colon) {
18844 self.advance();
18845 self.parse_slice_optional_endpoint(is_hash)?
18846 } else if matches!(self.peek(), Token::PackageSep) {
18847 return Err(
18848 self.syntax_err("Unexpected `::` after slice TO endpoint".to_string(), line)
18849 );
18850 } else {
18851 None
18852 };
18853 (to_v, step_v)
18854 };
18855
18856 if let (Some(f), Some(t)) = (from.as_ref(), to.as_ref()) {
18859 return Ok(Expr {
18860 kind: ExprKind::Range {
18861 from: f.clone(),
18862 to: t.clone(),
18863 exclusive: false,
18864 step,
18865 },
18866 line,
18867 });
18868 }
18869
18870 Ok(Expr {
18871 kind: ExprKind::SliceRange { from, to, step },
18872 line,
18873 })
18874 }
18875
18876 fn parse_slice_optional_endpoint(&mut self, is_hash: bool) -> StrykeResult<Option<Box<Expr>>> {
18879 if matches!(
18880 self.peek(),
18881 Token::Colon
18882 | Token::PackageSep
18883 | Token::Comma
18884 | Token::RBracket
18885 | Token::RBrace
18886 | Token::Eof
18887 ) {
18888 return Ok(None);
18889 }
18890 self.suppress_colon_range = self.suppress_colon_range.saturating_add(1);
18891 let r = self.parse_slice_endpoint(is_hash);
18892 self.suppress_colon_range = self.suppress_colon_range.saturating_sub(1);
18893 Ok(Some(Box::new(r?)))
18894 }
18895
18896 fn parse_slice_endpoint(&mut self, is_hash: bool) -> StrykeResult<Expr> {
18900 if is_hash {
18901 if let Token::Ident(name) = self.peek().clone() {
18902 if matches!(
18903 self.peek_at(1),
18904 Token::Colon
18905 | Token::PackageSep
18906 | Token::Comma
18907 | Token::RBracket
18908 | Token::RBrace
18909 ) {
18910 let line = self.peek_line();
18911 self.advance();
18912 return Ok(Expr {
18913 kind: ExprKind::String(name),
18914 line,
18915 });
18916 }
18917 }
18918 }
18919 self.parse_assign_expr()
18920 }
18921
18922 fn parse_method_arg_list_no_paren(&mut self) -> StrykeResult<Vec<Expr>> {
18926 let mut args = Vec::new();
18927 let call_line = self.prev_line();
18928 loop {
18929 if args.is_empty() && matches!(self.peek(), Token::LBrace) {
18932 break;
18933 }
18934 if matches!(
18935 self.peek(),
18936 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof | Token::PipeForward
18937 ) {
18938 break;
18939 }
18940 if let Token::Ident(ref kw) = self.peek().clone() {
18941 if matches!(
18942 kw.as_str(),
18943 "if" | "unless" | "while" | "until" | "for" | "foreach"
18944 ) {
18945 break;
18946 }
18947 }
18948 if args.is_empty()
18951 && (self.peek_method_arg_infix_terminator() || matches!(self.peek(), Token::Comma))
18952 {
18953 break;
18954 }
18955 if args.is_empty() && self.peek_line() > call_line {
18958 break;
18959 }
18960 args.push(self.parse_assign_expr()?);
18961 if !self.eat(&Token::Comma) {
18962 break;
18963 }
18964 }
18965 Ok(args)
18966 }
18967
18968 fn peek_method_arg_infix_terminator(&self) -> bool {
18971 matches!(
18972 self.peek(),
18973 Token::Plus
18974 | Token::Minus
18975 | Token::Star
18976 | Token::Slash
18977 | Token::Percent
18978 | Token::Power
18979 | Token::Dot
18980 | Token::X
18981 | Token::NumEq
18982 | Token::NumNe
18983 | Token::NumLt
18984 | Token::NumGt
18985 | Token::NumLe
18986 | Token::NumGe
18987 | Token::Spaceship
18988 | Token::StrEq
18989 | Token::StrNe
18990 | Token::StrLt
18991 | Token::StrGt
18992 | Token::StrLe
18993 | Token::StrGe
18994 | Token::StrCmp
18995 | Token::LogAnd
18996 | Token::LogOr
18997 | Token::LogAndWord
18998 | Token::LogOrWord
18999 | Token::DefinedOr
19000 | Token::BitAnd
19001 | Token::BitOr
19002 | Token::BitXor
19003 | Token::ShiftLeft
19004 | Token::ShiftRight
19005 | Token::Range
19006 | Token::RangeExclusive
19007 | Token::BindMatch
19008 | Token::BindNotMatch
19009 | Token::Arrow
19010 | Token::Question
19012 | Token::Colon
19013 | Token::Assign
19015 | Token::PlusAssign
19016 | Token::MinusAssign
19017 | Token::MulAssign
19018 | Token::DivAssign
19019 | Token::ModAssign
19020 | Token::PowAssign
19021 | Token::DotAssign
19022 | Token::AndAssign
19023 | Token::OrAssign
19024 | Token::XorAssign
19025 | Token::DefinedOrAssign
19026 | Token::ShiftLeftAssign
19027 | Token::ShiftRightAssign
19028 | Token::BitAndAssign
19029 | Token::BitOrAssign
19030 )
19031 }
19032
19033 fn parse_list_until_terminator(&mut self) -> StrykeResult<Vec<Expr>> {
19034 self.parse_list_until_terminator_inner(false)
19035 }
19036
19037 fn parse_list_until_terminator_allow_pipe(&mut self) -> StrykeResult<Vec<Expr>> {
19042 self.parse_list_until_terminator_inner(true)
19043 }
19044
19045 fn parse_list_until_terminator_inner(&mut self, allow_pipe: bool) -> StrykeResult<Vec<Expr>> {
19046 let mut args = Vec::new();
19047 let call_line = self.prev_line();
19052 loop {
19053 let is_terminator = if allow_pipe {
19057 matches!(
19058 self.peek(),
19059 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof
19060 )
19061 } else {
19062 matches!(
19063 self.peek(),
19064 Token::Semicolon
19065 | Token::RBrace
19066 | Token::RParen
19067 | Token::Eof
19068 | Token::PipeForward
19069 )
19070 };
19071 if is_terminator {
19072 break;
19073 }
19074 if let Token::Ident(ref kw) = self.peek().clone() {
19076 if matches!(
19077 kw.as_str(),
19078 "if" | "unless" | "while" | "until" | "for" | "foreach"
19079 ) {
19080 break;
19081 }
19082 }
19083 if args.is_empty() && self.peek_line() > call_line {
19090 break;
19091 }
19092 if allow_pipe {
19096 args.push(self.parse_assign_expr()?);
19097 } else {
19098 args.push(self.parse_assign_expr_stop_at_pipe()?);
19099 }
19100 if !self.eat(&Token::Comma) {
19101 break;
19102 }
19103 }
19104 Ok(args)
19105 }
19106
19107 fn parse_forced_hashref_body(&mut self, line: usize) -> StrykeResult<Expr> {
19114 let saved = self.pos;
19115 if let Ok(pairs) = self.try_parse_hash_ref() {
19116 return Ok(Expr {
19117 kind: ExprKind::HashRef(pairs),
19118 line,
19119 });
19120 }
19121 self.pos = saved;
19123 if matches!(self.peek(), Token::RBrace) {
19124 self.advance();
19125 return Ok(Expr {
19126 kind: ExprKind::HashRef(vec![]),
19127 line,
19128 });
19129 }
19130 let inner = self.parse_expression()?;
19134 self.expect(&Token::RBrace)?;
19135 let sentinel_key = Expr {
19136 kind: ExprKind::String("__HASH_SPREAD__".into()),
19137 line,
19138 };
19139 Ok(Expr {
19140 kind: ExprKind::HashRef(vec![(sentinel_key, inner)]),
19141 line,
19142 })
19143 }
19144
19145 fn try_parse_hash_ref(&mut self) -> StrykeResult<Vec<(Expr, Expr)>> {
19146 let mut pairs = Vec::new();
19147 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
19148 let line = self.peek_line();
19153 let key = if let Token::Ident(ref name) = self.peek().clone() {
19154 if matches!(self.peek_at(1), Token::FatArrow)
19155 && !Self::is_underscore_topic_slot(name)
19156 {
19157 self.advance();
19158 Expr {
19159 kind: ExprKind::String(name.clone()),
19160 line,
19161 }
19162 } else {
19163 self.parse_assign_expr()?
19164 }
19165 } else {
19166 self.parse_assign_expr()?
19167 };
19168 if matches!(self.peek(), Token::RBrace | Token::Comma)
19172 && matches!(
19173 key.kind,
19174 ExprKind::HashVar(_)
19175 | ExprKind::Deref {
19176 kind: Sigil::Hash,
19177 ..
19178 }
19179 )
19180 {
19181 let sentinel_key = Expr {
19185 kind: ExprKind::String("__HASH_SPREAD__".into()),
19186 line,
19187 };
19188 pairs.push((sentinel_key, key));
19189 self.eat(&Token::Comma);
19190 continue;
19191 }
19192 if self.eat(&Token::FatArrow) || self.eat(&Token::Comma) {
19194 let val = self.parse_assign_expr()?;
19195 pairs.push((key, val));
19196 self.eat(&Token::Comma);
19197 } else {
19198 return Err(self.syntax_err("Expected => or , in hash ref", key.line));
19199 }
19200 }
19201 self.expect(&Token::RBrace)?;
19202 Ok(pairs)
19203 }
19204
19205 fn parse_hashref_pairs_until(&mut self, term: &Token) -> StrykeResult<Vec<(Expr, Expr)>> {
19210 let mut pairs = Vec::new();
19211 while !matches!(&self.peek(), t if std::mem::discriminant(*t) == std::mem::discriminant(term))
19212 && !matches!(self.peek(), Token::Eof)
19213 {
19214 let line = self.peek_line();
19215 let key = if let Token::Ident(ref name) = self.peek().clone() {
19216 if matches!(self.peek_at(1), Token::FatArrow)
19217 && !Self::is_underscore_topic_slot(name)
19218 {
19219 self.advance();
19220 Expr {
19221 kind: ExprKind::String(name.clone()),
19222 line,
19223 }
19224 } else {
19225 self.parse_assign_expr()?
19226 }
19227 } else {
19228 self.parse_assign_expr()?
19229 };
19230 if self.eat(&Token::FatArrow) || self.eat(&Token::Comma) {
19231 let val = self.parse_assign_expr()?;
19232 pairs.push((key, val));
19233 self.eat(&Token::Comma);
19234 } else {
19235 return Err(self.syntax_err("Expected => or , in hash ref", key.line));
19236 }
19237 }
19238 Ok(pairs)
19239 }
19240
19241 fn interp_chain_subscripts(
19247 &self,
19248 chars: &[char],
19249 i: &mut usize,
19250 mut base: Expr,
19251 line: usize,
19252 ) -> Expr {
19253 loop {
19254 let (after, requires_subscript) =
19256 if *i + 1 < chars.len() && chars[*i] == '-' && chars[*i + 1] == '>' {
19257 (*i + 2, true)
19258 } else {
19259 (*i, false)
19260 };
19261 if after >= chars.len() {
19262 break;
19263 }
19264 match chars[after] {
19265 '[' => {
19266 *i = after + 1;
19267 let mut idx_str = String::new();
19268 while *i < chars.len() && chars[*i] != ']' {
19269 idx_str.push(chars[*i]);
19270 *i += 1;
19271 }
19272 if *i < chars.len() {
19273 *i += 1;
19274 }
19275 let idx_expr = if let Some(rest) = idx_str.strip_prefix('$') {
19276 Expr {
19277 kind: ExprKind::ScalarVar(rest.to_string()),
19278 line,
19279 }
19280 } else if let Ok(n) = idx_str.parse::<i64>() {
19281 Expr {
19282 kind: ExprKind::Integer(n),
19283 line,
19284 }
19285 } else {
19286 Expr {
19287 kind: ExprKind::String(idx_str),
19288 line,
19289 }
19290 };
19291 base = Expr {
19292 kind: ExprKind::ArrowDeref {
19293 expr: Box::new(base),
19294 index: Box::new(idx_expr),
19295 kind: DerefKind::Array,
19296 },
19297 line,
19298 };
19299 }
19300 '{' => {
19301 *i = after + 1;
19302 let mut key = String::new();
19303 let mut depth = 1usize;
19304 while *i < chars.len() && depth > 0 {
19305 if chars[*i] == '{' {
19306 depth += 1;
19307 } else if chars[*i] == '}' {
19308 depth -= 1;
19309 if depth == 0 {
19310 break;
19311 }
19312 }
19313 key.push(chars[*i]);
19314 *i += 1;
19315 }
19316 if *i < chars.len() {
19317 *i += 1;
19318 }
19319 let key_expr = if let Some(rest) = key.strip_prefix('$') {
19320 Expr {
19321 kind: ExprKind::ScalarVar(rest.to_string()),
19322 line,
19323 }
19324 } else {
19325 Expr {
19326 kind: ExprKind::String(key),
19327 line,
19328 }
19329 };
19330 base = Expr {
19331 kind: ExprKind::ArrowDeref {
19332 expr: Box::new(base),
19333 index: Box::new(key_expr),
19334 kind: DerefKind::Hash,
19335 },
19336 line,
19337 };
19338 }
19339 _ => {
19340 if requires_subscript {
19341 }
19343 break;
19344 }
19345 }
19346 }
19347 base
19348 }
19349
19350 fn no_interop_check_scalar_var_name(&self, name: &str, line: usize) -> StrykeResult<()> {
19354 if crate::no_interop_mode() && (name == "a" || name == "b") {
19355 return Err(self.syntax_err(
19356 format!(
19357 "stryke uses `_` / `_1` (bareword in code) or `$_` / `$_1` \
19358 (sigil inside string interpolation / when whitespace would \
19359 change parsing) instead of `${}` (--no-interop is active)",
19360 name
19361 ),
19362 line,
19363 ));
19364 }
19365 Ok(())
19366 }
19367
19368 fn parse_interpolated_string(&self, s: &str, line: usize) -> StrykeResult<Expr> {
19369 let mut parts = Vec::new();
19371 let mut literal = String::new();
19372 let chars: Vec<char> = s.chars().collect();
19373 let mut i = 0;
19374
19375 'istr: while i < chars.len() {
19376 if chars[i] == LITERAL_DOLLAR_IN_DQUOTE {
19377 literal.push('$');
19378 i += 1;
19379 continue;
19380 }
19381 if chars[i] == LITERAL_AT_IN_DQUOTE {
19382 literal.push('@');
19383 i += 1;
19384 continue;
19385 }
19386 if chars[i] == '\\' && i + 1 < chars.len() && chars[i + 1] == '$' {
19388 literal.push('\\');
19389 i += 1;
19390 }
19392 if chars[i] == '$' && i + 1 < chars.len() {
19393 if !literal.is_empty() {
19394 parts.push(StringPart::Literal(std::mem::take(&mut literal)));
19395 }
19396 i += 1; while i < chars.len() && chars[i].is_whitespace() {
19399 i += 1;
19400 }
19401 if i >= chars.len() {
19402 return Err(self.syntax_err("Final $ should be \\$ or $name", line));
19403 }
19404 if chars[i] == '#' {
19406 i += 1;
19407 let mut sname = String::from("#");
19408 while i < chars.len()
19409 && (chars[i].is_alphanumeric() || chars[i] == '_' || chars[i] == ':')
19410 {
19411 sname.push(chars[i]);
19412 i += 1;
19413 }
19414 while i + 1 < chars.len() && chars[i] == ':' && chars[i + 1] == ':' {
19415 sname.push_str("::");
19416 i += 2;
19417 while i < chars.len() && (chars[i].is_alphanumeric() || chars[i] == '_') {
19418 sname.push(chars[i]);
19419 i += 1;
19420 }
19421 }
19422 self.no_interop_check_scalar_var_name(&sname, line)?;
19423 parts.push(StringPart::ScalarVar(sname));
19424 continue;
19425 }
19426 if chars[i] == '$' {
19430 let next_c = chars.get(i + 1).copied();
19431 let is_pid = match next_c {
19432 None => true,
19433 Some(c)
19434 if !c.is_ascii_digit() && !matches!(c, 'A'..='Z' | 'a'..='z' | '_') =>
19435 {
19436 true
19437 }
19438 _ => false,
19439 };
19440 if is_pid {
19441 parts.push(StringPart::ScalarVar("$$".to_string()));
19442 i += 1; continue;
19444 }
19445 i += 1; }
19447 if chars[i] == '{' {
19448 i += 1;
19454 let mut inner = String::new();
19455 let mut depth = 1usize;
19456 while i < chars.len() && depth > 0 {
19457 match chars[i] {
19458 '{' => depth += 1,
19459 '}' => {
19460 depth -= 1;
19461 if depth == 0 {
19462 break;
19463 }
19464 }
19465 _ => {}
19466 }
19467 inner.push(chars[i]);
19468 i += 1;
19469 }
19470 if i < chars.len() {
19471 i += 1; }
19473
19474 let trimmed = inner.trim();
19478 let is_expr = trimmed.starts_with('$')
19479 || trimmed.starts_with('\\')
19480 || trimmed.starts_with('@') || trimmed.starts_with('%') || trimmed.contains(['(', '+', '-', '*', '/', '.', '?', '&', '|']);
19483 let mut base: Expr = if is_expr {
19484 match parse_expression_from_str(trimmed, "<interp>") {
19488 Ok(e) => Expr {
19489 kind: ExprKind::Deref {
19490 expr: Box::new(e),
19491 kind: Sigil::Scalar,
19492 },
19493 line,
19494 },
19495 Err(_) => Expr {
19496 kind: ExprKind::ScalarVar(inner.clone()),
19497 line,
19498 },
19499 }
19500 } else {
19501 self.no_interop_check_scalar_var_name(&inner, line)?;
19503 Expr {
19504 kind: ExprKind::ScalarVar(inner),
19505 line,
19506 }
19507 };
19508
19509 base = self.interp_chain_subscripts(&chars, &mut i, base, line);
19513 parts.push(StringPart::Expr(base));
19514 } else if chars[i] == '^' {
19515 let mut name = String::from("^");
19517 i += 1;
19518 while i < chars.len() && (chars[i].is_alphanumeric() || chars[i] == '_') {
19519 name.push(chars[i]);
19520 i += 1;
19521 }
19522 if i < chars.len() && chars[i] == '{' {
19523 i += 1; let mut key = String::new();
19525 let mut depth = 1;
19526 while i < chars.len() && depth > 0 {
19527 if chars[i] == '{' {
19528 depth += 1;
19529 } else if chars[i] == '}' {
19530 depth -= 1;
19531 if depth == 0 {
19532 break;
19533 }
19534 }
19535 key.push(chars[i]);
19536 i += 1;
19537 }
19538 if i < chars.len() {
19539 i += 1;
19540 }
19541 let key_expr = if let Some(rest) = key.strip_prefix('$') {
19542 Expr {
19543 kind: ExprKind::ScalarVar(rest.to_string()),
19544 line,
19545 }
19546 } else {
19547 Expr {
19548 kind: ExprKind::String(key),
19549 line,
19550 }
19551 };
19552 parts.push(StringPart::Expr(Expr {
19553 kind: ExprKind::HashElement {
19554 hash: name,
19555 key: Box::new(key_expr),
19556 },
19557 line,
19558 }));
19559 } else if i < chars.len() && chars[i] == '[' {
19560 i += 1;
19561 let mut idx_str = String::new();
19562 while i < chars.len() && chars[i] != ']' {
19563 idx_str.push(chars[i]);
19564 i += 1;
19565 }
19566 if i < chars.len() {
19567 i += 1;
19568 }
19569 let idx_expr = if let Some(rest) = idx_str.strip_prefix('$') {
19570 Expr {
19571 kind: ExprKind::ScalarVar(rest.to_string()),
19572 line,
19573 }
19574 } else if let Ok(n) = idx_str.parse::<i64>() {
19575 Expr {
19576 kind: ExprKind::Integer(n),
19577 line,
19578 }
19579 } else {
19580 Expr {
19581 kind: ExprKind::String(idx_str),
19582 line,
19583 }
19584 };
19585 parts.push(StringPart::Expr(Expr {
19586 kind: ExprKind::ArrayElement {
19587 array: name,
19588 index: Box::new(idx_expr),
19589 },
19590 line,
19591 }));
19592 } else {
19593 self.no_interop_check_scalar_var_name(&name, line)?;
19594 parts.push(StringPart::ScalarVar(name));
19595 }
19596 } else if chars[i].is_alphabetic() || chars[i] == '_' {
19597 let mut name = String::new();
19598 while i < chars.len() && (chars[i].is_alphanumeric() || chars[i] == '_') {
19599 name.push(chars[i]);
19600 i += 1;
19601 }
19602 while i + 1 < chars.len() && chars[i] == ':' && chars[i + 1] == ':' {
19607 name.push_str("::");
19608 i += 2;
19609 while i < chars.len() && (chars[i].is_alphanumeric() || chars[i] == '_') {
19610 name.push(chars[i]);
19611 i += 1;
19612 }
19613 }
19614 let is_topic_slot = name == "_"
19619 || (name.len() > 1
19620 && name.starts_with('_')
19621 && name[1..].bytes().all(|b| b.is_ascii_digit()));
19622 if is_topic_slot {
19623 let try_indexed = chars.get(i) == Some(&'<')
19625 && chars.get(i + 1).is_some_and(|c| c.is_ascii_digit());
19626 let mut handled_indexed = false;
19627 if try_indexed {
19628 let mut j = i + 1;
19629 while j < chars.len() && chars[j].is_ascii_digit() {
19630 j += 1;
19631 }
19632 let digits: String = chars[i + 1..j].iter().collect();
19633 if let Ok(n) = digits.parse::<usize>() {
19634 if n >= 1 {
19635 for _ in 0..n {
19636 name.push('<');
19637 }
19638 i = j;
19639 handled_indexed = true;
19640 }
19641 }
19642 }
19643 if !handled_indexed {
19644 while i < chars.len() && chars[i] == '<' {
19645 name.push('<');
19646 i += 1;
19647 }
19648 }
19649 }
19650 self.no_interop_check_scalar_var_name(&name, line)?;
19655 let mut base = if i < chars.len() && chars[i] == '{' {
19660 i += 1; let mut key = String::new();
19663 let mut depth = 1;
19664 while i < chars.len() && depth > 0 {
19665 if chars[i] == '{' {
19666 depth += 1;
19667 } else if chars[i] == '}' {
19668 depth -= 1;
19669 if depth == 0 {
19670 break;
19671 }
19672 }
19673 key.push(chars[i]);
19674 i += 1;
19675 }
19676 if i < chars.len() {
19677 i += 1;
19678 } let key_expr = if let Some(rest) = key.strip_prefix('$') {
19680 Expr {
19681 kind: ExprKind::ScalarVar(rest.to_string()),
19682 line,
19683 }
19684 } else {
19685 Expr {
19686 kind: ExprKind::String(key),
19687 line,
19688 }
19689 };
19690 Expr {
19691 kind: ExprKind::HashElement {
19692 hash: name,
19693 key: Box::new(key_expr),
19694 },
19695 line,
19696 }
19697 } else if i < chars.len() && chars[i] == '[' {
19698 i += 1;
19700 let mut idx_str = String::new();
19701 while i < chars.len() && chars[i] != ']' {
19702 idx_str.push(chars[i]);
19703 i += 1;
19704 }
19705 if i < chars.len() {
19706 i += 1;
19707 }
19708 let idx_expr = if let Some(rest) = idx_str.strip_prefix('$') {
19709 Expr {
19710 kind: ExprKind::ScalarVar(rest.to_string()),
19711 line,
19712 }
19713 } else if let Ok(n) = idx_str.parse::<i64>() {
19714 Expr {
19715 kind: ExprKind::Integer(n),
19716 line,
19717 }
19718 } else {
19719 Expr {
19720 kind: ExprKind::String(idx_str),
19721 line,
19722 }
19723 };
19724 Expr {
19725 kind: ExprKind::ArrayElement {
19726 array: name,
19727 index: Box::new(idx_expr),
19728 },
19729 line,
19730 }
19731 } else {
19732 Expr {
19734 kind: ExprKind::ScalarVar(name),
19735 line,
19736 }
19737 };
19738
19739 base = self.interp_chain_subscripts(&chars, &mut i, base, line);
19743 parts.push(StringPart::Expr(base));
19744 } else if chars[i].is_ascii_digit() {
19745 if chars[i] == '0' {
19747 i += 1;
19748 if i < chars.len() && chars[i].is_ascii_digit() {
19749 return Err(self.syntax_err(
19750 "Numeric variables with more than one digit may not start with '0'",
19751 line,
19752 ));
19753 }
19754 parts.push(StringPart::ScalarVar("0".into()));
19755 } else {
19756 let start = i;
19757 while i < chars.len() && chars[i].is_ascii_digit() {
19758 i += 1;
19759 }
19760 parts.push(StringPart::ScalarVar(chars[start..i].iter().collect()));
19761 }
19762 } else {
19763 let c = chars[i];
19764 let probe = c.to_string();
19765 if VMHelper::is_special_scalar_name_for_get(&probe)
19771 || matches!(c, '\'' | '`' | '&')
19772 {
19773 i += 1;
19774 if i < chars.len() && chars[i] == '{' {
19776 i += 1; let mut key = String::new();
19778 let mut depth = 1;
19779 while i < chars.len() && depth > 0 {
19780 if chars[i] == '{' {
19781 depth += 1;
19782 } else if chars[i] == '}' {
19783 depth -= 1;
19784 if depth == 0 {
19785 break;
19786 }
19787 }
19788 key.push(chars[i]);
19789 i += 1;
19790 }
19791 if i < chars.len() {
19792 i += 1;
19793 } let key_expr = if let Some(rest) = key.strip_prefix('$') {
19795 Expr {
19796 kind: ExprKind::ScalarVar(rest.to_string()),
19797 line,
19798 }
19799 } else {
19800 Expr {
19801 kind: ExprKind::String(key),
19802 line,
19803 }
19804 };
19805 let mut base = Expr {
19806 kind: ExprKind::HashElement {
19807 hash: probe,
19808 key: Box::new(key_expr),
19809 },
19810 line,
19811 };
19812 base = self.interp_chain_subscripts(&chars, &mut i, base, line);
19813 parts.push(StringPart::Expr(base));
19814 } else {
19815 let mut base = Expr {
19817 kind: ExprKind::ScalarVar(probe),
19818 line,
19819 };
19820 base = self.interp_chain_subscripts(&chars, &mut i, base, line);
19821 if matches!(base.kind, ExprKind::ScalarVar(_)) {
19822 if let ExprKind::ScalarVar(name) = base.kind {
19824 self.no_interop_check_scalar_var_name(&name, line)?;
19825 parts.push(StringPart::ScalarVar(name));
19826 }
19827 } else {
19828 parts.push(StringPart::Expr(base));
19829 }
19830 }
19831 } else {
19832 literal.push('$');
19833 literal.push(c);
19834 i += 1;
19835 }
19836 }
19837 } else if chars[i] == '@' && i + 1 < chars.len() {
19838 let next = chars[i + 1];
19839 if next == '$' {
19841 if !literal.is_empty() {
19842 parts.push(StringPart::Literal(std::mem::take(&mut literal)));
19843 }
19844 i += 1; debug_assert_eq!(chars[i], '$');
19846 i += 1; while i < chars.len() && chars[i].is_whitespace() {
19848 i += 1;
19849 }
19850 if i >= chars.len() {
19851 return Err(self.syntax_err(
19852 "Expected variable or block after `@$` in double-quoted string",
19853 line,
19854 ));
19855 }
19856 let inner_expr = if chars[i] == '{' {
19857 i += 1;
19858 let start = i;
19859 let mut depth = 1usize;
19860 while i < chars.len() && depth > 0 {
19861 match chars[i] {
19862 '{' => depth += 1,
19863 '}' => {
19864 depth -= 1;
19865 if depth == 0 {
19866 break;
19867 }
19868 }
19869 _ => {}
19870 }
19871 i += 1;
19872 }
19873 if depth != 0 {
19874 return Err(self.syntax_err(
19875 "Unterminated `${ ... }` after `@` in double-quoted string",
19876 line,
19877 ));
19878 }
19879 let inner: String = chars[start..i].iter().collect();
19880 i += 1; parse_expression_from_str(inner.trim(), "-e")?
19882 } else {
19883 let mut name = String::new();
19884 if chars[i] == '^' {
19885 name.push('^');
19886 i += 1;
19887 while i < chars.len() && (chars[i].is_alphanumeric() || chars[i] == '_')
19888 {
19889 name.push(chars[i]);
19890 i += 1;
19891 }
19892 } else {
19893 while i < chars.len()
19894 && (chars[i].is_alphanumeric()
19895 || chars[i] == '_'
19896 || chars[i] == ':')
19897 {
19898 name.push(chars[i]);
19899 i += 1;
19900 }
19901 while i + 1 < chars.len() && chars[i] == ':' && chars[i + 1] == ':' {
19902 name.push_str("::");
19903 i += 2;
19904 while i < chars.len()
19905 && (chars[i].is_alphanumeric() || chars[i] == '_')
19906 {
19907 name.push(chars[i]);
19908 i += 1;
19909 }
19910 }
19911 }
19912 if name.is_empty() {
19913 return Err(self.syntax_err(
19914 "Expected identifier after `@$` in double-quoted string",
19915 line,
19916 ));
19917 }
19918 Expr {
19919 kind: ExprKind::ScalarVar(name),
19920 line,
19921 }
19922 };
19923 parts.push(StringPart::Expr(Expr {
19924 kind: ExprKind::Deref {
19925 expr: Box::new(inner_expr),
19926 kind: Sigil::Array,
19927 },
19928 line,
19929 }));
19930 continue 'istr;
19931 }
19932 if next == '{' {
19933 if !literal.is_empty() {
19934 parts.push(StringPart::Literal(std::mem::take(&mut literal)));
19935 }
19936 i += 2; let start = i;
19938 let mut depth = 1usize;
19939 while i < chars.len() && depth > 0 {
19940 match chars[i] {
19941 '{' => depth += 1,
19942 '}' => {
19943 depth -= 1;
19944 if depth == 0 {
19945 break;
19946 }
19947 }
19948 _ => {}
19949 }
19950 i += 1;
19951 }
19952 if depth != 0 {
19953 return Err(
19954 self.syntax_err("Unterminated @{ ... } in double-quoted string", line)
19955 );
19956 }
19957 let inner: String = chars[start..i].iter().collect();
19958 i += 1; let inner_expr = parse_expression_from_str(inner.trim(), "-e")?;
19960 parts.push(StringPart::Expr(Expr {
19961 kind: ExprKind::Deref {
19962 expr: Box::new(inner_expr),
19963 kind: Sigil::Array,
19964 },
19965 line,
19966 }));
19967 continue 'istr;
19968 }
19969 if !(next.is_alphabetic() || next == '_' || next == '+' || next == '-') {
19970 literal.push(chars[i]);
19971 i += 1;
19972 } else {
19973 if !literal.is_empty() {
19974 parts.push(StringPart::Literal(std::mem::take(&mut literal)));
19975 }
19976 i += 1;
19977 let mut name = String::new();
19978 if i < chars.len() && (chars[i] == '+' || chars[i] == '-') {
19979 name.push(chars[i]);
19980 i += 1;
19981 } else {
19982 while i < chars.len() && (chars[i].is_alphanumeric() || chars[i] == '_') {
19983 name.push(chars[i]);
19984 i += 1;
19985 }
19986 while i + 1 < chars.len() && chars[i] == ':' && chars[i + 1] == ':' {
19987 name.push_str("::");
19988 i += 2;
19989 while i < chars.len() && (chars[i].is_alphanumeric() || chars[i] == '_')
19990 {
19991 name.push(chars[i]);
19992 i += 1;
19993 }
19994 }
19995 }
19996 if i < chars.len() && chars[i] == '[' {
19997 i += 1;
19998 let start_inner = i;
19999 let mut depth = 1usize;
20000 while i < chars.len() && depth > 0 {
20001 match chars[i] {
20002 '[' => depth += 1,
20003 ']' => depth -= 1,
20004 _ => {}
20005 }
20006 if depth == 0 {
20007 let inner: String = chars[start_inner..i].iter().collect();
20008 i += 1; let indices = parse_slice_indices_from_str(inner.trim(), "-e")?;
20010 parts.push(StringPart::Expr(Expr {
20011 kind: ExprKind::ArraySlice {
20012 array: name.clone(),
20013 indices,
20014 },
20015 line,
20016 }));
20017 continue 'istr;
20018 }
20019 i += 1;
20020 }
20021 return Err(self.syntax_err(
20022 "Unterminated [ in array slice inside quoted string",
20023 line,
20024 ));
20025 }
20026 parts.push(StringPart::ArrayVar(name));
20027 }
20028 } else if chars[i] == '#'
20029 && i + 1 < chars.len()
20030 && chars[i + 1] == '{'
20031 && !crate::compat_mode()
20032 {
20033 if !literal.is_empty() {
20035 parts.push(StringPart::Literal(std::mem::take(&mut literal)));
20036 }
20037 i += 2; let mut inner = String::new();
20039 let mut depth = 1usize;
20040 while i < chars.len() && depth > 0 {
20041 match chars[i] {
20042 '{' => depth += 1,
20043 '}' => {
20044 depth -= 1;
20045 if depth == 0 {
20046 break;
20047 }
20048 }
20049 _ => {}
20050 }
20051 inner.push(chars[i]);
20052 i += 1;
20053 }
20054 if i < chars.len() {
20055 i += 1; }
20057 let expr = parse_block_from_str(inner.trim(), "-e", line)?;
20058 parts.push(StringPart::Expr(expr));
20059 } else {
20060 literal.push(chars[i]);
20061 i += 1;
20062 }
20063 }
20064 if !literal.is_empty() {
20065 parts.push(StringPart::Literal(literal));
20066 }
20067
20068 if parts.len() == 1 {
20069 if let StringPart::Literal(s) = &parts[0] {
20070 return Ok(Expr {
20071 kind: ExprKind::String(s.clone()),
20072 line,
20073 });
20074 }
20075 }
20076 if parts.is_empty() {
20077 return Ok(Expr {
20078 kind: ExprKind::String(String::new()),
20079 line,
20080 });
20081 }
20082
20083 Ok(Expr {
20084 kind: ExprKind::InterpolatedString(parts),
20085 line,
20086 })
20087 }
20088
20089 fn expr_to_overload_key(&self, e: &Expr) -> StrykeResult<String> {
20090 match &e.kind {
20091 ExprKind::String(s) => Ok(s.clone()),
20092 _ => Err(self.syntax_err(
20093 "overload key must be a string literal (e.g. '\"\"' or '+')",
20094 e.line,
20095 )),
20096 }
20097 }
20098
20099 fn expr_to_overload_sub(&mut self, e: &Expr) -> StrykeResult<String> {
20100 match &e.kind {
20101 ExprKind::String(s) => Ok(s.clone()),
20102 ExprKind::Integer(n) => Ok(n.to_string()),
20103 ExprKind::SubroutineRef(s) | ExprKind::SubroutineCodeRef(s) => Ok(s.clone()),
20104 ExprKind::CodeRef { params, body } => {
20108 let id = self.next_overload_anon_id;
20109 self.next_overload_anon_id = self.next_overload_anon_id.saturating_add(1);
20110 let name = format!("__overload_anon_{}", id);
20111 self.pending_synthetic_subs.push(Statement {
20112 label: None,
20113 kind: StmtKind::SubDecl {
20114 name: name.clone(),
20115 params: params.clone(),
20116 body: body.clone(),
20117 prototype: None,
20118 },
20119 line: e.line,
20120 });
20121 Ok(name)
20122 }
20123 _ => Err(self.syntax_err(
20124 "overload handler must be a string literal, number (e.g. fallback => 1), or \\&subname (method in current package)",
20125 e.line,
20126 )),
20127 }
20128 }
20129}
20130
20131fn merge_expr_list(parts: Vec<Expr>) -> Expr {
20132 if parts.len() == 1 {
20133 parts.into_iter().next().unwrap()
20134 } else {
20135 let line = parts.first().map(|e| e.line).unwrap_or(0);
20136 Expr {
20137 kind: ExprKind::List(parts),
20138 line,
20139 }
20140 }
20141}
20142
20143pub fn parse_expression_from_str(s: &str, file: &str) -> StrykeResult<Expr> {
20145 let mut lexer = Lexer::new_with_file(s, file);
20146 let tokens = lexer.tokenize()?;
20147 let mut parser = Parser::new_with_file(tokens, file);
20148 let e = parser.parse_expression()?;
20149 if !parser.at_eof() {
20150 return Err(parser.syntax_err(
20151 "Extra tokens in embedded string expression",
20152 parser.peek_line(),
20153 ));
20154 }
20155 Ok(e)
20156}
20157
20158pub fn parse_block_from_str(s: &str, file: &str, line: usize) -> StrykeResult<Expr> {
20160 let mut lexer = Lexer::new_with_file(s, file);
20161 let tokens = lexer.tokenize()?;
20162 let mut parser = Parser::new_with_file(tokens, file);
20163 let stmts = parser.parse_statements()?;
20164 let inner_line = stmts.first().map(|st| st.line).unwrap_or(line);
20165 let inner = Expr {
20166 kind: ExprKind::CodeRef {
20167 params: vec![],
20168 body: stmts,
20169 },
20170 line: inner_line,
20171 };
20172 Ok(Expr {
20173 kind: ExprKind::Do(Box::new(inner)),
20174 line,
20175 })
20176}
20177
20178pub fn parse_slice_indices_from_str(s: &str, file: &str) -> StrykeResult<Vec<Expr>> {
20181 let mut lexer = Lexer::new_with_file(s, file);
20182 let tokens = lexer.tokenize()?;
20183 let mut parser = Parser::new_with_file(tokens, file);
20184 parser.parse_arg_list()
20185}
20186
20187pub fn parse_format_value_line(line: &str) -> StrykeResult<Vec<Expr>> {
20188 let trimmed = line.trim();
20189 if trimmed.is_empty() {
20190 return Ok(vec![]);
20191 }
20192 let mut lexer = Lexer::new(trimmed);
20193 let tokens = lexer.tokenize()?;
20194 let mut parser = Parser::new(tokens);
20195 let mut exprs = Vec::new();
20196 loop {
20197 if parser.at_eof() {
20198 break;
20199 }
20200 exprs.push(parser.parse_assign_expr()?);
20202 if parser.eat(&Token::Comma) {
20203 continue;
20204 }
20205 if !parser.at_eof() {
20206 return Err(parser.syntax_err("Extra tokens in format value line", parser.peek_line()));
20207 }
20208 break;
20209 }
20210 Ok(exprs)
20211}
20212
20213#[cfg(test)]
20214mod tests {
20215 use super::*;
20216
20217 fn parse_ok(code: &str) -> Program {
20218 let mut lexer = Lexer::new(code);
20219 let tokens = lexer.tokenize().expect("tokenize");
20220 let mut parser = Parser::new(tokens);
20221 parser.parse_program().expect("parse")
20222 }
20223
20224 fn parse_err(code: &str) -> String {
20225 let mut lexer = Lexer::new(code);
20226 let tokens = match lexer.tokenize() {
20227 Ok(t) => t,
20228 Err(e) => return e.message,
20229 };
20230 let mut parser = Parser::new(tokens);
20231 parser.parse_program().unwrap_err().message
20232 }
20233
20234 #[test]
20235 fn parse_empty_program() {
20236 let p = parse_ok("");
20237 assert!(p.statements.is_empty());
20238 }
20239
20240 #[test]
20241 fn parse_semicolons_only() {
20242 let p = parse_ok(";;");
20243 assert!(p.statements.len() <= 3);
20244 }
20245
20246 #[test]
20247 fn parse_simple_scalar_assignment() {
20248 let p = parse_ok("$x = 1");
20249 assert_eq!(p.statements.len(), 1);
20250 }
20251
20252 #[test]
20253 fn parse_simple_array_assignment() {
20254 let p = parse_ok("@arr = (1, 2, 3)");
20255 assert_eq!(p.statements.len(), 1);
20256 }
20257
20258 #[test]
20259 fn parse_simple_hash_assignment() {
20260 let p = parse_ok("%h = (a => 1, b => 2)");
20261 assert_eq!(p.statements.len(), 1);
20262 }
20263
20264 #[test]
20265 fn parse_subroutine_decl() {
20266 let p = parse_ok("fn foo { 1 }");
20267 assert_eq!(p.statements.len(), 1);
20268 match &p.statements[0].kind {
20269 StmtKind::SubDecl { name, .. } => assert_eq!(name, "foo"),
20270 _ => panic!("expected SubDecl"),
20271 }
20272 }
20273
20274 #[test]
20275 fn parse_class_method_expr_body_shorthand() {
20276 let p = parse_ok("class X { fn adg = \"\" }");
20277 match &p.statements[0].kind {
20278 StmtKind::ClassDecl { def } => {
20279 let m = def.method("adg").expect("adg method");
20280 let body = m.body.as_ref().expect("body");
20281 assert_eq!(body.len(), 1);
20282 match &body[0].kind {
20283 StmtKind::Expression(e) => match &e.kind {
20284 ExprKind::String(s) => assert!(s.is_empty()),
20285 _ => panic!("expected string expr, got {:?}", e.kind),
20286 },
20287 _ => panic!("expected expression stmt"),
20288 }
20289 }
20290 _ => panic!("expected ClassDecl"),
20291 }
20292 }
20293
20294 #[test]
20295 fn parse_named_fn_eq_shorthand_with_sig() {
20296 let p = parse_ok("fn add_one($x) = $x + 1");
20297 match &p.statements[0].kind {
20298 StmtKind::SubDecl {
20299 name, params, body, ..
20300 } => {
20301 assert_eq!(name, "add_one");
20302 assert_eq!(params.len(), 1);
20303 assert_eq!(body.len(), 1);
20304 }
20305 _ => panic!("expected SubDecl"),
20306 }
20307 }
20308
20309 #[test]
20310 fn parse_anon_fn_eq_shorthand_with_sig() {
20311 let p = parse_ok("my $f = fn($x) = 23");
20312 match &p.statements[0].kind {
20313 StmtKind::My(decls) => {
20314 let init = decls[0].initializer.as_ref().expect("initializer");
20315 match &init.kind {
20316 ExprKind::CodeRef { params, body } => {
20317 assert_eq!(params.len(), 1);
20318 assert_eq!(body.len(), 1);
20319 }
20320 _ => panic!("expected CodeRef"),
20321 }
20322 }
20323 _ => panic!("expected My"),
20324 }
20325 }
20326
20327 #[test]
20328 fn parse_struct_method_eq_shorthand() {
20329 let p = parse_ok("struct S { fn double($a) = $a * 2 }");
20330 match &p.statements[0].kind {
20331 StmtKind::StructDecl { def } => {
20332 assert_eq!(def.methods.len(), 1);
20333 assert_eq!(def.methods[0].name, "double");
20334 assert_eq!(def.methods[0].body.len(), 1);
20335 }
20336 _ => panic!("expected StructDecl"),
20337 }
20338 }
20339
20340 #[test]
20341 fn parse_trait_method_eq_shorthand() {
20342 let p = parse_ok("trait T { fn k = 0 }");
20343 match &p.statements[0].kind {
20344 StmtKind::TraitDecl { def } => {
20345 let m = def.method("k").expect("k");
20346 let body = m.body.as_ref().expect("default body");
20347 assert_eq!(body.len(), 1);
20348 }
20349 _ => panic!("expected TraitDecl"),
20350 }
20351 }
20352
20353 #[test]
20354 fn parse_fn_eq_shorthand_rejects_top_level_comma() {
20355 let msg = parse_err("fn z = 1, 2");
20356 assert!(
20357 msg.contains("single expression") || msg.contains("comma"),
20358 "{}",
20359 msg
20360 );
20361 }
20362
20363 #[test]
20364 fn parse_subroutine_with_prototype() {
20365 let p = parse_ok("fn foo ($$) { 1 }");
20366 assert_eq!(p.statements.len(), 1);
20367 match &p.statements[0].kind {
20368 StmtKind::SubDecl { prototype, .. } => {
20369 assert!(prototype.is_some());
20370 }
20371 _ => panic!("expected SubDecl"),
20372 }
20373 }
20374
20375 #[test]
20376 fn parse_anonymous_fn() {
20377 let p = parse_ok("my $f = fn { 1 }");
20378 assert_eq!(p.statements.len(), 1);
20379 }
20380
20381 #[test]
20382 fn parse_if_statement() {
20383 let p = parse_ok("if (1) { 2 }");
20384 assert_eq!(p.statements.len(), 1);
20385 matches!(&p.statements[0].kind, StmtKind::If { .. });
20386 }
20387
20388 #[test]
20389 fn parse_if_elsif_else() {
20390 let p = parse_ok("if (0) { 1 } elsif (1) { 2 } else { 3 }");
20391 assert_eq!(p.statements.len(), 1);
20392 }
20393
20394 #[test]
20395 fn parse_unless_statement() {
20396 let p = parse_ok("unless (0) { 1 }");
20397 assert_eq!(p.statements.len(), 1);
20398 }
20399
20400 #[test]
20401 fn parse_while_loop() {
20402 let p = parse_ok("while ($x) { $x-- }");
20403 assert_eq!(p.statements.len(), 1);
20404 }
20405
20406 #[test]
20407 fn parse_until_loop() {
20408 let p = parse_ok("until ($x) { $x++ }");
20409 assert_eq!(p.statements.len(), 1);
20410 }
20411
20412 #[test]
20413 fn parse_for_c_style() {
20414 let p = parse_ok("for (my $i=0; $i<10; $i++) { 1 }");
20415 assert_eq!(p.statements.len(), 1);
20416 }
20417
20418 #[test]
20419 fn parse_foreach_loop() {
20420 let p = parse_ok("foreach my $x (@arr) { 1 }");
20421 assert_eq!(p.statements.len(), 1);
20422 }
20423
20424 #[test]
20425 fn parse_loop_with_label() {
20426 let p = parse_ok("OUTER: for my $i (1..10) { last OUTER }");
20427 assert_eq!(p.statements.len(), 1);
20428 assert_eq!(p.statements[0].label.as_deref(), Some("OUTER"));
20429 }
20430
20431 #[test]
20432 fn parse_begin_block() {
20433 let p = parse_ok("BEGIN { 1 }");
20434 assert_eq!(p.statements.len(), 1);
20435 matches!(&p.statements[0].kind, StmtKind::Begin(_));
20436 }
20437
20438 #[test]
20439 fn parse_end_block() {
20440 let p = parse_ok("END { 1 }");
20441 assert_eq!(p.statements.len(), 1);
20442 matches!(&p.statements[0].kind, StmtKind::End(_));
20443 }
20444
20445 #[test]
20446 fn parse_package_statement() {
20447 let p = parse_ok("package Foo::Bar");
20448 assert_eq!(p.statements.len(), 1);
20449 match &p.statements[0].kind {
20450 StmtKind::Package { name } => assert_eq!(name, "Foo::Bar"),
20451 _ => panic!("expected Package"),
20452 }
20453 }
20454
20455 #[test]
20456 fn parse_use_statement() {
20457 let p = parse_ok("use strict");
20458 assert_eq!(p.statements.len(), 1);
20459 }
20460
20461 #[test]
20462 fn parse_no_statement() {
20463 let p = parse_ok("no warnings");
20464 assert_eq!(p.statements.len(), 1);
20465 }
20466
20467 #[test]
20468 fn parse_require_bareword() {
20469 let p = parse_ok("require Foo::Bar");
20470 assert_eq!(p.statements.len(), 1);
20471 }
20472
20473 #[test]
20474 fn parse_require_string() {
20475 let p = parse_ok(r#"require "foo.pl""#);
20476 assert_eq!(p.statements.len(), 1);
20477 }
20478
20479 #[test]
20480 fn parse_eval_block() {
20481 let p = parse_ok("eval { 1 }");
20482 assert_eq!(p.statements.len(), 1);
20483 }
20484
20485 #[test]
20486 fn parse_eval_string() {
20487 let p = parse_ok(r#"eval "1 + 2""#);
20488 assert_eq!(p.statements.len(), 1);
20489 }
20490
20491 #[test]
20492 fn parse_qw_word_list() {
20493 let p = parse_ok("my @a = qw(foo bar baz)");
20494 assert_eq!(p.statements.len(), 1);
20495 }
20496
20497 #[test]
20498 fn parse_q_string() {
20499 let p = parse_ok("my $s = q{hello}");
20500 assert_eq!(p.statements.len(), 1);
20501 }
20502
20503 #[test]
20504 fn parse_qq_string() {
20505 let p = parse_ok(r#"my $s = qq(hello $x)"#);
20506 assert_eq!(p.statements.len(), 1);
20507 }
20508
20509 #[test]
20510 fn parse_regex_match() {
20511 let p = parse_ok(r#"$x =~ /foo/"#);
20512 assert_eq!(p.statements.len(), 1);
20513 }
20514
20515 #[test]
20516 fn parse_regex_substitution() {
20517 let p = parse_ok(r#"$x =~ s/foo/bar/g"#);
20518 assert_eq!(p.statements.len(), 1);
20519 }
20520
20521 #[test]
20522 fn parse_transliterate() {
20523 let p = parse_ok(r#"$x =~ tr/a-z/A-Z/"#);
20524 assert_eq!(p.statements.len(), 1);
20525 }
20526
20527 #[test]
20528 fn parse_ternary_operator() {
20529 let p = parse_ok("my $x = $a ? 1 : 2");
20530 assert_eq!(p.statements.len(), 1);
20531 }
20532
20533 #[test]
20534 fn parse_arrow_method_call() {
20535 let p = parse_ok("$obj->method()");
20536 assert_eq!(p.statements.len(), 1);
20537 }
20538
20539 #[test]
20540 fn parse_arrow_deref_hash() {
20541 let p = parse_ok("$r->{key}");
20542 assert_eq!(p.statements.len(), 1);
20543 }
20544
20545 #[test]
20546 fn parse_arrow_deref_array() {
20547 let p = parse_ok("$r->[0]");
20548 assert_eq!(p.statements.len(), 1);
20549 }
20550
20551 #[test]
20552 fn parse_chained_arrow_deref() {
20553 let p = parse_ok("$r->{a}[0]{b}");
20554 assert_eq!(p.statements.len(), 1);
20555 }
20556
20557 #[test]
20558 fn parse_my_multiple_vars() {
20559 let p = parse_ok("my ($a, $b, $c) = (1, 2, 3)");
20560 assert_eq!(p.statements.len(), 1);
20561 }
20562
20563 #[test]
20564 fn parse_our_scalar() {
20565 let p = parse_ok("our $VERSION = '1.0'");
20566 assert_eq!(p.statements.len(), 1);
20567 }
20568
20569 #[test]
20570 fn parse_local_scalar() {
20571 let p = parse_ok("local $/ = undef");
20572 assert_eq!(p.statements.len(), 1);
20573 }
20574
20575 #[test]
20576 fn parse_state_variable() {
20577 let p = parse_ok("fn Test::counter { state $n = 0; $n++ }");
20578 assert_eq!(p.statements.len(), 1);
20579 }
20580
20581 #[test]
20582 fn parse_postfix_if() {
20583 let p = parse_ok("print 1 if $x");
20584 assert_eq!(p.statements.len(), 1);
20585 }
20586
20587 #[test]
20588 fn parse_postfix_unless() {
20589 let p = parse_ok("die 'error' unless $ok");
20590 assert_eq!(p.statements.len(), 1);
20591 }
20592
20593 #[test]
20594 fn parse_postfix_while() {
20595 let p = parse_ok("$x++ while $x < 10");
20596 assert_eq!(p.statements.len(), 1);
20597 }
20598
20599 #[test]
20600 fn parse_postfix_for() {
20601 let p = parse_ok("print for @arr");
20602 assert_eq!(p.statements.len(), 1);
20603 }
20604
20605 #[test]
20606 fn parse_last_next_redo() {
20607 let p = parse_ok("for (@a) { next if $_ < 0; last if $_ > 10 }");
20608 assert_eq!(p.statements.len(), 1);
20609 }
20610
20611 #[test]
20612 fn parse_return_statement() {
20613 let p = parse_ok("fn foo { return 42 }");
20614 assert_eq!(p.statements.len(), 1);
20615 }
20616
20617 #[test]
20618 fn parse_wantarray() {
20619 let p = parse_ok("fn foo { wantarray ? @a : $a }");
20620 assert_eq!(p.statements.len(), 1);
20621 }
20622
20623 #[test]
20624 fn parse_caller_builtin() {
20625 let p = parse_ok("my @c = caller");
20626 assert_eq!(p.statements.len(), 1);
20627 }
20628
20629 #[test]
20630 fn parse_ref_to_array() {
20631 let p = parse_ok("my $r = \\@arr");
20632 assert_eq!(p.statements.len(), 1);
20633 }
20634
20635 #[test]
20636 fn parse_ref_to_hash() {
20637 let p = parse_ok("my $r = \\%hash");
20638 assert_eq!(p.statements.len(), 1);
20639 }
20640
20641 #[test]
20642 fn parse_ref_to_scalar() {
20643 let p = parse_ok("my $r = \\$x");
20644 assert_eq!(p.statements.len(), 1);
20645 }
20646
20647 #[test]
20648 fn parse_deref_scalar() {
20649 let p = parse_ok("my $v = $$r");
20650 assert_eq!(p.statements.len(), 1);
20651 }
20652
20653 #[test]
20654 fn parse_deref_array() {
20655 let p = parse_ok("my @a = @$r");
20656 assert_eq!(p.statements.len(), 1);
20657 }
20658
20659 #[test]
20660 fn parse_deref_hash() {
20661 let p = parse_ok("my %h = %$r");
20662 assert_eq!(p.statements.len(), 1);
20663 }
20664
20665 #[test]
20666 fn parse_blessed_ref() {
20667 let p = parse_ok("bless $r, 'Foo'");
20668 assert_eq!(p.statements.len(), 1);
20669 }
20670
20671 #[test]
20672 fn parse_heredoc_basic() {
20673 let p = parse_ok("my $s = <<END;\nfoo\nEND");
20674 assert_eq!(p.statements.len(), 1);
20675 }
20676
20677 #[test]
20678 fn parse_heredoc_quoted() {
20679 let p = parse_ok("my $s = <<'END';\nfoo\nEND");
20680 assert_eq!(p.statements.len(), 1);
20681 }
20682
20683 #[test]
20684 fn parse_do_block() {
20685 let p = parse_ok("my $x = do { 1 + 2 }");
20686 assert_eq!(p.statements.len(), 1);
20687 }
20688
20689 #[test]
20690 fn parse_do_file() {
20691 let p = parse_ok(r#"do "foo.pl""#);
20692 assert_eq!(p.statements.len(), 1);
20693 }
20694
20695 #[test]
20696 fn parse_map_expression() {
20697 let p = parse_ok("my @b = map { $_ * 2 } @a");
20698 assert_eq!(p.statements.len(), 1);
20699 }
20700
20701 #[test]
20704 fn parse_dist_thread_on_scalar_empty_list_source() {
20705 let p = parse_ok("~d> on $c () map { _ * 2 }");
20706 assert_eq!(p.statements.len(), 1);
20707 let StmtKind::Expression(root) = &p.statements[0].kind else {
20708 panic!("expected Expression statement");
20709 };
20710 let ExprKind::DistReduceExpr { cluster, list, .. } = &root.kind else {
20711 panic!("expected DistReduceExpr, got {:?}", root.kind);
20712 };
20713 assert!(
20714 matches!(cluster.kind, ExprKind::ScalarVar(ref s) if s == "c"),
20715 "expected cluster $c, got {:?}",
20716 cluster.kind
20717 );
20718 assert!(
20719 matches!(list.kind, ExprKind::List(ref v) if v.is_empty()),
20720 "expected empty list source, got {:?}",
20721 list.kind
20722 );
20723 }
20724
20725 #[test]
20726 fn parse_grep_expression() {
20727 let p = parse_ok("my @b = grep { $_ > 0 } @a");
20728 assert_eq!(p.statements.len(), 1);
20729 }
20730
20731 #[test]
20732 fn parse_sort_expression() {
20733 let p = parse_ok("my @b = sort { $a <=> $b } @a");
20734 assert_eq!(p.statements.len(), 1);
20735 }
20736
20737 #[test]
20738 fn pipe_sort_does_not_swallow_next_my_decl() {
20739 let p = parse_ok("my @s = @data |> sort\nmy $j = join(\",\", @s)");
20743 assert_eq!(
20744 p.statements.len(),
20745 2,
20746 "expected 2 stmts (sort + join decl), got {}: {:?}",
20747 p.statements.len(),
20748 p.statements
20749 .iter()
20750 .map(|s| format!("{:?}", s.kind).chars().take(60).collect::<String>())
20751 .collect::<Vec<_>>(),
20752 );
20753 }
20754
20755 #[test]
20756 fn pipe_sort_multiline_pipeline_preserves_next_decl() {
20757 let p = parse_ok(
20761 "my @bk = @{$inv->by_cat(\"bakery\")} |> maps { _->label() } |> sort\nmy $j = join(\"|\", @bk)",
20762 );
20763 assert_eq!(p.statements.len(), 2);
20764 }
20765
20766 #[test]
20767 fn parse_pipe_forward() {
20768 let p = parse_ok("@a |> map { $_ * 2 }");
20769 assert_eq!(p.statements.len(), 1);
20770 }
20771
20772 #[test]
20773 fn parse_expression_from_str_simple() {
20774 let e = parse_expression_from_str("$x + 1", "-e").unwrap();
20775 assert!(matches!(e.kind, ExprKind::BinOp { .. }));
20776 }
20777
20778 #[test]
20779 fn parse_expression_from_str_extra_tokens_error() {
20780 let err = parse_expression_from_str("$x; $y", "-e").unwrap_err();
20781 assert!(err.message.contains("Extra tokens"));
20782 }
20783
20784 #[test]
20785 fn parse_slice_indices_from_str_basic() {
20786 let indices = parse_slice_indices_from_str("0, 1, 2", "-e").unwrap();
20787 assert_eq!(indices.len(), 3);
20788 }
20789
20790 #[test]
20791 fn parse_format_value_line_empty() {
20792 let exprs = parse_format_value_line("").unwrap();
20793 assert!(exprs.is_empty());
20794 }
20795
20796 #[test]
20797 fn parse_format_value_line_single() {
20798 let exprs = parse_format_value_line("$x").unwrap();
20799 assert_eq!(exprs.len(), 1);
20800 }
20801
20802 #[test]
20803 fn parse_format_value_line_multiple() {
20804 let exprs = parse_format_value_line("$a, $b, $c").unwrap();
20805 assert_eq!(exprs.len(), 3);
20806 }
20807
20808 #[test]
20809 fn parse_unclosed_brace_error() {
20810 let err = parse_err("fn foo {");
20811 assert!(!err.is_empty());
20812 }
20813
20814 #[test]
20815 fn parse_unclosed_paren_error() {
20816 let err = parse_err("print (1, 2");
20817 assert!(!err.is_empty());
20818 }
20819
20820 #[test]
20821 fn parse_invalid_statement_error() {
20822 let err = parse_err("???");
20823 assert!(!err.is_empty());
20824 }
20825
20826 #[test]
20827 fn merge_expr_list_single() {
20828 let e = Expr {
20829 kind: ExprKind::Integer(1),
20830 line: 1,
20831 };
20832 let merged = merge_expr_list(vec![e.clone()]);
20833 matches!(merged.kind, ExprKind::Integer(1));
20834 }
20835
20836 #[test]
20837 fn merge_expr_list_multiple() {
20838 let e1 = Expr {
20839 kind: ExprKind::Integer(1),
20840 line: 1,
20841 };
20842 let e2 = Expr {
20843 kind: ExprKind::Integer(2),
20844 line: 1,
20845 };
20846 let merged = merge_expr_list(vec![e1, e2]);
20847 matches!(merged.kind, ExprKind::List(_));
20848 }
20849
20850 struct NoInteropGuard {
20859 saved: Option<bool>,
20860 }
20861 impl NoInteropGuard {
20862 fn on() -> Self {
20863 let saved = crate::no_interop_mode_tls();
20864 crate::set_no_interop_mode_tls(Some(true));
20865 Self { saved }
20866 }
20867 }
20868 impl Drop for NoInteropGuard {
20869 fn drop(&mut self) {
20870 crate::set_no_interop_mode_tls(self.saved);
20871 }
20872 }
20873
20874 #[test]
20875 fn no_interop_rejects_sub_keyword() {
20876 let _g = NoInteropGuard::on();
20877 let err = parse_err("sub foo { 1 }");
20878 assert!(
20879 err.contains("--no-interop") && err.contains("fn"),
20880 "sub rejected with fn hint: got {err:?}"
20881 );
20882 }
20883
20884 #[test]
20885 fn no_interop_rejects_say() {
20886 let _g = NoInteropGuard::on();
20887 let err = parse_err("say 1");
20888 assert!(
20889 err.contains("--no-interop") && err.contains("`p`"),
20890 "say rejected with p hint: got {err:?}"
20891 );
20892 }
20893
20894 #[test]
20895 fn no_interop_rejects_scalar_keyword() {
20896 let _g = NoInteropGuard::on();
20897 let err = parse_err("my $n = scalar @x");
20898 assert!(
20899 err.contains("--no-interop") && (err.contains("len") || err.contains("cnt")),
20900 "scalar rejected with len/cnt hint: got {err:?}"
20901 );
20902 }
20903
20904 #[test]
20905 fn no_interop_rejects_reverse() {
20906 let _g = NoInteropGuard::on();
20907 let err = parse_err("my @y = reverse @x");
20908 assert!(
20909 err.contains("--no-interop") && err.contains("rev"),
20910 "reverse rejected with rev hint: got {err:?}"
20911 );
20912 }
20913
20914 #[test]
20919 fn no_interop_accepts_stryke_idioms() {
20920 let _g = NoInteropGuard::on();
20921 parse_ok("fn foo { 1 }");
20924 parse_ok("p 1");
20925 parse_ok("my @x = (1, 2, 3); my $n = len(@x)");
20926 parse_ok("my @x = (1, 2, 3); my @y = rev(@x)");
20927 }
20928
20929 #[test]
20934 fn default_mode_still_accepts_perl5_forms() {
20935 parse_ok("sub foo { 1 }");
20937 parse_ok("say 1");
20938 }
20939
20940 #[test]
20945 fn no_interop_rejects_bare_dollar_a_dollar_b() {
20946 let _g = NoInteropGuard::on();
20947 let err = parse_err("my $x = $a + $b");
20949 assert!(
20950 err.contains("--no-interop") || err.contains("$_0") || err.contains("$_1"),
20951 "$a/$b rejected with positional hint: got {err:?}"
20952 );
20953 }
20954
20955 #[test]
20959 fn no_interop_rejects_dollar_a_inside_sort_block() {
20960 let _g = NoInteropGuard::on();
20961 let err = parse_err("my @s = sort { $a <=> $b } (3, 1, 2)");
20962 assert!(
20963 err.contains("--no-interop") || err.contains("$_0") || err.contains("$_1"),
20964 "$a in sort block rejected: got {err:?}"
20965 );
20966 }
20967
20968 #[test]
20971 fn no_interop_accepts_positional_underscore_in_sort_block() {
20972 let _g = NoInteropGuard::on();
20973 parse_ok("my @s = sort { $_0 <=> $_1 } (3, 1, 2)");
20974 }
20975
20976 #[test]
20981 fn colon_range_parses_in_for_loop() {
20982 let _g = NoInteropGuard::on();
20983 parse_ok("for my $i (1:10) { p $i }");
20984 parse_ok("my @r = 0:99");
20985 parse_ok("my @r = -5:5");
20986 }
20987
20988 #[test]
20990 fn postfix_statement_modifiers_parse() {
20991 let _g = NoInteropGuard::on();
20992 parse_ok("p _ for (1, 2, 3)");
20993 parse_ok("p 1 if 1");
20994 parse_ok("p 0 unless 0");
20995 }
20996
20997 #[test]
21000 fn pipe_forward_accepts_both_function_and_block_rhs() {
21001 let _g = NoInteropGuard::on();
21002 parse_ok("my $r = 1:10 |> sum");
21003 parse_ok("my @r = 1:10 |> maps { _ * 2 }");
21004 parse_ok("my @r = 1:10 |> grep { _ % 2 == 0 } |> maps { _ + 1 }");
21005 }
21006
21007 #[test]
21009 fn bareword_positional_underscore_n_parses_in_blocks() {
21010 let _g = NoInteropGuard::on();
21011 parse_ok("my @s = sort { _0 <=> _1 } (3, 1, 2)");
21013 parse_ok("my @r = maps { _0 * 2 } (1, 2, 3)");
21015 }
21016
21017 #[test]
21021 fn no_interop_accepts_struct_decl() {
21022 let _g = NoInteropGuard::on();
21023 parse_ok("struct Point { x => Int, y => Int }");
21024 }
21025
21026 #[test]
21027 fn no_interop_accepts_enum_decl() {
21028 let _g = NoInteropGuard::on();
21029 parse_ok("enum Color { Red, Green, Blue }");
21030 parse_ok("enum Maybe { Just => Int, Nothing }");
21034 }
21035
21036 #[test]
21037 fn no_interop_accepts_class_decl_with_methods() {
21038 let _g = NoInteropGuard::on();
21039 parse_ok(
21040 "class Rect {\n width: Float\n height: Float\n\n fn area { $self->width * $self->height }\n}",
21041 );
21042 }
21043
21044 #[test]
21045 fn no_interop_accepts_trait_decl() {
21046 let _g = NoInteropGuard::on();
21047 parse_ok("trait Greeter { fn greet; fn loudly { p \"GREET\" } }");
21048 }
21049
21050 #[test]
21055 fn defined_or_assign_compound_operators_parse() {
21056 let _g = NoInteropGuard::on();
21057 parse_ok("my $h = {}; $h->{x} ||= []");
21058 parse_ok("my $v; $v //= 0");
21059 }
21060
21061 #[test]
21064 fn explicit_hashref_literal_parses() {
21065 let _g = NoInteropGuard::on();
21066 parse_ok("my $row = +{ region => \"north\", qty => 10 }");
21067 parse_ok("my @rows = (+{ a => 1 }, +{ a => 2 })");
21068 }
21069
21070 #[test]
21073 fn eval_block_and_dollar_at_parse() {
21074 let _g = NoInteropGuard::on();
21075 parse_ok("my $r = eval { 1 + 2 }; p $@ if $@");
21076 parse_ok("eval { die \"boom\" }; p $@");
21077 }
21078
21079 #[test]
21082 fn try_catch_parses() {
21083 let _g = NoInteropGuard::on();
21084 parse_ok("try { die \"boom\" } catch ($e) { p $e }");
21085 }
21086
21087 #[test]
21090 fn file_test_operators_parse() {
21091 let _g = NoInteropGuard::on();
21092 parse_ok("p 1 if -d \"/tmp\"");
21093 parse_ok("p 2 if -f \"/etc/hosts\"");
21094 parse_ok("p 3 if -e $0");
21095 parse_ok("my $sz = -s \"/etc/hosts\"");
21096 }
21097
21098 #[test]
21102 fn thread_macros_parse() {
21103 let _g = NoInteropGuard::on();
21104 parse_ok("my $r = ~> 5 +1 *2");
21105 parse_ok("my @r = ~> (1,2,3) maps { _ * 2 }");
21106 }
21107
21108 #[test]
21112 fn hash_destructure_sub_signature_parses() {
21113 let _g = NoInteropGuard::on();
21114 parse_ok("fn handle({ name => $name, qty => $qty }) { p \"$name x $qty\" }");
21115 }
21116
21117 #[test]
21120 fn anonymous_fn_parses() {
21121 let _g = NoInteropGuard::on();
21122 parse_ok("my $f = fn { _0 * 2 }");
21123 parse_ok("my @doubled = maps { _0 * 2 } (1, 2, 3)");
21124 }
21125
21126 #[test]
21128 fn ternary_and_chained_ternary_parse() {
21129 let _g = NoInteropGuard::on();
21130 parse_ok("my $r = $x > 0 ? \"pos\" : \"neg\"");
21131 parse_ok("my $r = $x > 0 ? \"pos\" : $x < 0 ? \"neg\" : \"zero\"");
21132 }
21133
21134 #[test]
21137 fn array_slice_with_negative_indices_parses() {
21138 let _g = NoInteropGuard::on();
21139 parse_ok("my @arr = (1,2,3,4,5); my @tail = @arr[-3:-1]");
21140 parse_ok("my @arr = (1,2,3); my @last_two = @arr[-2:]");
21141 }
21142
21143 #[test]
21146 fn state_variable_declaration_parses() {
21147 let _g = NoInteropGuard::on();
21148 parse_ok("fn my_counter { state $n = 0; $n++; $n }");
21151 parse_ok("fn my_memo($k) { state %cache; $cache{$k} //= compute($k) }");
21152 }
21153
21154 #[test]
21157 fn our_declaration_parses() {
21158 let _g = NoInteropGuard::on();
21159 parse_ok("our $VERSION = 1.0");
21160 parse_ok("our @EXPORT = (1, 2, 3)");
21161 }
21162
21163 #[test]
21166 fn regex_binding_operators_parse() {
21167 let _g = NoInteropGuard::on();
21168 parse_ok("p 1 if $s =~ /^\\d+$/");
21169 parse_ok("p 0 unless $s !~ /[A-Z]/");
21170 parse_ok("my @m = $s =~ /(\\w+)/g");
21171 }
21172
21173 #[test]
21177 fn nested_data_structure_literals_parse() {
21178 let _g = NoInteropGuard::on();
21179 parse_ok("my %h = (a => [1, 2, 3], b => [4, 5])");
21180 parse_ok("my @rows = (+{ x => 1 }, +{ x => 2 })");
21181 parse_ok("my %grid = (cells => [+{ row => 1 }, +{ row => 2 }])");
21182 }
21183
21184 #[test]
21187 fn anonymous_fn_with_explicit_params_parses() {
21188 let _g = NoInteropGuard::on();
21189 parse_ok("my $add = fn ($x, $y) { $x + $y }");
21190 parse_ok("my $h = fn ($v, %opts) { p $v; p %opts }");
21191 }
21192
21193 #[test]
21196 fn package_declaration_parses() {
21197 let _g = NoInteropGuard::on();
21198 parse_ok("package Foo; my $x = 1");
21199 parse_ok("package Foo::Bar::Baz; our $VERSION = 0.01");
21200 }
21201
21202 #[test]
21206 fn loop_control_keywords_parse() {
21207 let _g = NoInteropGuard::on();
21208 parse_ok("for my $i (1:10) { next if $i % 2; p $i }");
21209 parse_ok("while (1) { last if $done }");
21210 parse_ok("my $rerun = 0; for (1:5) { if ($rerun) { redo } }");
21211 }
21212
21213 #[test]
21216 fn labelled_loops_parse() {
21217 let _g = NoInteropGuard::on();
21218 parse_ok("OUTER: for my $i (1:10) { last OUTER if $i > 5 }");
21219 parse_ok("OUTER: for my $i (1:3) { INNER: for my $j (1:3) { next OUTER if $j > $i } }");
21220 }
21221
21222 #[test]
21226 fn string_repeat_x_operator_parses() {
21227 let _g = NoInteropGuard::on();
21228 parse_ok("my $sep = \"-\" x 40");
21229 parse_ok("my @zeros = (0) x 100");
21230 parse_ok("my $bar = \"#\" x $count");
21231 }
21232
21233 #[test]
21236 fn chomp_chop_parse() {
21237 let _g = NoInteropGuard::on();
21238 parse_ok("chomp(my $line = <STDIN>)");
21239 parse_ok("my $s = \"hi\\n\"; chomp $s");
21240 parse_ok("my $t = \"hi\"; chop $t");
21241 }
21242
21243 #[test]
21246 fn substitution_operator_parses() {
21247 let _g = NoInteropGuard::on();
21248 parse_ok("my $s = \"abc\"; $s =~ s/b/X/");
21249 parse_ok("my $s = \"AaBb\"; $s =~ s/[a-z]//g");
21250 parse_ok("my $s = \"Hello\"; $s =~ s/(.)/\\1\\1/g");
21251 }
21252
21253 #[test]
21256 fn sprintf_parses_with_format_specs() {
21257 let _g = NoInteropGuard::on();
21258 parse_ok("my $row = sprintf(\"%-10s %5d\", \"foo\", 42)");
21259 parse_ok("p sprintf(\"%.3f ms\", 1.234)");
21260 parse_ok("p sprintf(\"%04x\", 255)");
21261 }
21262
21263 #[test]
21266 fn diamond_stdin_reads_parse() {
21267 let _g = NoInteropGuard::on();
21268 parse_ok("my $line = <STDIN>");
21269 parse_ok("my @lines = <STDIN>");
21270 parse_ok("while (my $line = <STDIN>) { p $line }");
21271 }
21272
21273 #[test]
21276 fn chained_method_calls_parse() {
21277 let _g = NoInteropGuard::on();
21278 parse_ok("$obj->foo->bar");
21279 parse_ok("my $r = $row->{region}");
21280 parse_ok("$obj->set(1)->get");
21281 }
21282
21283 #[test]
21286 fn hash_deref_forms_parse() {
21287 let _g = NoInteropGuard::on();
21288 parse_ok("my $h = +{a=>1}; my %copy = %$h");
21289 parse_ok("my $h = +{a=>1}; my @k = keys %$h");
21290 parse_ok("my $h = +{a=>1}; p $h->{a}");
21291 parse_ok("my $h = +{a=>1, b=>2}; p len(keys %{$h})");
21292 }
21293
21294 #[test]
21297 fn die_unless_assertion_parses() {
21298 let _g = NoInteropGuard::on();
21299 parse_ok("die \"x must be 1\" unless 1 == 1");
21300 parse_ok("die \"empty list\" if len(@x) == 0");
21301 parse_ok("my $x = 1; die \"hi\" unless $x");
21302 }
21303
21304 #[test]
21307 fn qw_literal_parses() {
21308 let _g = NoInteropGuard::on();
21309 parse_ok("my @w = qw(red green blue)");
21310 parse_ok("for my $name (qw(Alice Bob Carol)) { p $name }");
21311 }
21312
21313 #[test]
21316 fn array_slice_with_explicit_indices_parses() {
21317 let _g = NoInteropGuard::on();
21318 parse_ok("my @a = (10, 20, 30, 40); my @s = @a[0, 2]");
21319 parse_ok("my @a = (10, 20, 30); my @s = @a[2, 0, 1]");
21320 }
21321
21322 #[test]
21325 fn hash_slice_with_keys_parses() {
21326 let _g = NoInteropGuard::on();
21327 parse_ok("my %h = (a=>1, b=>2, c=>3); my @v = @h{'a','c'}");
21328 }
21329
21330 #[test]
21334 fn bitwise_operators_parse() {
21335 let _g = NoInteropGuard::on();
21336 parse_ok("my $x = 0xAA; my $y = $x & 0x0F");
21337 parse_ok("my $x = 1; my $y = $x | 2 | 4");
21338 parse_ok("my $x = 0xFF; my $y = $x ^ 0x80");
21339 parse_ok("my $x = 0xAA; my $y = ~$x & 0xff");
21340 parse_ok("my $x = 1; my $y = $x << 4");
21341 parse_ok("my $x = 0xF0; my $y = $x >> 2");
21342 }
21343
21344 #[test]
21347 fn numeric_literal_prefixes_parse() {
21348 let _g = NoInteropGuard::on();
21349 parse_ok("my $hex = 0xCAFEBABE");
21350 parse_ok("my $bin = 0b1101");
21351 parse_ok("my $hex8 = 0xff & 0x0f");
21352 }
21353
21354 #[test]
21357 fn negative_array_index_parses() {
21358 let _g = NoInteropGuard::on();
21359 parse_ok("my @arr = (1, 2, 3); p $arr[-1]");
21362 parse_ok("my @stack = (10, 20, 30); p $stack[-1] if len(@stack) > 0");
21363 }
21364
21365 #[test]
21368 fn loop_control_standalone_parses() {
21369 let _g = NoInteropGuard::on();
21370 parse_ok("while (1) { last }");
21371 parse_ok("for my $i (1:10) { next if $i == 3 }");
21372 parse_ok("fn ret { return 42 }");
21373 }
21374
21375 #[test]
21379 fn shift_pop_on_array_parses() {
21380 let _g = NoInteropGuard::on();
21381 parse_ok("my @a = (1, 2, 3); my $first = shift @a");
21382 parse_ok("my @a = (1, 2, 3); my $last = pop @a");
21383 parse_ok("fn drop_first(@xs) { shift @xs; @xs }");
21384 }
21385
21386 #[test]
21389 fn special_internal_names_parse() {
21390 let _g = NoInteropGuard::on();
21391 parse_ok("p __FILE__");
21392 parse_ok("p __LINE__");
21393 parse_ok("p __PACKAGE__");
21394 parse_ok("p __FILE__ . \":\" . __LINE__");
21395 }
21396
21397 #[test]
21400 fn deeply_nested_ternary_parses() {
21401 let _g = NoInteropGuard::on();
21402 parse_ok("my $g = ($p eq \"a\") ? 1 : ($p eq \"b\") ? 2 : ($p eq \"c\") ? 3 : 4");
21403 }
21404
21405 #[test]
21409 fn array_of_hashref_chained_subscript_parses() {
21410 let _g = NoInteropGuard::on();
21411 parse_ok("my @rows = (+{name=>'a',sc=>1}); p $rows[0]->{name}");
21412 parse_ok("my @rows = (+{n=>10},+{n=>20}); p $rows[-1]->{n}");
21413 }
21414
21415 #[test]
21418 fn nested_for_loops_parse() {
21419 let _g = NoInteropGuard::on();
21420 parse_ok("for my $r (0:5) { for my $c (0:5) { p \"$r,$c\" } }");
21421 }
21422
21423 #[test]
21427 fn dot_assign_string_append_parses() {
21428 let _g = NoInteropGuard::on();
21429 parse_ok("my $s = \"a\"; $s .= \"b\"");
21430 parse_ok("my $out = \"\"; $out .= \"x\" for (1:3)");
21431 }
21432
21433 #[test]
21436 fn unshift_parses() {
21437 let _g = NoInteropGuard::on();
21438 parse_ok("my @path = (3, 4); unshift @path, 2; unshift @path, 1");
21439 parse_ok("my @q; unshift @q, $_ for (1:5)");
21440 }
21441
21442 #[test]
21445 fn defined_or_fallback_parses() {
21446 let _g = NoInteropGuard::on();
21447 parse_ok("my $x; my $y = $x // 0");
21448 parse_ok("my %h = (a=>1); my $v = $h{missing} // -1");
21449 parse_ok("p 1 if defined $foo");
21450 }
21451
21452 #[test]
21455 fn rev_over_range_parses() {
21456 let _g = NoInteropGuard::on();
21457 parse_ok("for my $i (rev 0:9) { p $i }");
21458 parse_ok("my @r = rev (1, 2, 3, 4)");
21459 }
21460
21461 #[test]
21464 fn array_deref_forms_parse() {
21465 let _g = NoInteropGuard::on();
21466 parse_ok("my $r = [1, 2, 3]; my @copy = @$r");
21467 parse_ok("my $r = [1, 2, 3]; my @copy = @{$r}");
21468 parse_ok("my @rs = ([1], [2, 3]); my @flat; push @flat, @$_ for @rs");
21469 }
21470
21471 #[test]
21474 fn hashref_subscript_alt_forms_parse() {
21475 let _g = NoInteropGuard::on();
21476 parse_ok("my $h = +{a=>1}; p $h->{a}");
21477 parse_ok("my $h = +{a=>1}; p ${$h}{a}");
21478 }
21479
21480 #[test]
21483 fn numeric_builtins_parse() {
21484 let _g = NoInteropGuard::on();
21485 parse_ok("my $x = abs(-5)");
21486 parse_ok("my $f = int(3.7)");
21487 parse_ok("my $s = sqrt(2)");
21488 parse_ok("my $n = int($x * 100 + 0.5) / 100");
21489 }
21490
21491 #[test]
21495 fn local_declaration_parses() {
21496 let _g = NoInteropGuard::on();
21497 parse_ok("our $g = 1; (fn { local $g = 99; p $g })->()");
21499 }
21500
21501 #[test]
21505 fn srand_rand_parse() {
21506 let _g = NoInteropGuard::on();
21507 parse_ok("srand(42); my $r = rand(6)");
21508 parse_ok("srand(); my $r = int(rand(100))");
21509 }
21510
21511 #[test]
21514 fn substr_parses() {
21515 let _g = NoInteropGuard::on();
21516 parse_ok("my $s = \"hello\"; p substr($s, 0, 1)");
21517 parse_ok("my $s = \"hello\"; p substr($s, 1)");
21518 parse_ok("my $s = \"hello\"; p substr($s, -2)");
21519 }
21520
21521 #[test]
21524 fn underscore_separators_in_numbers_parse() {
21525 let _g = NoInteropGuard::on();
21526 parse_ok("my $n = 1_000_000");
21527 parse_ok("my $r = 1_000_000 / 365");
21528 parse_ok("my $hex = 0xff_ff");
21529 }
21530
21531 #[test]
21534 fn arrayref_of_arrayref_access_parses() {
21535 let _g = NoInteropGuard::on();
21536 parse_ok("my $grid = [[1, 2], [3, 4]]");
21537 parse_ok("my $grid = [[1, 2], [3, 4]]; p $grid->[0]->[1]");
21538 parse_ok("my $grid = [[1, 2], [3, 4]]; p $grid->[1][0]");
21539 }
21540
21541 #[test]
21544 fn arrow_chain_assignment_parses() {
21545 let _g = NoInteropGuard::on();
21546 parse_ok("my $g = [[0, 0]]; $g->[0]->[1] = 99");
21547 parse_ok("my $g = [[0, 0]]; $g->[0][1] = 99");
21548 }
21549
21550 #[test]
21553 fn tuple_destructure_from_arrayref_parses() {
21554 let _g = NoInteropGuard::on();
21555 parse_ok("my $e = [10, 20]; my ($lhs, $rhs) = @$e");
21556 parse_ok("my $e = [\"x\", 1]; my ($name, $weight) = ($e->[0], $e->[1])");
21557 }
21558
21559 #[test]
21562 fn next_last_unless_postfix_parses() {
21563 let _g = NoInteropGuard::on();
21564 parse_ok("for my $i (1:10) { next unless $i % 2 == 0; p $i }");
21565 parse_ok("while (1) { last unless $live }");
21566 }
21567
21568 #[test]
21571 fn keys_on_braced_hash_deref_parses() {
21572 let _g = NoInteropGuard::on();
21573 parse_ok("my $h = +{a=>1}; my @k = keys %{$h}");
21574 parse_ok("my $h = +{a=>+{b=>1}}; my @k = keys %{$h->{a}}");
21575 }
21576
21577 #[test]
21581 fn namespaced_fn_decl_parses() {
21582 let _g = NoInteropGuard::on();
21583 parse_ok("fn Module::method($x) { $x * 2 }");
21584 parse_ok("fn Foo::Bar::helper { 42 }");
21585 parse_ok("fn Demo::run { p \"running\" }");
21586 }
21587
21588 #[test]
21591 fn namespaced_fn_call_parses() {
21592 let _g = NoInteropGuard::on();
21593 parse_ok("fn Module::add($x, $y) { $x + $y } p Module::add(2, 3)");
21594 parse_ok("fn Foo::Bar::baz { 1 } fn Demo::main { Foo::Bar::baz() + Foo::Bar::baz() }");
21597 }
21598
21599 #[test]
21602 fn index_builtin_parses() {
21603 let _g = NoInteropGuard::on();
21604 parse_ok("my $i = index(\"hello world\", \"world\")");
21605 parse_ok("my $i = index($s, $pat, 0)");
21606 }
21607
21608 #[test]
21612 fn rev_on_array_literal_parses() {
21613 let _g = NoInteropGuard::on();
21614 parse_ok("my @r = rev (1, 2, 3, 4)");
21615 parse_ok("for my $x (rev (\"a\", \"b\", \"c\")) { p $x }");
21616 }
21617
21618 #[test]
21623 fn numeric_and_string_comparison_in_one_expr_parses() {
21624 let _g = NoInteropGuard::on();
21625 parse_ok("my $r = $_0 <=> $_1 || $name cmp $other");
21626 parse_ok("p 1 if $x == $y && $name eq \"foo\"");
21627 }
21628
21629 #[test]
21635 fn bareword_positional_in_sort_reduce_blocks_parses() {
21636 let _g = NoInteropGuard::on();
21637 parse_ok("my @s = sort { _0 <=> _1 } (3, 1, 2)");
21639 parse_ok("my $r = (1, 2, 3) |> reduce { _0 + _1 }");
21641 parse_ok("my $s = (\"a\", \"b\") |> reduce { _0 . _1 }");
21643 }
21644
21645 #[test]
21648 fn bareword_topic_in_maps_grep_parses() {
21649 let _g = NoInteropGuard::on();
21650 parse_ok("my @r = (1, 2, 3) |> maps { _ * 2 }");
21651 parse_ok("my @r = (1, 2, 3, 4) |> grep { _ % 2 == 0 }");
21652 parse_ok("my @r = 1:100 |> pmap { _ ** 3 }");
21653 parse_ok("my @r = 1:100 |> pgrep { _ % 7 == 0 }");
21654 }
21655
21656 #[test]
21660 fn bareword_vs_sigil_in_string_interp_parses() {
21661 let _g = NoInteropGuard::on();
21662 parse_ok("my @r = (5, 10) |> maps { _ + 1 }");
21664 parse_ok("p \"first=$_0 second=$_1\"");
21668 parse_ok("p \"got: $_\"");
21669 }
21670
21671 #[test]
21674 fn bareword_topic_in_postfix_for_parses() {
21675 let _g = NoInteropGuard::on();
21676 parse_ok("my $n = 0; $n += _ for (1, 2, 3, 4)");
21677 parse_ok("my %h; $h{_}++ for (\"a\", \"b\", \"a\")");
21678 }
21679
21680 #[test]
21683 fn bareword_topic_as_hash_key_parses() {
21684 let _g = NoInteropGuard::on();
21685 parse_ok("my %h; $h{_}++ for (\"a\", \"b\", \"a\")");
21686 parse_ok("my @arr = (1, 2, 3); my %seen; $seen{$arr[_]}++ for (0, 1, 2)");
21687 }
21688
21689 #[test]
21692 fn bareword_topic_inside_subscript_chain_parses() {
21693 let _g = NoInteropGuard::on();
21694 parse_ok(
21695 "my @c = (\"a\", \"b\", \"c\"); my %seen = (a => 1, b => 2, c => 1); \
21696 my @hits = grep { $seen{$c[_]} == 1 } (0, 1, 2)",
21697 );
21698 }
21699
21700 #[test]
21703 fn ref_builtin_parses() {
21704 let _g = NoInteropGuard::on();
21705 parse_ok("my $x = [1, 2]; p ref($x)");
21706 parse_ok("my $r = +{a => 1}; p 1 if ref($r) eq \"HASH\"");
21707 }
21708
21709 #[test]
21713 fn expression_bodied_recursive_fn_parses() {
21714 let _g = NoInteropGuard::on();
21715 parse_ok("fn N::gcd = _1 == 0 ? _0 : N::gcd(_1, _0 % _1)");
21716 parse_ok("fn N::lcm = _0 * _1 / N::gcd(_0, _1)");
21717 }
21718
21719 #[test]
21722 fn expression_bodied_pipe_reduce_parses() {
21723 let _g = NoInteropGuard::on();
21724 parse_ok(
21725 "fn N::gcd = _1 == 0 ? _0 : N::gcd(_1, _0 % _1); \
21726 fn N::gcd_list = @{_} |> reduce { N::gcd(_0, _1) }",
21727 );
21728 }
21729
21730 #[test]
21733 fn reduce_fold_with_hashref_accumulator_parses() {
21734 let _g = NoInteropGuard::on();
21735 parse_ok(
21736 "my @xs = (3, 2, 3); \
21737 my $st = (+{cur => 0, best => -100}, @xs) |> reduce { \
21738 +{ cur => _1, best => _0->{best} } \
21739 }",
21740 );
21741 }
21742
21743 #[test]
21746 fn array_deref_slice_with_variable_bounds_parses() {
21747 let _g = NoInteropGuard::on();
21748 parse_ok(
21749 "my @a = (10, 20, 30, 40, 50); my $r = \\@a; \
21750 my $lo = 1; my $hi = 3; \
21751 my @s = @$r[$lo:$hi]",
21752 );
21753 }
21754
21755 #[test]
21758 fn min_max_over_array_deref_slice_parses() {
21759 let _g = NoInteropGuard::on();
21760 parse_ok(
21761 "my @a = (1, 3, 2, 5); my $r = \\@a; \
21762 my $lo = 0; my $hi = 2; \
21763 my $m1 = min(@$r[$lo:$hi]); \
21764 my $m2 = max(@$r[$lo:$hi])",
21765 );
21766 }
21767
21768 #[test]
21772 fn flat_maps_with_recursive_call_parses() {
21773 let _g = NoInteropGuard::on();
21774 parse_ok(
21775 "fn Flat::flatten($r) = @$r |> flat_maps { \
21776 ref(_) eq \"ARRAY\" ? Flat::flatten(_) : (_) \
21777 }",
21778 );
21779 }
21780
21781 #[test]
21784 fn nested_map_with_outer_lexical_capture_parses() {
21785 let _g = NoInteropGuard::on();
21786 parse_ok(
21787 "my @lists = ([1,2,3], [10,20,30]); \
21788 my @r = 0:2 |> maps { my $i = _; [map { _->[$i] } @lists] }",
21789 );
21790 }
21791
21792 #[test]
21795 fn array_deref_of_bareword_topic_parses() {
21796 let _g = NoInteropGuard::on();
21797 parse_ok("my @lists = ([1,2,3], [10,20,30]); p min(map { len(@{_}) } @lists)");
21798 }
21799
21800 #[test]
21805 fn thread_macro_accepts_glob_rand_srand_stages() {
21806 parse_ok("my @r = ~> \"/tmp/*\" glob sort");
21807 parse_ok("my $i = ~> 100 rand int");
21808 parse_ok("~> 42 srand");
21809 }
21810
21811 #[test]
21815 fn recursive_fn_with_arrayref_assignment_parses() {
21816 let _g = NoInteropGuard::on();
21817 parse_ok(
21818 "fn UF::find($uf, $x) { \
21819 return $x if $uf->{parent}[$x] == $x; \
21820 $uf->{parent}[$x] = UF::find($uf, $uf->{parent}[$x]); \
21821 $uf->{parent}[$x] \
21822 }",
21823 );
21824 }
21825
21826 #[test]
21829 fn hashref_init_with_range_and_repeat_parses() {
21830 let _g = NoInteropGuard::on();
21831 parse_ok("fn UF::new($n) = +{ parent => [0:$n - 1], rank => [(0) x $n], count => $n }");
21832 }
21833
21834 #[test]
21837 fn postfix_for_arrayref_deref_parses() {
21838 let _g = NoInteropGuard::on();
21839 parse_ok("my @words = (\"a\", \"b\"); my $r = \\@words; my @out; push @out, $_ for @$r");
21840 }
21841
21842 #[test]
21845 fn tuple_swap_destructure_parses() {
21846 let _g = NoInteropGuard::on();
21847 parse_ok("my $ra = 1; my $rb = 2; ($ra, $rb) = ($rb, $ra)");
21848 }
21849
21850 #[test]
21855 fn explicit_2d_array_row_init_parses() {
21856 let _g = NoInteropGuard::on();
21857 parse_ok(
21858 "my @d; my $m = 3; my $n = 4; \
21859 for my $i (0:$m) { $d[$i] = [(0) x ($n + 1)] }",
21860 );
21861 }
21862
21863 #[test]
21866 fn min_with_three_args_parses() {
21867 let _g = NoInteropGuard::on();
21868 parse_ok("my $x = min(1, 2, 3)");
21869 parse_ok("my @d; $d[0][0] = 5; my $r = min($d[0][0] + 1, $d[0][0] + 1, $d[0][0] + 0)");
21870 }
21871
21872 #[test]
21875 fn string_slice_with_len_bound_parses() {
21876 let _g = NoInteropGuard::on();
21877 parse_ok(
21878 "my $w = \"apple\"; my $pre = \"app\"; \
21879 my $ok = len($w) >= len($pre) && $w[0:len($pre) - 1] eq $pre",
21880 );
21881 }
21882
21883 #[test]
21886 fn sort_block_with_arrow_deref_topic_parses() {
21887 let _g = NoInteropGuard::on();
21888 parse_ok(
21889 "my @edges = ([0,1,4], [2,3,1], [1,2,2]); \
21890 my @sorted = sort { _0->[2] <=> _1->[2] } @edges",
21891 );
21892 }
21893
21894 #[test]
21897 fn cstyle_for_with_postdecrement_parses() {
21898 let _g = NoInteropGuard::on();
21899 parse_ok(
21900 "my @arr = (1, 2, 3, 4, 5); \
21901 my $n = len(@arr); \
21902 for (my $i = $n - 1; $i > 0; $i--) { p $arr[$i] }",
21903 );
21904 }
21905
21906 #[test]
21909 fn tuple_swap_arrayref_index_parses() {
21910 let _g = NoInteropGuard::on();
21911 parse_ok(
21912 "my @arr = (1, 2, 3); my $r = \\@arr; my $i = 0; my $j = 2; \
21913 ($r->[$i], $r->[$j]) = ($r->[$j], $r->[$i])",
21914 );
21915 }
21916
21917 #[test]
21920 fn recursive_backtracking_arrayref_mutation_parses() {
21921 let _g = NoInteropGuard::on();
21922 parse_ok(
21923 "fn Q::go($n, $cols, $count_ref) { \
21924 my $r = len(@$cols); \
21925 if ($r == $n) { $$count_ref++; return } \
21926 for my $c (0:$n - 1) { \
21927 push @$cols, $c; \
21928 Q::go($n, $cols, $count_ref); \
21929 pop @$cols \
21930 } \
21931 }",
21932 );
21933 }
21934
21935 #[test]
21938 fn nested_hashref_chain_parses() {
21939 let _g = NoInteropGuard::on();
21940 parse_ok(
21941 "my $c = +{ nodes => +{ a => +{ val => 1, prev => undef, next => \"b\" } } }; \
21942 my $p = $c->{nodes}{a}{prev}; \
21943 my $n = $c->{nodes}{a}{next}",
21944 );
21945 }
21946
21947 #[test]
21950 fn scalar_ref_postincrement_parses() {
21951 let _g = NoInteropGuard::on();
21952 parse_ok("my $n = 0; my $ref = \\$n; $$ref++; p $n");
21953 }
21954
21955 #[test]
21958 fn hash_of_hash_autoviv_increment_parses() {
21959 let _g = NoInteropGuard::on();
21960 parse_ok(
21961 "my %table; \
21962 my $prev = \"the\"; my $next = \"quick\"; \
21963 $table{$prev} //= +{}; \
21964 $table{$prev}{$next}++",
21965 );
21966 }
21967
21968 #[test]
21971 fn cstyle_for_with_literal_bounds_parses() {
21972 let _g = NoInteropGuard::on();
21973 parse_ok("my $sum = 0; for (my $i = 0; $i < 10; $i++) { $sum += $i }");
21974 }
21975
21976 #[test]
21982 fn recursive_expression_body_with_ternary_parses() {
21983 let _g = NoInteropGuard::on();
21984 parse_ok("fn J::s($n, $k) = $n == 1 ? 0 : (J::s($n - 1, $k) + $k) % $n");
21985 }
21986
21987 #[test]
21991 fn namespaced_quote_like_tail_segments_parse() {
21992 let _g = NoInteropGuard::on();
21993 parse_ok("fn Foo::s($x) = $x + 1");
21994 parse_ok("fn Foo::m($x) = $x * 2");
21995 parse_ok("fn Foo::q($x) = $x");
21996 parse_ok("fn Foo::qq($x) = $x");
21997 parse_ok("fn Foo::qx($x) = $x");
21998 parse_ok("fn Foo::qr($x) = $x");
21999 parse_ok("fn Foo::tr($x) = $x");
22000 parse_ok("fn Foo::y($x) = $x");
22001 }
22002
22003 #[test]
22006 fn splice_single_remove_parses() {
22007 let _g = NoInteropGuard::on();
22008 parse_ok("my @circle = 0:5; splice @circle, 2, 1");
22009 }
22010
22011 #[test]
22013 fn atan2_call_parses() {
22014 let _g = NoInteropGuard::on();
22015 parse_ok("fn MC::true_pi = atan2(0, -1)");
22016 parse_ok("my $pi = atan2(0, -1)");
22017 }
22018
22019 #[test]
22022 fn flat_2d_array_indexing_parses() {
22023 let _g = NoInteropGuard::on();
22024 parse_ok(
22025 "my @board = (0) x 81; \
22026 my $r = 3; my $c = 5; \
22027 $board[$r * 9 + $c] = 7; \
22028 my $v = $board[$r * 9 + $c]",
22029 );
22030 }
22031
22032 #[test]
22038 fn par_top_level_prefix_form_parses() {
22039 let _g = NoInteropGuard::on();
22040 parse_ok("my @r = par { _ * 2 } (1, 2, 3, 4)");
22041 parse_ok("par { p _ } @big");
22042 }
22043
22044 #[test]
22048 fn dp_max_step_chained_subscript_parses() {
22049 let _g = NoInteropGuard::on();
22050 parse_ok(
22051 "my @d; for my $i (0:3) { $d[$i] = [(0) x 4] } \
22052 $d[1][1] = max($d[0][1], $d[1][0]); \
22053 my $r = $d[1][1]",
22054 );
22055 }
22056
22057 #[test]
22059 fn rolling_hash_arithmetic_parses() {
22060 let _g = NoInteropGuard::on();
22061 parse_ok(
22062 "my $h = 0; my $base = 257; my $mod = 1000000007; my $high = 256; \
22063 my $drop = 65; my $add = 90; \
22064 $h = (($h - $drop * $high) * $base + $add) % $mod; \
22065 $h = ($h + $mod) % $mod",
22066 );
22067 }
22068
22069 #[test]
22072 fn triple_nested_2d_via_k_parses() {
22073 let _g = NoInteropGuard::on();
22074 parse_ok(
22075 "my @d; for my $i (0:3) { $d[$i] = [(0) x 4] } \
22076 for my $k (0:3) { for my $i (0:3) { for my $j (0:3) { \
22077 $d[$i][$j] = $d[$i][$k] + $d[$k][$j] \
22078 if $d[$i][$k] + $d[$k][$j] < $d[$i][$j] \
22079 } } }",
22080 );
22081 }
22082
22083 #[test]
22086 fn dp_array_repeat_init_parses() {
22087 let _g = NoInteropGuard::on();
22088 parse_ok("my $amount = 11; my $INF = 1e18; my @dp = ($INF) x ($amount + 1); $dp[0] = 0");
22089 }
22090
22091 #[test]
22093 fn rev_split_join_chain_parses() {
22094 let _g = NoInteropGuard::on();
22095 parse_ok("my $s = \"abc\"; my $r = join(\"\", rev split //, $s)");
22096 }
22097
22098 #[test]
22101 fn cstyle_for_decrement_with_arrayref_swap_parses() {
22102 let _g = NoInteropGuard::on();
22103 parse_ok(
22104 "my @arr = (1, 2, 3); my $r = \\@arr; \
22105 for (my $end = len(@arr) - 1; $end > 0; $end--) { \
22106 ($r->[0], $r->[$end]) = ($r->[$end], $r->[0]) \
22107 }",
22108 );
22109 }
22110
22111 #[test]
22114 fn shift_in_while_loop_parses() {
22115 let _g = NoInteropGuard::on();
22116 parse_ok(
22117 "my @q = (0, 1, 2); my @out; \
22118 while (len(@q) > 0) { my $u = shift @q; push @out, $u }",
22119 );
22120 }
22121
22122 #[test]
22125 fn mod_pow_squaring_loop_parses() {
22126 let _g = NoInteropGuard::on();
22127 parse_ok(
22128 "fn MR::mod_pow($base, $exp, $m) { \
22129 my $result = 1; my $bs = $base % $m; my $e = $exp; \
22130 while ($e > 0) { \
22131 $result = ($result * $bs) % $m if $e % 2 == 1; \
22132 $e = int($e / 2); \
22133 $bs = ($bs * $bs) % $m \
22134 } \
22135 $result \
22136 }",
22137 );
22138 }
22139
22140 #[test]
22143 fn dp_traceback_walk_parses() {
22144 let _g = NoInteropGuard::on();
22145 parse_ok(
22146 "my @xs = (1, 2, 3); my $n = 3; my $target = 4; \
22147 my @dp; for my $i (0:$n) { $dp[$i] = [(0) x ($target + 1)] } \
22148 my @out; my $j = $target; \
22149 for (my $i = $n; $i > 0; $i--) { \
22150 my $v = $xs[$i - 1]; \
22151 if ($j >= $v && $dp[$i - 1][$j - $v] == 1) { \
22152 unshift @out, $v; $j -= $v \
22153 } \
22154 }",
22155 );
22156 }
22157
22158 #[test]
22161 fn binary_search_in_for_loop_parses() {
22162 let _g = NoInteropGuard::on();
22163 parse_ok(
22164 "my @xs = (3, 1, 4, 1, 5); my @tails; \
22165 for my $x (@xs) { \
22166 my $lo = 0; my $hi = len @tails; \
22167 while ($lo < $hi) { \
22168 my $mid = int(($lo + $hi) / 2); \
22169 if ($tails[$mid] < $x) { $lo = $mid + 1 } else { $hi = $mid } \
22170 } \
22171 if ($lo == len @tails) { push @tails, $x } else { $tails[$lo] = $x } \
22172 }",
22173 );
22174 }
22175
22176 #[test]
22179 fn edge_relaxation_destructure_parses() {
22180 let _g = NoInteropGuard::on();
22181 parse_ok(
22182 "my @edges = ([0, 1, 5], [1, 2, -3]); \
22183 my @dist = (0, 1e18, 1e18); my $INF = 1e18; \
22184 for my $e (@edges) { \
22185 my ($u, $w, $cost) = @$e; \
22186 next if $dist[$u] >= $INF; \
22187 $dist[$w] = $dist[$u] + $cost if $dist[$u] + $cost < $dist[$w] \
22188 }",
22189 );
22190 }
22191
22192 #[test]
22195 fn bitwise_and_with_negation_parses() {
22196 let _g = NoInteropGuard::on();
22197 parse_ok("fn Fenwick::lsb($x) = $x & -$x");
22198 parse_ok("my $k = 12; my $lo_bit = $k & -$k");
22199 }
22200
22201 #[test]
22204 fn counting_sort_cumulative_loop_parses() {
22205 let _g = NoInteropGuard::on();
22206 parse_ok(
22207 "my @count = (3, 1, 2, 4); my $running = 0; \
22208 for my $v (0:3) { \
22209 my $c = $count[$v]; \
22210 $count[$v] = $running; \
22211 $running += $c \
22212 }",
22213 );
22214 }
22215
22216 #[test]
22220 fn recursive_tree_walk_with_hashref_parses() {
22221 let _g = NoInteropGuard::on();
22222 parse_ok(
22223 "fn Huff::walk($node, $prefix, $codes) { \
22224 return unless defined $node; \
22225 if (exists $node->{sym}) { \
22226 $codes->{$node->{sym}} = $prefix eq \"\" ? \"0\" : $prefix; \
22227 return \
22228 } \
22229 Huff::walk($node->{left}, $prefix . \"0\", $codes); \
22230 Huff::walk($node->{right}, $prefix . \"1\", $codes) \
22231 }",
22232 );
22233 }
22234
22235 #[test]
22238 fn z_array_window_arithmetic_parses() {
22239 let _g = NoInteropGuard::on();
22240 parse_ok(
22241 "my @c = (\"a\", \"b\", \"a\"); my $n = 3; \
22242 my @z = (0) x $n; $z[0] = $n; \
22243 my $l = 0; my $r = 0; my $i = 1; \
22244 if ($i < $r) { \
22245 my $inside = $r - $i < $z[$i - $l] ? $r - $i : $z[$i - $l]; \
22246 $z[$i] = $inside \
22247 } \
22248 while ($i + $z[$i] < $n && $c[$z[$i]] eq $c[$i + $z[$i]]) { $z[$i]++ }",
22249 );
22250 }
22251
22252 #[test]
22255 fn bfs_with_parent_link_node_parses() {
22256 let _g = NoInteropGuard::on();
22257 parse_ok(
22258 "my @open = (+{ r => 0, c => 0, g => 0, parent => undef }); \
22259 while (len(@open) > 0) { \
22260 my $cur = shift @open; \
22261 for my $d ([-1, 0], [1, 0], [0, -1], [0, 1]) { \
22262 my ($dr, $dc) = @$d; \
22263 push @open, +{ \
22264 r => $cur->{r} + $dr, c => $cur->{c} + $dc, \
22265 g => $cur->{g} + 1, parent => $cur \
22266 } \
22267 } \
22268 last \
22269 }",
22270 );
22271 }
22272
22273 #[test]
22276 fn cross_product_sort_comparator_parses() {
22277 let _g = NoInteropGuard::on();
22278 parse_ok(
22279 "fn Hull::cross($p1, $p2, $p3) = \
22280 ($p2->[0] - $p1->[0]) * ($p3->[1] - $p1->[1]) - \
22281 ($p2->[1] - $p1->[1]) * ($p3->[0] - $p1->[0]); \
22282 my $pivot = [0, 0]; my @pts = ([1, 1], [2, 0]); \
22283 my @sorted = sort { \
22284 my $c = Hull::cross($pivot, _0, _1); \
22285 $c == 0 ? 0 : ($c < 0 ? 1 : -1) \
22286 } @pts",
22287 );
22288 }
22289
22290 #[test]
22293 fn lomuto_partition_loop_parses() {
22294 let _g = NoInteropGuard::on();
22295 parse_ok(
22296 "fn QS::partition($arr, $lo, $hi) { \
22297 my $pivot = $arr->[$hi]; \
22298 my $i = $lo - 1; \
22299 for my $j ($lo:$hi - 1) { \
22300 if ($arr->[$j] <= $pivot) { \
22301 $i++; \
22302 ($arr->[$i], $arr->[$j]) = ($arr->[$j], $arr->[$i]) \
22303 } \
22304 } \
22305 ($arr->[$i + 1], $arr->[$hi]) = ($arr->[$hi], $arr->[$i + 1]); \
22306 $i + 1 \
22307 }",
22308 );
22309 }
22310
22311 #[test]
22314 fn tortoise_hare_diff_loop_parses() {
22315 let _g = NoInteropGuard::on();
22316 parse_ok(
22317 "my $x = 2; my $y = 2; my $n = 35; my $d = 1; \
22318 while ($d == 1) { \
22319 $x = ($x * $x + 1) % $n; \
22320 $y = ($y * $y + 1) % $n; \
22321 $y = ($y * $y + 1) % $n; \
22322 my $diff = $x > $y ? $x - $y : $y - $x; \
22323 $d = $diff \
22324 }",
22325 );
22326 }
22327
22328 #[test]
22331 fn ext_gcd_recursive_destructure_parses() {
22332 let _g = NoInteropGuard::on();
22333 parse_ok(
22334 "fn Mod::ext_gcd($va, $vb) { \
22335 return [$va, 1, 0] if $vb == 0; \
22336 my $r = Mod::ext_gcd($vb, $va % $vb); \
22337 my ($g, $x1, $y1) = @$r; \
22338 [$g, $y1, $x1 - int($va / $vb) * $y1] \
22339 }",
22340 );
22341 }
22342
22343 #[test]
22346 fn convolution_recurrence_dp_parses() {
22347 let _g = NoInteropGuard::on();
22348 parse_ok(
22349 "my @c = (1); my $n = 5; \
22350 for my $i (1:$n) { \
22351 my $sum = 0; \
22352 for my $j (0:$i - 1) { $sum += $c[$j] * $c[$i - 1 - $j] } \
22353 push @c, $sum \
22354 }",
22355 );
22356 }
22357
22358 #[test]
22363 fn tarjan_scc_shared_state_parses() {
22364 let _g = NoInteropGuard::on();
22365 parse_ok(
22366 "fn SCC::strong_connect($s, $v) { \
22367 $s->{idx_of}{$v} = $s->{index}; \
22368 $s->{low_of}{$v} = $s->{index}; \
22369 $s->{index}++; \
22370 push @{$s->{stack}}, $v; \
22371 $s->{on_stack}{$v} = 1; \
22372 for my $w (@{$s->{adj}{$v}}) { \
22373 if (!exists $s->{idx_of}{$w}) { \
22374 SCC::strong_connect($s, $w); \
22375 $s->{low_of}{$v} = $s->{low_of}{$w} if $s->{low_of}{$w} < $s->{low_of}{$v} \
22376 } \
22377 } \
22378 }",
22379 );
22380 }
22381
22382 #[test]
22385 fn heaps_algorithm_recursive_parses() {
22386 let _g = NoInteropGuard::on();
22387 parse_ok(
22388 "fn Perm::heaps_inner($arr, $n, $out) { \
22389 if ($n == 1) { my @snap = @$arr; push @$out, \\@snap; return } \
22390 for my $i (0:$n - 1) { \
22391 Perm::heaps_inner($arr, $n - 1, $out); \
22392 if ($n % 2 == 0) { \
22393 ($arr->[$i], $arr->[$n - 1]) = ($arr->[$n - 1], $arr->[$i]) \
22394 } else { \
22395 ($arr->[0], $arr->[$n - 1]) = ($arr->[$n - 1], $arr->[0]) \
22396 } \
22397 } \
22398 }",
22399 );
22400 }
22401
22402 #[test]
22410 fn format_as_hash_key_parses() {
22411 parse_ok("my %opts; $opts{format} = \"csv\"");
22412 parse_ok("my %opts = (format => \"csv\", level => 9)");
22413 parse_ok("my $h = +{ format => \"csv\" }");
22414 parse_ok("my @keys = ($h->{format}, $h->{level})");
22415 }
22416
22417 #[test]
22419 fn format_as_method_call_parses() {
22420 parse_ok("class Foo { val: Str; fn format($self) { \"x\" } } my $f = Foo(val => \"y\"); my $s = $f->format()");
22421 }
22422
22423 #[test]
22425 fn format_as_namespaced_tail_parses() {
22426 parse_ok("fn Foo::format($x) = $x . \"!\"");
22427 parse_ok("fn Foo::format($x) = $x . \"!\"; my $r = Foo::format(\"hi\")");
22428 }
22429
22430 #[test]
22437 fn arrow_hash_compound_assign_parses_all_ops() {
22438 let _g = NoInteropGuard::on();
22439 parse_ok("my $h = +{n=>10}; $h->{n} -= 1");
22440 parse_ok("my $h = +{n=>10}; $h->{n} += 1");
22441 parse_ok("my $h = +{n=>10}; $h->{n} *= 2");
22442 parse_ok("my $h = +{n=>10}; $h->{n} /= 2");
22443 parse_ok("my $h = +{n=>10}; $h->{n} %= 3");
22444 parse_ok("my $h = +{n=>\"x\"}; $h->{n} .= \"y\"");
22445 }
22446
22447 #[test]
22451 fn arrow_hash_compound_assign_value_chains_parses() {
22452 let _g = NoInteropGuard::on();
22453 parse_ok("my $h = +{n=>10}; my $v = $h->{n} -= 1");
22454 parse_ok("my $h = +{n=>10}; my @list = ($h->{n} += 5, $h->{n} += 5)");
22455 parse_ok("my $h = +{n=>10}; my $double = ($h->{n} -= 1) * 2");
22456 }
22457}