1use crate::ast::*;
2use crate::error::{ErrorKind, StrykeError, StrykeResult};
3use crate::lexer::{Lexer, LITERAL_AT_IN_DQUOTE, LITERAL_DOLLAR_IN_DQUOTE};
4use crate::token::{
5 is_frozen_decl, is_lexical_decl, is_lexical_or_our_decl, is_post_style_decl, Token,
6};
7use crate::vm_helper::VMHelper;
8
9fn postfix_lbracket_is_arrow_container(expr: &Expr) -> bool {
12 matches!(
13 expr.kind,
14 ExprKind::ArrayElement { .. }
15 | ExprKind::HashElement { .. }
16 | ExprKind::ArrowDeref { .. }
17 | ExprKind::Deref {
18 kind: Sigil::Scalar,
19 ..
20 }
21 )
22}
23
24fn destructure_stmt_from_var_decls(keyword: &str, decls: Vec<VarDecl>, line: usize) -> Statement {
25 let kind = match keyword {
26 "my" => StmtKind::My(decls),
27 "mysync" => StmtKind::MySync(decls),
28 "our" => StmtKind::Our(decls),
29 "oursync" => StmtKind::OurSync(decls),
30 "local" => StmtKind::Local(decls),
31 "state" => StmtKind::State(decls),
32 _ => unreachable!("parse_my_our_local keyword"),
33 };
34 Statement {
35 label: None,
36 kind,
37 line,
38 }
39}
40
41fn destructure_stmt_die_string(line: usize, msg: &str) -> Statement {
42 Statement {
43 label: None,
44 kind: StmtKind::Expression(Expr {
45 kind: ExprKind::Die(vec![Expr {
46 kind: ExprKind::String(msg.to_string()),
47 line,
48 }]),
49 line,
50 }),
51 line,
52 }
53}
54
55fn destructure_stmt_unless_die(line: usize, cond: Expr, msg: &str) -> Statement {
56 Statement {
57 label: None,
58 kind: StmtKind::Unless {
59 condition: cond,
60 body: vec![destructure_stmt_die_string(line, msg)],
61 else_block: None,
62 },
63 line,
64 }
65}
66
67fn destructure_expr_scalar_tmp(name: &str, line: usize) -> Expr {
68 Expr {
69 kind: ExprKind::ScalarVar(name.to_string()),
70 line,
71 }
72}
73
74fn destructure_expr_array_len(tmp: &str, line: usize) -> Expr {
75 Expr {
76 kind: ExprKind::Deref {
77 expr: Box::new(destructure_expr_scalar_tmp(tmp, line)),
78 kind: Sigil::Array,
79 },
80 line,
81 }
82}
83pub struct Parser {
85 tokens: Vec<(Token, usize)>,
87 pos: usize,
89 next_rate_limit_slot: u32,
91 suppress_indirect_paren_call: u32,
94 pipe_rhs_depth: u32,
100 block_depth: u32,
107 no_pipe_forward_depth: u32,
115 suppress_scalar_hash_brace: u32,
118 next_desugar_tmp: u32,
120 error_file: String,
122 declared_subs: std::collections::HashSet<String>,
124 suppress_parenless_call: u32,
128 pending_thread_input: Option<Expr>,
133 suppress_slash_as_div: u32,
136 pub suppress_m_regex: u32,
139 suppress_colon_range: u32,
143 suppress_tilde_range: u32,
149 thread_last_mode: bool,
152 pub parsing_module: bool,
155 list_construct_close_pos: Option<usize>,
163 pending_synthetic_subs: Vec<Statement>,
168 next_overload_anon_id: u32,
170 pub bare_positional_indices: std::collections::HashSet<usize>,
177 current_package: String,
183}
184
185impl Parser {
186 pub fn new(tokens: Vec<(Token, usize)>) -> Self {
188 Self::new_with_file(tokens, "-e")
189 }
190 pub fn new_with_file(tokens: Vec<(Token, usize)>, file: impl Into<String>) -> Self {
192 Self {
193 tokens,
194 pos: 0,
195 next_rate_limit_slot: 0,
196 suppress_indirect_paren_call: 0,
197 pipe_rhs_depth: 0,
198 no_pipe_forward_depth: 0,
199 suppress_scalar_hash_brace: 0,
200 next_desugar_tmp: 0,
201 error_file: file.into(),
202 declared_subs: std::collections::HashSet::new(),
203 suppress_parenless_call: 0,
204 pending_thread_input: None,
205 suppress_slash_as_div: 0,
206 suppress_m_regex: 0,
207 suppress_colon_range: 0,
208 suppress_tilde_range: 0,
209 thread_last_mode: false,
210 pending_synthetic_subs: Vec::new(),
211 next_overload_anon_id: 0,
212 parsing_module: false,
213 list_construct_close_pos: None,
214 bare_positional_indices: std::collections::HashSet::new(),
215 block_depth: 0,
216 current_package: "main".to_string(),
217 }
218 }
219
220 fn alloc_desugar_tmp(&mut self) -> u32 {
221 let n = self.next_desugar_tmp;
222 self.next_desugar_tmp = self.next_desugar_tmp.saturating_add(1);
223 n
224 }
225
226 #[inline]
230 fn in_pipe_rhs(&self) -> bool {
231 self.pipe_rhs_depth > 0
232 }
233
234 fn pipe_supplies_slurped_list_operand(&self) -> bool {
237 self.in_pipe_rhs()
238 && (matches!(
239 self.peek(),
240 Token::Semicolon
241 | Token::RBrace
242 | Token::RParen
243 | Token::Eof
244 | Token::Comma
245 | Token::PipeForward
246 ) || self.peek_line() > self.prev_line())
247 }
248
249 #[inline]
254 fn pipe_placeholder_list(&self, line: usize) -> Expr {
255 Expr {
256 kind: ExprKind::List(vec![]),
257 line,
258 }
259 }
260
261 fn is_block_then_list_pipe_builtin(name: &str) -> bool {
266 matches!(
267 name,
268 "pfirst"
269 | "pany"
270 | "any"
271 | "all"
272 | "none"
273 | "first"
274 | "find_index"
275 | "firstidx"
276 | "first_index"
277 | "take_while"
278 | "drop_while"
279 | "skip_while"
280 | "reject"
281 | "grepv"
282 | "tap"
283 | "peek"
284 | "group_by"
285 | "chunk_by"
286 | "partition"
287 | "min_by"
288 | "max_by"
289 | "zip_with"
290 | "count_by"
291 )
292 }
293
294 fn lift_bareword_to_topic_call(expr: Expr) -> Expr {
305 let line = expr.line;
306 let topic = || Expr {
307 kind: ExprKind::ScalarVar("_".into()),
308 line,
309 };
310 match expr.kind {
311 ExprKind::Bareword(ref name) => Expr {
312 kind: ExprKind::FuncCall {
313 name: name.clone(),
314 args: vec![topic()],
315 },
316 line,
317 },
318 ExprKind::Unlink(ref args) if args.is_empty() => Expr {
320 kind: ExprKind::Unlink(vec![topic()]),
321 line,
322 },
323 ExprKind::Chmod(ref args) if args.is_empty() => Expr {
324 kind: ExprKind::Chmod(vec![topic()]),
325 line,
326 },
327 ExprKind::Stat(_) => expr,
329 ExprKind::Lstat(_) => expr,
330 ExprKind::Readlink(_) => expr,
331 ExprKind::Rev(ref inner) => {
333 if matches!(inner.kind, ExprKind::List(ref v) if v.is_empty()) {
334 Expr {
335 kind: ExprKind::Rev(Box::new(topic())),
336 line,
337 }
338 } else {
339 expr
340 }
341 }
342 _ => expr,
343 }
344 }
345
346 fn parse_assign_expr_stop_at_pipe(&mut self) -> StrykeResult<Expr> {
354 self.no_pipe_forward_depth = self.no_pipe_forward_depth.saturating_add(1);
355 let r = self.parse_assign_expr();
356 self.no_pipe_forward_depth = self.no_pipe_forward_depth.saturating_sub(1);
357 r
358 }
359
360 fn syntax_err(&self, message: impl Into<String>, line: usize) -> StrykeError {
361 StrykeError::new(ErrorKind::Syntax, message, line, self.error_file.clone())
362 }
363
364 fn try_parse_coderef_listop_args(&mut self, line: usize) -> StrykeResult<Option<Vec<Expr>>> {
371 if !matches!(self.peek(), Token::ScalarVar(_) | Token::Backslash) {
372 return Ok(None);
373 }
374 let f = self.parse_assign_expr_stop_at_pipe()?;
375 let _ = self.eat(&Token::Comma);
376 let list = if self.in_pipe_rhs()
377 && matches!(
378 self.peek(),
379 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof | Token::PipeForward
380 ) {
381 self.pipe_placeholder_list(line)
382 } else {
383 self.parse_expression()?
384 };
385 Ok(Some(vec![f, list]))
386 }
387
388 fn alloc_rate_limit_slot(&mut self) -> u32 {
389 let s = self.next_rate_limit_slot;
390 self.next_rate_limit_slot = self.next_rate_limit_slot.saturating_add(1);
391 s
392 }
393
394 fn peek(&self) -> &Token {
395 self.tokens
396 .get(self.pos)
397 .map(|(t, _)| t)
398 .unwrap_or(&Token::Eof)
399 }
400
401 fn peek_line(&self) -> usize {
402 self.tokens.get(self.pos).map(|(_, l)| *l).unwrap_or(0)
403 }
404
405 fn peek_at(&self, offset: usize) -> &Token {
406 self.tokens
407 .get(self.pos + offset)
408 .map(|(t, _)| t)
409 .unwrap_or(&Token::Eof)
410 }
411
412 fn advance(&mut self) -> (Token, usize) {
413 let tok = self
414 .tokens
415 .get(self.pos)
416 .cloned()
417 .unwrap_or((Token::Eof, 0));
418 self.pos += 1;
419 tok
420 }
421
422 fn prev_line(&self) -> usize {
424 if self.pos > 0 {
425 self.tokens.get(self.pos - 1).map(|(_, l)| *l).unwrap_or(0)
426 } else {
427 0
428 }
429 }
430
431 fn looks_like_hashref(&self) -> bool {
440 debug_assert!(matches!(self.peek(), Token::LBrace));
441 let tok1 = self.peek_at(1);
442 let tok2 = self.peek_at(2);
443 match tok1 {
444 Token::RBrace => true,
445 Token::Ident(_)
446 | Token::SingleString(_)
447 | Token::DoubleString(_)
448 | Token::ScalarVar(_)
449 | Token::Integer(_) => matches!(tok2, Token::FatArrow),
450 Token::HashVar(_) => matches!(tok2, Token::RBrace | Token::Comma),
451 _ => false,
452 }
453 }
454
455 fn expect(&mut self, expected: &Token) -> StrykeResult<usize> {
456 let (tok, line) = self.advance();
457 if std::mem::discriminant(&tok) == std::mem::discriminant(expected) {
458 Ok(line)
459 } else {
460 Err(self.syntax_err(format!("Expected {:?}, got {:?}", expected, tok), line))
461 }
462 }
463
464 fn eat(&mut self, expected: &Token) -> bool {
465 if std::mem::discriminant(self.peek()) == std::mem::discriminant(expected) {
466 self.advance();
467 true
468 } else {
469 false
470 }
471 }
472
473 fn at_eof(&self) -> bool {
474 matches!(self.peek(), Token::Eof)
475 }
476
477 fn filetest_allows_implicit_topic(tok: &Token) -> bool {
479 matches!(
480 tok,
481 Token::RParen
482 | Token::Semicolon
483 | Token::Comma
484 | Token::RBrace
485 | Token::Eof
486 | Token::LogAnd
487 | Token::LogOr
488 | Token::LogAndWord
489 | Token::LogOrWord
490 | Token::PipeForward
491 )
492 }
493
494 fn next_is_new_stmt_keyword(&self, stmt_line: usize) -> bool {
498 if crate::compat_mode() {
500 return false;
501 }
502 if self.peek_line() == stmt_line {
503 return false;
504 }
505 matches!(
506 self.peek(),
507 Token::Ident(ref kw) if matches!(kw.as_str(),
508 "use" | "import" | "no" | "my" | "our" | "local" | "sub" | "struct" | "enum"
509 | "if" | "unless" | "while" | "until" | "for" | "foreach" | "loop" | "ploop"
510 | "pwhile" | "return" | "last" | "next" | "redo" | "package" | "require"
511 | "BEGIN" | "END" | "UNITCHECK" | "frozen" | "const" | "typed"
512 | "fn" | "class" | "abstract" | "final" | "trait"
517 | "state" | "mysync" | "varsync" | "oursync" | "var" | "val"
518 )
519 )
520 }
521
522 fn next_is_new_statement_start(&self, stmt_line: usize) -> bool {
526 if crate::compat_mode() {
527 return false;
528 }
529 if self.peek_line() == stmt_line {
530 return false;
531 }
532 matches!(
533 self.peek(),
534 Token::ScalarVar(_)
535 | Token::DerefScalarVar(_)
536 | Token::ArrayVar(_)
537 | Token::HashVar(_)
538 | Token::LBrace
539 ) || self.next_is_new_stmt_keyword(stmt_line)
540 }
541
542 pub fn parse_program(&mut self) -> StrykeResult<Program> {
545 let mut statements = self.parse_statements()?;
546 if !self.pending_synthetic_subs.is_empty() {
550 let synthetics = std::mem::take(&mut self.pending_synthetic_subs);
551 let mut combined = Vec::with_capacity(synthetics.len() + statements.len());
552 combined.extend(synthetics);
553 combined.append(&mut statements);
554 statements = combined;
555 }
556 Ok(Program { statements })
557 }
558
559 pub fn parse_statements(&mut self) -> StrykeResult<Vec<Statement>> {
561 let mut statements = Vec::new();
562 while !self.at_eof() {
563 if matches!(self.peek(), Token::Semicolon) {
564 let line = self.peek_line();
565 self.advance();
566 statements.push(Statement {
567 label: None,
568 kind: StmtKind::Empty,
569 line,
570 });
571 continue;
572 }
573 statements.push(self.parse_statement()?);
574 }
575 Ok(statements)
576 }
577
578 fn parse_statement(&mut self) -> StrykeResult<Statement> {
581 let line = self.peek_line();
582
583 let label = match self.peek().clone() {
586 Token::Ident(_) => {
587 if matches!(self.peek_at(1), Token::Colon)
588 && !matches!(self.peek_at(2), Token::Colon)
589 {
590 let (tok, _) = self.advance();
591 let l = match tok {
592 Token::Ident(l) => l,
593 _ => unreachable!(),
594 };
595 self.advance(); Some(l)
597 } else {
598 None
599 }
600 }
601 _ => None,
602 };
603
604 let mut stmt = match self.peek().clone() {
605 Token::FormatDecl { .. } => {
606 let tok_line = self.peek_line();
607 let (tok, _) = self.advance();
608 match tok {
609 Token::FormatDecl { name, lines } => Statement {
610 label: label.clone(),
611 kind: StmtKind::FormatDecl { name, lines },
612 line: tok_line,
613 },
614 _ => unreachable!(),
615 }
616 }
617 Token::Ident(ref kw) => match kw.as_str() {
618 "if" => self.parse_if()?,
619 "unless" => self.parse_unless()?,
620 "while" => {
621 let mut s = self.parse_while()?;
622 if let StmtKind::While {
623 label: ref mut lbl, ..
624 } = s.kind
625 {
626 *lbl = label.clone();
627 }
628 s
629 }
630 "loop" => {
636 let mut s = self.parse_loop()?;
637 if let StmtKind::While {
638 label: ref mut lbl, ..
639 } = s.kind
640 {
641 *lbl = label.clone();
642 }
643 s
644 }
645 "ploop" if !crate::compat_mode() => self.parse_ploop()?,
649 "pwhile" if !crate::compat_mode() => self.parse_pwhile()?,
650 "until" => {
651 let mut s = self.parse_until()?;
652 if let StmtKind::Until {
653 label: ref mut lbl, ..
654 } = s.kind
655 {
656 *lbl = label.clone();
657 }
658 s
659 }
660 "for" => {
661 let mut s = self.parse_for_or_foreach()?;
662 match s.kind {
663 StmtKind::For {
664 label: ref mut lbl, ..
665 }
666 | StmtKind::Foreach {
667 label: ref mut lbl, ..
668 } => *lbl = label.clone(),
669 _ => {}
670 }
671 s
672 }
673 "foreach" => {
674 let mut s = self.parse_foreach()?;
675 if let StmtKind::Foreach {
676 label: ref mut lbl, ..
677 } = s.kind
678 {
679 *lbl = label.clone();
680 }
681 s
682 }
683 "sub" => {
684 if crate::no_interop_mode() {
685 return Err(self.syntax_err(
686 "stryke uses `fn` instead of `sub` (--no-interop is active)",
687 self.peek_line(),
688 ));
689 }
690 self.parse_sub_decl(true)?
691 }
692 "fn" => self.parse_sub_decl(false)?,
693 "struct" => {
694 if crate::compat_mode() {
695 return Err(self.syntax_err(
696 "`struct` is a stryke extension (disabled by --compat)",
697 self.peek_line(),
698 ));
699 }
700 self.parse_struct_decl()?
701 }
702 "enum" => {
703 if crate::compat_mode() {
704 return Err(self.syntax_err(
705 "`enum` is a stryke extension (disabled by --compat)",
706 self.peek_line(),
707 ));
708 }
709 self.parse_enum_decl()?
710 }
711 "class" => {
712 if crate::compat_mode() {
713 return Err(self.syntax_err(
715 "Perl 5.38 `class` syntax not yet implemented in --compat mode",
716 self.peek_line(),
717 ));
718 }
719 self.parse_class_decl(false, false)?
720 }
721 "abstract" => {
722 self.advance(); if !matches!(self.peek(), Token::Ident(ref s) if s == "class") {
724 return Err(self.syntax_err(
725 "`abstract` must be followed by `class`",
726 self.peek_line(),
727 ));
728 }
729 self.parse_class_decl(true, false)?
730 }
731 "final" => {
732 self.advance(); if !matches!(self.peek(), Token::Ident(ref s) if s == "class") {
734 return Err(self
735 .syntax_err("`final` must be followed by `class`", self.peek_line()));
736 }
737 self.parse_class_decl(false, true)?
738 }
739 "trait" => {
740 if crate::compat_mode() {
741 return Err(self.syntax_err(
742 "`trait` is a stryke extension (disabled by --compat)",
743 self.peek_line(),
744 ));
745 }
746 self.parse_trait_decl()?
747 }
748 "my" => self.parse_my_our_local("my", false)?,
749 "var" if matches!(
760 self.peek_at(1),
761 Token::ScalarVar(_)
762 | Token::ArrayVar(_)
763 | Token::HashVar(_)
764 | Token::Star
765 | Token::LParen
766 ) || matches!(
767 self.peek_at(1),
768 Token::Ident(ref k) if k == "typed"
769 ) =>
770 {
771 if crate::compat_mode() {
772 return Err(self.syntax_err(
773 "`var` is a stryke extension (disabled by --compat)",
774 self.peek_line(),
775 ));
776 }
777 self.parse_my_our_local("my", false)?
778 }
779 "val" if matches!(
780 self.peek_at(1),
781 Token::ScalarVar(_)
782 | Token::ArrayVar(_)
783 | Token::HashVar(_)
784 | Token::Star
785 | Token::LParen
786 ) || matches!(
787 self.peek_at(1),
788 Token::Ident(ref k) if k == "typed"
789 ) =>
790 {
791 if crate::compat_mode() {
792 return Err(self.syntax_err(
793 "`val` is a stryke extension (disabled by --compat)",
794 self.peek_line(),
795 ));
796 }
797 let mut stmt = self.parse_my_our_local("my", true)?;
798 if let StmtKind::My(ref mut decls) = stmt.kind {
799 for decl in decls.iter_mut() {
800 decl.frozen = true;
801 }
802 }
803 stmt
804 }
805 "state" => self.parse_my_our_local("state", false)?,
806 "mysync" => {
807 if crate::compat_mode() {
808 return Err(self.syntax_err(
809 "`mysync` is a stryke extension (disabled by --compat)",
810 self.peek_line(),
811 ));
812 }
813 self.parse_my_our_local("mysync", false)?
814 }
815 "varsync" => {
820 if crate::compat_mode() {
821 return Err(self.syntax_err(
822 "`varsync` is a stryke extension (disabled by --compat)",
823 self.peek_line(),
824 ));
825 }
826 self.parse_my_our_local("mysync", false)?
827 }
828 "oursync" => {
829 if crate::compat_mode() {
830 return Err(self.syntax_err(
831 "`oursync` is a stryke extension (disabled by --compat)",
832 self.peek_line(),
833 ));
834 }
835 self.parse_my_our_local("oursync", false)?
836 }
837 "frozen" | "const" => {
838 let leading = kw.as_str().to_string();
839 if crate::compat_mode() {
840 return Err(self.syntax_err(
841 format!("`{leading}` is a stryke extension (disabled by --compat)"),
842 self.peek_line(),
843 ));
844 }
845 self.advance(); if let Token::Ident(ref kw) = self.peek().clone() {
857 if is_lexical_decl(kw) {
858 let mut stmt = self.parse_my_our_local("my", true)?;
863 if let StmtKind::My(ref mut decls) = stmt.kind {
864 for decl in decls.iter_mut() {
865 decl.frozen = true;
866 }
867 }
868 stmt
869 } else {
870 return Err(self.syntax_err(
871 format!("Expected 'my' / 'var' / 'val' after '{leading}'"),
872 self.peek_line(),
873 ));
874 }
875 } else {
876 return Err(self.syntax_err(
877 format!("Expected 'my' / 'var' / 'val' after '{leading}'"),
878 self.peek_line(),
879 ));
880 }
881 }
882 "typed" => {
883 if crate::compat_mode() {
884 return Err(self.syntax_err(
885 "`typed` is a stryke extension (disabled by --compat)",
886 self.peek_line(),
887 ));
888 }
889 self.advance();
890 if let Token::Ident(ref kw) = self.peek().clone() {
891 let raw_kw = kw.clone();
899 if is_lexical_decl(&raw_kw) {
900 let mut stmt = self.parse_my_our_local("my", true)?;
901 if is_frozen_decl(&raw_kw) {
902 if let StmtKind::My(ref mut decls) = stmt.kind {
903 for decl in decls.iter_mut() {
904 decl.frozen = true;
905 }
906 }
907 }
908 stmt
909 } else {
910 return Err(self.syntax_err(
911 "Expected 'my' / 'var' / 'val' after 'typed'",
912 self.peek_line(),
913 ));
914 }
915 } else {
916 return Err(self.syntax_err(
917 "Expected 'my' / 'var' / 'val' after 'typed'",
918 self.peek_line(),
919 ));
920 }
921 }
922 "our" => self.parse_my_our_local("our", false)?,
923 "local" => self.parse_my_our_local("local", false)?,
924 "package" => self.parse_package()?,
925 "use" | "import" => self.parse_use()?,
929 "no" => self.parse_no()?,
930 "return" => self.parse_return()?,
931 "last" => {
932 self.advance();
933 let lbl = self.try_take_loop_label();
934 let stmt = Statement {
935 label: None,
936 kind: StmtKind::Last(lbl.or(label.clone())),
937 line,
938 };
939 self.parse_stmt_postfix_modifier(stmt)?
940 }
941 "next" => {
942 self.advance();
943 let lbl = self.try_take_loop_label();
944 let stmt = Statement {
945 label: None,
946 kind: StmtKind::Next(lbl.or(label.clone())),
947 line,
948 };
949 self.parse_stmt_postfix_modifier(stmt)?
950 }
951 "redo" => {
952 self.advance();
953 let lbl = self.try_take_loop_label();
954 let stmt = Statement {
955 label: None,
956 kind: StmtKind::Redo(lbl.or(label.clone())),
957 line,
958 };
959 self.parse_stmt_postfix_modifier(stmt)?
960 }
961 "BEGIN" => {
962 self.advance();
963 let block = self.parse_block()?;
964 Statement {
965 label: None,
966 kind: StmtKind::Begin(block),
967 line,
968 }
969 }
970 "END" => {
971 self.advance();
972 let block = self.parse_block()?;
973 Statement {
974 label: None,
975 kind: StmtKind::End(block),
976 line,
977 }
978 }
979 "UNITCHECK" => {
980 self.advance();
981 let block = self.parse_block()?;
982 Statement {
983 label: None,
984 kind: StmtKind::UnitCheck(block),
985 line,
986 }
987 }
988 "CHECK" => {
989 self.advance();
990 let block = self.parse_block()?;
991 Statement {
992 label: None,
993 kind: StmtKind::Check(block),
994 line,
995 }
996 }
997 "INIT" => {
998 self.advance();
999 let block = self.parse_block()?;
1000 Statement {
1001 label: None,
1002 kind: StmtKind::Init(block),
1003 line,
1004 }
1005 }
1006 "goto" => {
1007 self.advance();
1008 let target = self.parse_expression()?;
1009 let stmt = Statement {
1010 label: None,
1011 kind: StmtKind::Goto {
1012 target: Box::new(target),
1013 },
1014 line,
1015 };
1016 self.parse_stmt_postfix_modifier(stmt)?
1018 }
1019 "continue" => {
1020 self.advance();
1021 let block = self.parse_block()?;
1022 Statement {
1023 label: None,
1024 kind: StmtKind::Continue(block),
1025 line,
1026 }
1027 }
1028 "before"
1029 if matches!(
1030 self.peek_at(1),
1031 Token::SingleString(_) | Token::DoubleString(_)
1032 ) =>
1033 {
1034 self.parse_advice_decl(crate::ast::AdviceKind::Before)?
1035 }
1036 "after"
1037 if matches!(
1038 self.peek_at(1),
1039 Token::SingleString(_) | Token::DoubleString(_)
1040 ) =>
1041 {
1042 self.parse_advice_decl(crate::ast::AdviceKind::After)?
1043 }
1044 "around"
1045 if matches!(
1046 self.peek_at(1),
1047 Token::SingleString(_) | Token::DoubleString(_)
1048 ) =>
1049 {
1050 self.parse_advice_decl(crate::ast::AdviceKind::Around)?
1051 }
1052 "try" => self.parse_try_catch()?,
1053 "defer" => self.parse_defer_stmt()?,
1054 "tie" => self.parse_tie_stmt()?,
1055 "given" => self.parse_given()?,
1056 "when" => self.parse_when_stmt()?,
1057 "default" => self.parse_default_stmt()?,
1058 "eval_timeout" => self.parse_eval_timeout()?,
1059 "do" => {
1060 if matches!(self.peek_at(1), Token::LBrace) {
1061 self.advance();
1062 let body = self.parse_block()?;
1063 if let Token::Ident(ref w) = self.peek().clone() {
1064 if w == "while" {
1065 self.advance();
1066 self.expect(&Token::LParen)?;
1067 let mut condition = self.parse_expression()?;
1068 Self::mark_match_scalar_g_for_boolean_condition(&mut condition);
1069 self.expect(&Token::RParen)?;
1070 self.eat(&Token::Semicolon);
1071 Statement {
1072 label: label.clone(),
1073 kind: StmtKind::DoWhile { body, condition },
1074 line,
1075 }
1076 } else {
1077 let inner_line = body.first().map(|s| s.line).unwrap_or(line);
1078 let inner = Expr {
1079 kind: ExprKind::CodeRef {
1080 params: vec![],
1081 body,
1082 },
1083 line: inner_line,
1084 };
1085 let expr = Expr {
1086 kind: ExprKind::Do(Box::new(inner)),
1087 line,
1088 };
1089 let stmt = Statement {
1090 label: label.clone(),
1091 kind: StmtKind::Expression(expr),
1092 line,
1093 };
1094 self.parse_stmt_postfix_modifier(stmt)?
1096 }
1097 } else {
1098 let inner_line = body.first().map(|s| s.line).unwrap_or(line);
1099 let inner = Expr {
1100 kind: ExprKind::CodeRef {
1101 params: vec![],
1102 body,
1103 },
1104 line: inner_line,
1105 };
1106 let expr = Expr {
1107 kind: ExprKind::Do(Box::new(inner)),
1108 line,
1109 };
1110 let stmt = Statement {
1111 label: label.clone(),
1112 kind: StmtKind::Expression(expr),
1113 line,
1114 };
1115 self.parse_stmt_postfix_modifier(stmt)?
1116 }
1117 } else {
1118 if let Some(expr) = self.try_parse_bareword_stmt_call() {
1119 let stmt = self.maybe_postfix_modifier(expr)?;
1120 self.parse_stmt_postfix_modifier(stmt)?
1121 } else {
1122 let expr = self.parse_expression()?;
1123 let stmt = self.maybe_postfix_modifier(expr)?;
1124 self.parse_stmt_postfix_modifier(stmt)?
1125 }
1126 }
1127 }
1128 _ => {
1129 if let Some(expr) = self.try_parse_bareword_stmt_call() {
1131 let stmt = self.maybe_postfix_modifier(expr)?;
1132 self.parse_stmt_postfix_modifier(stmt)?
1133 } else {
1134 let expr = self.parse_expression()?;
1135 let stmt = self.maybe_postfix_modifier(expr)?;
1136 self.parse_stmt_postfix_modifier(stmt)?
1137 }
1138 }
1139 },
1140 Token::LBrace => {
1141 if self.looks_like_hashref() {
1144 let expr = self.parse_expression()?;
1145 let stmt = self.maybe_postfix_modifier(expr)?;
1146 self.parse_stmt_postfix_modifier(stmt)?
1147 } else {
1148 let block = self.parse_block()?;
1149 let stmt = Statement {
1150 label: None,
1151 kind: StmtKind::Block(block),
1152 line,
1153 };
1154 self.parse_stmt_postfix_modifier(stmt)?
1156 }
1157 }
1158 _ => {
1159 let expr = self.parse_expression()?;
1160 let stmt = self.maybe_postfix_modifier(expr)?;
1161 self.parse_stmt_postfix_modifier(stmt)?
1162 }
1163 };
1164
1165 stmt.label = label;
1166 Ok(stmt)
1167 }
1168
1169 fn try_take_loop_label(&mut self) -> Option<String> {
1175 let Token::Ident(s) = self.peek() else {
1176 return None;
1177 };
1178 let mut chars = s.chars();
1179 let first = chars.next()?;
1180 if !(first.is_ascii_uppercase() || first == '_') {
1181 return None;
1182 }
1183 let ok = s
1184 .chars()
1185 .all(|c| c.is_ascii_uppercase() || c.is_ascii_digit() || c == '_');
1186 if !ok {
1187 return None;
1188 }
1189 let (Token::Ident(l), _) = self.advance() else {
1190 unreachable!()
1191 };
1192 Some(l)
1193 }
1194
1195 fn parse_stmt_postfix_modifier(&mut self, stmt: Statement) -> StrykeResult<Statement> {
1197 let line = stmt.line;
1198 if !crate::compat_mode() && self.peek_line() > self.prev_line() {
1208 self.eat(&Token::Semicolon);
1209 return Ok(stmt);
1210 }
1211 if let Token::Ident(ref kw) = self.peek().clone() {
1212 match kw.as_str() {
1213 "if" => {
1214 self.advance();
1215 let mut cond = self.parse_expression()?;
1216 Self::mark_match_scalar_g_for_boolean_condition(&mut cond);
1217 self.eat(&Token::Semicolon);
1218 return Ok(Statement {
1219 label: None,
1220 kind: StmtKind::If {
1221 condition: cond,
1222 body: vec![stmt],
1223 elsifs: vec![],
1224 else_block: None,
1225 },
1226 line,
1227 });
1228 }
1229 "unless" => {
1230 self.advance();
1231 let mut cond = self.parse_expression()?;
1232 Self::mark_match_scalar_g_for_boolean_condition(&mut cond);
1233 self.eat(&Token::Semicolon);
1234 return Ok(Statement {
1235 label: None,
1236 kind: StmtKind::Unless {
1237 condition: cond,
1238 body: vec![stmt],
1239 else_block: None,
1240 },
1241 line,
1242 });
1243 }
1244 "while" | "until" | "for" | "foreach" => {
1245 if let Some(expr) = Self::stmt_into_postfix_body_expr(stmt) {
1248 let out = self.maybe_postfix_modifier(expr)?;
1249 self.eat(&Token::Semicolon);
1250 return Ok(out);
1251 }
1252 return Err(self.syntax_err(
1253 format!("postfix `{}` is not supported on this statement form", kw),
1254 self.peek_line(),
1255 ));
1256 }
1257 "pmap" | "pflat_map" | "pgrep" | "pfor" | "pforeach" | "preduce" | "pcache" | "par" => {
1259 let line = stmt.line;
1260 let block = self.stmt_into_parallel_block(stmt)?;
1261 let which = kw.as_str();
1262 self.advance();
1263 self.eat(&Token::Comma);
1264 let (list, progress) = self.parse_assign_expr_list_optional_progress()?;
1265 self.eat(&Token::Semicolon);
1266 let list = Box::new(list);
1267 let progress = progress.map(Box::new);
1268 let kind = match which {
1269 "pmap" => ExprKind::PMapExpr {
1270 block,
1271 list,
1272 progress,
1273 flat_outputs: false,
1274 on_cluster: None,
1275 stream: false,
1276 },
1277 "pflat_map" => ExprKind::PMapExpr {
1278 block,
1279 list,
1280 progress,
1281 flat_outputs: true,
1282 on_cluster: None,
1283 stream: false,
1284 },
1285 "pgrep" => ExprKind::PGrepExpr {
1286 block,
1287 list,
1288 progress,
1289 stream: false,
1290 },
1291 "pfor" | "pforeach" => ExprKind::PForExpr {
1292 block,
1293 list,
1294 progress,
1295 },
1296 "preduce" => ExprKind::PReduceExpr {
1297 block,
1298 list,
1299 progress,
1300 },
1301 "pcache" => ExprKind::PcacheExpr {
1302 block,
1303 list,
1304 progress,
1305 },
1306 "par" => ExprKind::ParExpr { block, list },
1307 _ => unreachable!(),
1308 };
1309 return Ok(Statement {
1310 label: None,
1311 kind: StmtKind::Expression(Expr { kind, line }),
1312 line,
1313 });
1314 }
1315 _ => {}
1316 }
1317 }
1318 self.eat(&Token::Semicolon);
1319 Ok(stmt)
1320 }
1321
1322 fn stmt_into_parallel_block(&self, stmt: Statement) -> StrykeResult<Block> {
1325 let line = stmt.line;
1326 match stmt.kind {
1327 StmtKind::Block(block) => Ok(block),
1328 StmtKind::Expression(expr) => {
1329 if let ExprKind::Do(ref inner) = expr.kind {
1330 if let ExprKind::CodeRef { ref body, .. } = inner.kind {
1331 return Ok(body.clone());
1332 }
1333 }
1334 Ok(vec![Statement {
1335 label: None,
1336 kind: StmtKind::Expression(expr),
1337 line,
1338 }])
1339 }
1340 _ => Err(self.syntax_err(
1341 "postfix parallel op expects `do { }`, a bare `{ }` block, or an expression statement",
1342 line,
1343 )),
1344 }
1345 }
1346
1347 fn stmt_into_postfix_body_expr(stmt: Statement) -> Option<Expr> {
1350 match stmt.kind {
1351 StmtKind::Expression(expr) => Some(expr),
1352 StmtKind::Block(block) => {
1353 let line = stmt.line;
1354 let inner = Expr {
1355 kind: ExprKind::CodeRef {
1356 params: vec![],
1357 body: block,
1358 },
1359 line,
1360 };
1361 Some(Expr {
1362 kind: ExprKind::Do(Box::new(inner)),
1363 line,
1364 })
1365 }
1366 _ => None,
1367 }
1368 }
1369
1370 fn peek_is_postfix_stmt_modifier_keyword(&self) -> bool {
1373 matches!(
1374 self.peek(),
1375 Token::Ident(ref kw)
1376 if matches!(
1377 kw.as_str(),
1378 "if" | "unless" | "while" | "until" | "for" | "foreach"
1379 )
1380 )
1381 }
1382
1383 fn peek_is_named_unary_terminator(&self) -> bool {
1390 matches!(
1391 self.peek(),
1392 Token::Semicolon
1393 | Token::RBrace
1394 | Token::RParen
1395 | Token::RBracket
1396 | Token::Eof
1397 | Token::Comma
1398 | Token::FatArrow
1399 | Token::PipeForward
1400 | Token::Question
1401 | Token::Colon
1402 | Token::NumEq
1403 | Token::NumNe
1404 | Token::NumLt
1405 | Token::NumGt
1406 | Token::NumLe
1407 | Token::NumGe
1408 | Token::Spaceship
1409 | Token::StrEq
1410 | Token::StrNe
1411 | Token::StrLt
1412 | Token::StrGt
1413 | Token::StrLe
1414 | Token::StrGe
1415 | Token::StrCmp
1416 | Token::LogAnd
1417 | Token::LogOr
1418 | Token::LogAndWord
1419 | Token::LogOrWord
1420 | Token::DefinedOr
1421 | Token::Range
1422 | Token::RangeExclusive
1423 | Token::Assign
1424 | Token::PlusAssign
1425 | Token::MinusAssign
1426 | Token::MulAssign
1427 | Token::DivAssign
1428 | Token::ModAssign
1429 | Token::PowAssign
1430 | Token::DotAssign
1431 | Token::AndAssign
1432 | Token::OrAssign
1433 | Token::XorAssign
1434 | Token::DefinedOrAssign
1435 | Token::ShiftLeftAssign
1436 | Token::ShiftRightAssign
1437 | Token::BitAndAssign
1438 | Token::BitOrAssign
1439 )
1440 }
1441
1442 fn maybe_postfix_modifier(&mut self, expr: Expr) -> StrykeResult<Statement> {
1443 let line = expr.line;
1444 if !crate::compat_mode() && self.peek_line() > self.prev_line() {
1447 return Ok(Statement {
1448 label: None,
1449 kind: StmtKind::Expression(expr),
1450 line,
1451 });
1452 }
1453 match self.peek() {
1454 Token::Ident(ref kw) => match kw.as_str() {
1455 "if" => {
1456 self.advance();
1457 let cond = self.parse_expression()?;
1458 Ok(Statement {
1459 label: None,
1460 kind: StmtKind::Expression(Expr {
1461 kind: ExprKind::PostfixIf {
1462 expr: Box::new(expr),
1463 condition: Box::new(cond),
1464 },
1465 line,
1466 }),
1467 line,
1468 })
1469 }
1470 "unless" => {
1471 self.advance();
1472 let cond = self.parse_expression()?;
1473 Ok(Statement {
1474 label: None,
1475 kind: StmtKind::Expression(Expr {
1476 kind: ExprKind::PostfixUnless {
1477 expr: Box::new(expr),
1478 condition: Box::new(cond),
1479 },
1480 line,
1481 }),
1482 line,
1483 })
1484 }
1485 "while" => {
1486 self.advance();
1487 let cond = self.parse_expression()?;
1488 Ok(Statement {
1489 label: None,
1490 kind: StmtKind::Expression(Expr {
1491 kind: ExprKind::PostfixWhile {
1492 expr: Box::new(expr),
1493 condition: Box::new(cond),
1494 },
1495 line,
1496 }),
1497 line,
1498 })
1499 }
1500 "until" => {
1501 self.advance();
1502 let cond = self.parse_expression()?;
1503 Ok(Statement {
1504 label: None,
1505 kind: StmtKind::Expression(Expr {
1506 kind: ExprKind::PostfixUntil {
1507 expr: Box::new(expr),
1508 condition: Box::new(cond),
1509 },
1510 line,
1511 }),
1512 line,
1513 })
1514 }
1515 "for" | "foreach" => {
1516 self.advance();
1517 let list = self.parse_expression()?;
1518 Ok(Statement {
1519 label: None,
1520 kind: StmtKind::Expression(Expr {
1521 kind: ExprKind::PostfixForeach {
1522 expr: Box::new(expr),
1523 list: Box::new(list),
1524 },
1525 line,
1526 }),
1527 line,
1528 })
1529 }
1530 _ => Ok(Statement {
1531 label: None,
1532 kind: StmtKind::Expression(expr),
1533 line,
1534 }),
1535 },
1536 _ => Ok(Statement {
1537 label: None,
1538 kind: StmtKind::Expression(expr),
1539 line,
1540 }),
1541 }
1542 }
1543
1544 fn try_parse_bareword_stmt_call(&mut self) -> Option<Expr> {
1546 let saved = self.pos;
1547 let line = self.peek_line();
1548 let mut name = match self.peek() {
1549 Token::Ident(n) => n.clone(),
1550 _ => return None,
1551 };
1552 if name.starts_with('\x00') || !Self::bareword_stmt_may_be_sub(&name) {
1554 return None;
1555 }
1556 self.advance();
1557 while self.eat(&Token::PackageSep) {
1558 match self.advance() {
1559 (Token::Ident(part), _) => {
1560 name = format!("{}::{}", name, part);
1561 }
1562 _ => {
1563 self.pos = saved;
1564 return None;
1565 }
1566 }
1567 }
1568 match self.peek() {
1569 Token::Semicolon | Token::RBrace => Some(Expr {
1570 kind: ExprKind::FuncCall { name, args: vec![] },
1571 line,
1572 }),
1573 _ => {
1574 self.pos = saved;
1575 None
1576 }
1577 }
1578 }
1579
1580 pub(crate) fn operator_keyword_to_ident_str(tok: &Token) -> Option<&'static str> {
1584 Some(match tok {
1585 Token::StrEq => "eq",
1586 Token::StrNe => "ne",
1587 Token::StrLt => "lt",
1588 Token::StrGt => "gt",
1589 Token::StrLe => "le",
1590 Token::StrGe => "ge",
1591 Token::StrCmp => "cmp",
1592 Token::LogAndWord => "and",
1593 Token::LogOrWord => "or",
1594 Token::LogNotWord => "not",
1595 Token::X => "x",
1596 _ => return None,
1597 })
1598 }
1599
1600 pub(crate) fn is_underscore_topic_slot(name: &str) -> bool {
1604 if name == "_" {
1605 return true;
1606 }
1607 if !name.starts_with('_') || name.len() < 2 {
1608 return false;
1609 }
1610 let bytes = name.as_bytes();
1611 let mut i = 1;
1612 while i < bytes.len() && bytes[i].is_ascii_digit() {
1614 i += 1;
1615 }
1616 let chevrons_start = i;
1618 while i < bytes.len() && bytes[i] == b'<' {
1619 i += 1;
1620 }
1621 i == bytes.len() && (i > 1 || chevrons_start > 1)
1623 }
1624
1625 pub(crate) fn is_reserved_special_var_name(name: &str) -> bool {
1635 matches!(
1636 name,
1637 "STDIN" | "STDOUT" | "STDERR" | "ARGV" | "ARGVOUT" | "DATA"
1639 | "ENV" | "INC" | "SIG" | "ISA"
1647 | "EXPORT" | "EXPORT_OK" | "EXPORT_TAGS"
1648 | "VERSION"
1649 | "__FILE__" | "__LINE__" | "__PACKAGE__" | "__SUB__"
1651 | "__DATA__" | "__END__"
1652 )
1653 }
1654
1655 fn bareword_stmt_may_be_sub(name: &str) -> bool {
1657 if Self::is_underscore_topic_slot(name) {
1662 return false;
1663 }
1664 !matches!(
1665 name,
1666 "__FILE__"
1667 | "__LINE__"
1668 | "__PACKAGE__"
1669 | "__SUB__"
1670 | "abs"
1671 | "async"
1672 | "spawn"
1673 | "atan2"
1674 | "await"
1675 | "barrier"
1676 | "bless"
1677 | "burp"
1678 | "caller"
1679 | "capture"
1680 | "cat"
1681 | "chdir"
1682 | "chmod"
1683 | "chomp"
1684 | "chop"
1685 | "chr"
1686 | "chown"
1687 | "closedir"
1688 | "close"
1689 | "collect"
1690 | "cos"
1691 | "crypt"
1692 | "defined"
1693 | "dec"
1694 | "delete"
1695 | "die"
1696 | "deque"
1697 | "do"
1698 | "each"
1699 | "eof"
1700 | "fore"
1701 | "eval"
1702 | "exec"
1703 | "exists"
1704 | "exit"
1705 | "exp"
1706 | "fan"
1707 | "fan_cap"
1708 | "fc"
1709 | "fetch_url"
1710 | "d"
1711 | "dirs"
1712 | "dr"
1713 | "f"
1714 | "fi"
1715 | "files"
1716 | "filesf"
1717 | "filter"
1718 | "fr"
1719 | "getcwd"
1720 | "glob_par"
1721 | "par_sed"
1722 | "glob"
1723 | "god"
1724 | "grep"
1725 | "greps"
1726 | "heap"
1727 | "hex"
1728 | "inc"
1729 | "index"
1730 | "int"
1731 | "join"
1732 | "keys"
1733 | "lcfirst"
1734 | "lc"
1735 | "length"
1736 | "link"
1737 | "log"
1738 | "lstat"
1739 | "map"
1740 | "flat_map"
1741 | "maps"
1742 | "flat_maps"
1743 | "flatten"
1744 | "frequencies"
1745 | "freq"
1746 | "pfrequencies"
1747 | "pfreq"
1748 | "interleave"
1749 | "ddump"
1750 | "stringify"
1751 | "str"
1752 | "s"
1753 | "input"
1754 | "lines"
1755 | "words"
1756 | "chars"
1757 | "digits"
1758 | "letters"
1759 | "letters_uc"
1760 | "letters_lc"
1761 | "punctuation"
1762 | "sentences"
1763 | "paragraphs"
1764 | "sections"
1765 | "numbers"
1766 | "graphemes"
1767 | "columns"
1768 | "trim"
1769 | "avg"
1770 | "top"
1771 | "pager"
1772 | "pg"
1773 | "less"
1774 | "count_by"
1775 | "to_file"
1776 | "to_json"
1777 | "to_csv"
1778 | "grep_v"
1779 | "select_keys"
1780 | "pluck"
1781 | "clamp"
1782 | "normalize"
1783 | "stddev"
1784 | "squared"
1785 | "square"
1786 | "cubed"
1787 | "cube"
1788 | "expt"
1789 | "pow"
1790 | "pw"
1791 | "snake_case"
1792 | "camel_case"
1793 | "kebab_case"
1794 | "to_toml"
1795 | "to_yaml"
1796 | "to_xml"
1797 | "to_html"
1798 | "to_markdown"
1799 | "xopen"
1800 | "clip"
1801 | "paste"
1802 | "to_table"
1803 | "sparkline"
1804 | "bar_chart"
1805 | "flame"
1806 | "set"
1807 | "list_count"
1808 | "list_size"
1809 | "count"
1810 | "size"
1811 | "cnt"
1812 | "len"
1813 | "all"
1814 | "any"
1815 | "none"
1816 | "take_while"
1817 | "drop_while"
1818 | "skip_while"
1819 | "skip"
1820 | "first_or"
1821 | "tap"
1822 | "peek"
1823 | "partition"
1824 | "min_by"
1825 | "max_by"
1826 | "zip_with"
1827 | "group_by"
1828 | "chunk_by"
1829 | "with_index"
1830 | "puniq"
1831 | "pfirst"
1832 | "pany"
1833 | "uniq"
1834 | "distinct"
1835 | "shuffle"
1836 | "shuffled"
1837 | "chunked"
1838 | "windowed"
1839 | "match"
1840 | "mkdir"
1841 | "every"
1842 | "gen"
1843 | "oct"
1844 | "open"
1845 | "p"
1846 | "opendir"
1847 | "ord"
1848 | "par"
1849 | "par_lines"
1850 | "par_walk"
1851 | "pipe"
1852 | "pipes"
1853 | "block_devices"
1854 | "char_devices"
1855 | "exe"
1856 | "executables"
1857 | "rate_limit"
1858 | "retry"
1859 | "pcache"
1860 | "pchannel"
1861 | "pfor"
1862 | "pforeach"
1863 | "pgrep"
1864 | "pgreps"
1865 | "pipeline"
1866 | "pmap_chunked"
1867 | "pmap_reduce"
1868 | "par_reduce"
1869 | "pmap_on"
1870 | "pflat_map_on"
1871 | "pmap"
1872 | "pmaps"
1873 | "pflat_map"
1874 | "pflat_maps"
1875 | "pop"
1876 | "pos"
1877 | "ppool"
1878 | "preduce_init"
1879 | "preduce"
1880 | "pselect"
1881 | "printf"
1882 | "print"
1883 | "pr"
1884 | "psort"
1885 | "push"
1886 | "pwatch"
1887 | "rand"
1888 | "readdir"
1889 | "readlink"
1890 | "reduce"
1891 | "fold"
1892 | "inject"
1893 | "first"
1894 | "detect"
1895 | "find"
1896 | "find_all"
1897 | "find_index"
1898 | "firstidx"
1899 | "first_index"
1900 | "ref"
1901 | "rename"
1902 | "require"
1903 | "rev"
1904 | "reverse"
1905 | "reversed"
1906 | "rewinddir"
1907 | "rindex"
1908 | "rmdir"
1909 | "rm"
1910 | "say"
1911 | "scalar"
1912 | "seekdir"
1913 | "shift"
1914 | "sin"
1915 | "slurp"
1916 | "swallow"
1917 | "ingest"
1918 | "sockets"
1919 | "sort"
1920 | "splice"
1921 | "splice_last"
1922 | "splice1"
1923 | "spl_last"
1924 | "split"
1925 | "sprintf"
1926 | "sqrt"
1927 | "srand"
1928 | "stat"
1929 | "study"
1930 | "substr"
1931 | "symlink"
1932 | "sym_links"
1933 | "system"
1934 | "telldir"
1935 | "timer"
1936 | "trace"
1937 | "ucfirst"
1938 | "uc"
1939 | "undef"
1940 | "umask"
1941 | "unlink"
1942 | "unshift"
1943 | "utime"
1944 | "values"
1945 | "wantarray"
1946 | "warn"
1947 | "watch"
1948 | "yield"
1949 | "sub"
1950 )
1951 }
1952
1953 fn parse_block(&mut self) -> StrykeResult<Block> {
1954 self.expect(&Token::LBrace)?;
1955 let saved_pipe_rhs_depth = self.pipe_rhs_depth;
1958 self.pipe_rhs_depth = 0;
1959 self.block_depth += 1;
1960 let mut stmts = Vec::new();
1961 if let Some(param_stmts) = self.try_parse_block_params()? {
1965 stmts.extend(param_stmts);
1966 }
1967 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
1968 if self.eat(&Token::Semicolon) {
1969 continue;
1970 }
1971 stmts.push(self.parse_statement()?);
1972 }
1973 self.expect(&Token::RBrace)?;
1974 self.pipe_rhs_depth = saved_pipe_rhs_depth;
1975 self.block_depth -= 1;
1976 Self::default_topic_for_sole_bareword(&mut stmts);
1977 Ok(stmts)
1978 }
1979
1980 fn try_parse_block_params(&mut self) -> StrykeResult<Option<Vec<Statement>>> {
1985 if !matches!(self.peek(), Token::BitOr) {
1986 return Ok(None);
1987 }
1988 let mut i = 1; loop {
1991 match self.peek_at(i) {
1992 Token::ScalarVar(_) => i += 1,
1993 _ => return Ok(None), }
1995 match self.peek_at(i) {
1996 Token::BitOr => break, Token::Comma => i += 1, _ => return Ok(None), }
2000 }
2001 let line = self.peek_line();
2003 self.advance(); let mut names = Vec::new();
2005 loop {
2006 if let Token::ScalarVar(ref name) = self.peek().clone() {
2007 names.push(name.clone());
2008 self.advance();
2009 }
2010 if self.eat(&Token::BitOr) {
2011 break;
2012 }
2013 self.expect(&Token::Comma)?;
2014 }
2015 let sources: Vec<&str> = match names.len() {
2020 1 => vec!["_"],
2021 2 => vec!["a", "b"],
2022 n => {
2023 let _ = n;
2025 vec![] }
2027 };
2028 let mut stmts = Vec::with_capacity(names.len());
2029 if !sources.is_empty() {
2030 for (name, src) in names.iter().zip(sources.iter()) {
2031 stmts.push(Statement {
2032 label: None,
2033 kind: StmtKind::My(vec![VarDecl {
2034 sigil: Sigil::Scalar,
2035 name: name.clone(),
2036 initializer: Some(Expr {
2037 kind: ExprKind::ScalarVar(src.to_string()),
2038 line,
2039 }),
2040 frozen: false,
2041 type_annotation: None,
2042 list_context: false,
2043 }]),
2044 line,
2045 });
2046 }
2047 } else {
2048 for (idx, name) in names.iter().enumerate() {
2050 let src = if idx == 0 {
2051 "_".to_string()
2052 } else {
2053 format!("_{idx}")
2054 };
2055 stmts.push(Statement {
2056 label: None,
2057 kind: StmtKind::My(vec![VarDecl {
2058 sigil: Sigil::Scalar,
2059 name: name.clone(),
2060 initializer: Some(Expr {
2061 kind: ExprKind::ScalarVar(src),
2062 line,
2063 }),
2064 frozen: false,
2065 type_annotation: None,
2066 list_context: false,
2067 }]),
2068 line,
2069 });
2070 }
2071 }
2072 Ok(Some(stmts))
2073 }
2074
2075 fn default_topic_for_sole_bareword(stmts: &mut [Statement]) {
2090 let [only] = stmts else { return };
2091 let StmtKind::Expression(ref mut expr) = only.kind else {
2092 return;
2093 };
2094 let topic_line = expr.line;
2095 let topic_arg = || Expr {
2096 kind: ExprKind::ScalarVar("_".to_string()),
2097 line: topic_line,
2098 };
2099 match expr.kind {
2100 ExprKind::FuncCall {
2102 ref name,
2103 ref mut args,
2104 } if args.is_empty()
2105 && (Self::is_known_bareword(name) || Self::is_try_builtin_name(name)) =>
2106 {
2107 args.push(topic_arg());
2108 }
2109 ExprKind::Bareword(ref name)
2113 if (Self::is_known_bareword(name) || Self::is_try_builtin_name(name)) =>
2114 {
2115 let n = name.clone();
2116 expr.kind = ExprKind::FuncCall {
2117 name: n,
2118 args: vec![topic_arg()],
2119 };
2120 }
2121 _ => {}
2122 }
2123 }
2124
2125 fn parse_defer_stmt(&mut self) -> StrykeResult<Statement> {
2129 let line = self.peek_line();
2130 self.advance(); let body = self.parse_block()?;
2132 self.eat(&Token::Semicolon);
2133 let coderef = Expr {
2135 kind: ExprKind::CodeRef {
2136 params: vec![],
2137 body,
2138 },
2139 line,
2140 };
2141 Ok(Statement {
2142 label: None,
2143 kind: StmtKind::Expression(Expr {
2144 kind: ExprKind::FuncCall {
2145 name: "defer__internal".to_string(),
2146 args: vec![coderef],
2147 },
2148 line,
2149 }),
2150 line,
2151 })
2152 }
2153
2154 fn parse_try_catch(&mut self) -> StrykeResult<Statement> {
2156 let line = self.peek_line();
2157 self.advance(); let try_block = self.parse_block()?;
2159 match self.peek() {
2160 Token::Ident(ref k) if k == "catch" => {
2161 self.advance();
2162 }
2163 _ => {
2164 return Err(self.syntax_err("expected 'catch' after try block", self.peek_line()));
2165 }
2166 }
2167 self.expect(&Token::LParen)?;
2168 let catch_var = self.parse_scalar_var_name()?;
2169 self.expect(&Token::RParen)?;
2170 let catch_block = self.parse_block()?;
2171 let finally_block = match self.peek() {
2172 Token::Ident(ref k) if k == "finally" => {
2173 self.advance();
2174 Some(self.parse_block()?)
2175 }
2176 _ => None,
2177 };
2178 self.eat(&Token::Semicolon);
2179 Ok(Statement {
2180 label: None,
2181 kind: StmtKind::TryCatch {
2182 try_block,
2183 catch_var,
2184 catch_block,
2185 finally_block,
2186 },
2187 line,
2188 })
2189 }
2190
2191 fn parse_thread_macro(&mut self, _line: usize, thread_last: bool) -> StrykeResult<Expr> {
2205 self.parse_thread_macro_inner(_line, thread_last, None)
2206 }
2207
2208 fn parse_thread_macro_inner(
2217 &mut self,
2218 _line: usize,
2219 thread_last: bool,
2220 mut parallel_collector: Option<&mut Vec<Expr>>,
2221 ) -> StrykeResult<Expr> {
2222 let saved_thread_last = self.thread_last_mode;
2224 self.thread_last_mode = thread_last;
2225
2226 let pipe_rhs_wrap = self.in_pipe_rhs();
2227 let mut result = if let Some(pre) = self.pending_thread_input.take() {
2231 pre
2232 } else if pipe_rhs_wrap {
2233 Expr {
2234 kind: ExprKind::ArrayElement {
2235 array: "_".to_string(),
2236 index: Box::new(Expr {
2237 kind: ExprKind::Integer(0),
2238 line: _line,
2239 }),
2240 },
2241 line: _line,
2242 }
2243 } else {
2244 self.suppress_parenless_call = self.suppress_parenless_call.saturating_add(1);
2247 let expr = self.parse_thread_input();
2248 self.suppress_parenless_call = self.suppress_parenless_call.saturating_sub(1);
2249 expr?
2250 };
2251 let source_for_par = if parallel_collector.is_some() {
2255 let src = std::mem::replace(
2256 &mut result,
2257 Expr {
2258 kind: ExprKind::ScalarVar("_".into()),
2259 line: _line,
2260 },
2261 );
2262 Some(src)
2263 } else {
2264 None
2265 };
2266
2267 let mut last_stage_end_line = self.prev_line();
2269
2270 loop {
2272 if self.peek_line() > last_stage_end_line {
2277 break;
2278 }
2279
2280 match self.peek() {
2284 Token::Semicolon
2285 | Token::RBrace
2286 | Token::RParen
2287 | Token::RBracket
2288 | Token::PipeForward
2289 | Token::Eof
2290 | Token::ScalarVar(_)
2291 | Token::ArrayVar(_)
2292 | Token::HashVar(_)
2293 | Token::Comma => break,
2294 Token::LogOr if matches!(self.peek_at(1), Token::NumGt) => break,
2299 Token::BitOr
2301 if matches!(self.peek_at(1), Token::Ident(ref n) if n == "then")
2302 && matches!(self.peek_at(2), Token::BitOr) =>
2303 {
2304 break
2305 }
2306 Token::Ident(ref kw)
2307 if matches!(
2308 kw.as_str(),
2309 "my" | "var" | "val" | "our"
2310 | "local"
2311 | "state"
2312 | "if"
2313 | "unless"
2314 | "while"
2315 | "until"
2316 | "for"
2317 | "foreach"
2318 | "return"
2319 | "last"
2320 | "next"
2321 | "redo"
2322 ) =>
2323 {
2324 break
2325 }
2326 _ => {}
2327 }
2328
2329 let stage_line = self.peek_line();
2330
2331 match self.peek().clone() {
2333 Token::ArrowBrace => {
2335 self.advance(); let mut stmts = Vec::new();
2337 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
2338 if self.eat(&Token::Semicolon) {
2339 continue;
2340 }
2341 stmts.push(self.parse_statement()?);
2342 }
2343 self.expect(&Token::RBrace)?;
2344 let code_ref = Expr {
2345 kind: ExprKind::CodeRef {
2346 params: vec![],
2347 body: stmts,
2348 },
2349 line: stage_line,
2350 };
2351 result = self.pipe_forward_apply(result, code_ref, stage_line)?;
2352 }
2353 Token::Ident(ref name) if name == "sub" => {
2355 if crate::no_interop_mode() {
2356 return Err(self.syntax_err(
2357 "stryke uses `fn {}` instead of `sub {}` (--no-interop)",
2358 stage_line,
2359 ));
2360 }
2361 self.advance(); let (params, _prototype) = self.parse_sub_sig_or_prototype_opt()?;
2363 let body = self.parse_block()?;
2364 let code_ref = Expr {
2365 kind: ExprKind::CodeRef { params, body },
2366 line: stage_line,
2367 };
2368 result = self.pipe_forward_apply(result, code_ref, stage_line)?;
2369 }
2370 Token::Ident(ref name) if name == "fn" => {
2372 self.advance(); let (params, _prototype) = self.parse_sub_sig_or_prototype_opt()?;
2374 self.parse_sub_attributes()?;
2375 let body = self.parse_fn_eq_body_or_block(false)?;
2376 let code_ref = Expr {
2377 kind: ExprKind::CodeRef { params, body },
2378 line: stage_line,
2379 };
2380 result = self.pipe_forward_apply(result, code_ref, stage_line)?;
2381 }
2382 Token::Ident(ref name) => {
2384 let mut func_name = name.clone();
2385 self.advance();
2386
2387 while matches!(self.peek(), Token::PackageSep) {
2389 self.advance(); if let Token::Ident(ref part) = self.peek().clone() {
2391 func_name.push_str("::");
2392 func_name.push_str(part);
2393 self.advance();
2394 } else {
2395 return Err(self.syntax_err(
2396 format!(
2397 "Expected identifier after `::` in thread stage, got {:?}",
2398 self.peek()
2399 ),
2400 stage_line,
2401 ));
2402 }
2403 }
2404
2405 if func_name.starts_with('\x00') {
2407 let parts: Vec<&str> = func_name.split('\x00').collect();
2408 if parts.len() >= 4 && parts[1] == "s" {
2409 let delim = parts.get(5).and_then(|s| s.chars().next()).unwrap_or('/');
2410 let stage = Expr {
2411 kind: ExprKind::Substitution {
2412 expr: Box::new(result.clone()),
2413 pattern: parts[2].to_string(),
2414 replacement: parts[3].to_string(),
2415 flags: format!("{}r", parts.get(4).unwrap_or(&"")),
2416 delim,
2417 },
2418 line: stage_line,
2419 };
2420 result = stage;
2421 last_stage_end_line = self.prev_line();
2422 continue;
2423 }
2424 if parts.len() >= 4 && parts[1] == "tr" {
2425 let delim = parts.get(5).and_then(|s| s.chars().next()).unwrap_or('/');
2426 let stage = Expr {
2427 kind: ExprKind::Transliterate {
2428 expr: Box::new(result.clone()),
2429 from: parts[2].to_string(),
2430 to: parts[3].to_string(),
2431 flags: format!("{}r", parts.get(4).unwrap_or(&"")),
2432 delim,
2433 },
2434 line: stage_line,
2435 };
2436 result = stage;
2437 last_stage_end_line = self.prev_line();
2438 continue;
2439 }
2440 return Err(
2441 self.syntax_err("Unexpected encoded token in thread", stage_line)
2442 );
2443 }
2444
2445 if matches!(self.peek(), Token::Plus)
2450 && matches!(self.peek_at(1), Token::LBrace)
2451 {
2452 self.advance(); self.expect(&Token::LBrace)?;
2454 let pairs = self.try_parse_hash_ref()?;
2456 let hashref_expr = Expr {
2457 kind: ExprKind::HashRef(pairs),
2458 line: stage_line,
2459 };
2460 let flatten_array_refs =
2461 matches!(func_name.as_str(), "flat_map" | "flat_maps");
2462 let stream = matches!(func_name.as_str(), "maps" | "flat_maps");
2463 let placeholder = Expr {
2465 kind: ExprKind::Undef,
2466 line: stage_line,
2467 };
2468 let map_node = Expr {
2469 kind: ExprKind::MapExprComma {
2470 expr: Box::new(hashref_expr),
2471 list: Box::new(placeholder),
2472 flatten_array_refs,
2473 stream,
2474 },
2475 line: stage_line,
2476 };
2477 result = self.pipe_forward_apply(result, map_node, stage_line)?;
2478 } else if func_name == "pmap_chunked" {
2480 let chunk_size = self.parse_assign_expr()?;
2481 let block = self.parse_block_or_bareword_block()?;
2482 let placeholder = self.pipe_placeholder_list(stage_line);
2483 let stage = Expr {
2484 kind: ExprKind::PMapChunkedExpr {
2485 chunk_size: Box::new(chunk_size),
2486 block,
2487 list: Box::new(placeholder),
2488 progress: None,
2489 },
2490 line: stage_line,
2491 };
2492 result = self.pipe_forward_apply(result, stage, stage_line)?;
2493 } else if func_name == "preduce_init" {
2495 let init = self.parse_assign_expr()?;
2496 let block = self.parse_block_or_bareword_block()?;
2497 let placeholder = self.pipe_placeholder_list(stage_line);
2498 let stage = Expr {
2499 kind: ExprKind::PReduceInitExpr {
2500 init: Box::new(init),
2501 block,
2502 list: Box::new(placeholder),
2503 progress: None,
2504 },
2505 line: stage_line,
2506 };
2507 result = self.pipe_forward_apply(result, stage, stage_line)?;
2508 } else if func_name == "pmap_reduce" {
2510 let map_block = self.parse_block_or_bareword_block()?;
2511 let reduce_block = if matches!(self.peek(), Token::LBrace) {
2512 self.parse_block()?
2513 } else {
2514 self.expect(&Token::Comma)?;
2515 self.parse_block_or_bareword_cmp_block()?
2516 };
2517 let placeholder = self.pipe_placeholder_list(stage_line);
2518 let stage = Expr {
2519 kind: ExprKind::PMapReduceExpr {
2520 map_block,
2521 reduce_block,
2522 list: Box::new(placeholder),
2523 progress: None,
2524 },
2525 line: stage_line,
2526 };
2527 result = self.pipe_forward_apply(result, stage, stage_line)?;
2528 } else if func_name == "par_reduce" {
2533 let extract_block = self.parse_block_or_bareword_block()?;
2534 let reduce_block = if matches!(self.peek(), Token::LBrace) {
2535 Some(self.parse_block()?)
2536 } else {
2537 None
2538 };
2539 let placeholder = self.pipe_placeholder_list(stage_line);
2540 let stage = Expr {
2541 kind: ExprKind::ParReduceExpr {
2542 extract_block,
2543 reduce_block,
2544 list: Box::new(placeholder),
2545 },
2546 line: stage_line,
2547 };
2548 result = self.pipe_forward_apply(result, stage, stage_line)?;
2549 } else if func_name == "pmap_on" || func_name == "pflat_map_on" {
2554 self.suppress_scalar_hash_brace =
2557 self.suppress_scalar_hash_brace.saturating_add(1);
2558 let cluster = self.parse_assign_expr();
2559 self.suppress_scalar_hash_brace =
2560 self.suppress_scalar_hash_brace.saturating_sub(1);
2561 let cluster = cluster?;
2562 self.eat(&Token::Comma);
2565 let block = self.parse_block_or_bareword_block()?;
2566 let placeholder = self.pipe_placeholder_list(stage_line);
2567 let stage = Expr {
2568 kind: ExprKind::PMapExpr {
2569 block,
2570 list: Box::new(placeholder),
2571 progress: None,
2572 flat_outputs: func_name == "pflat_map_on",
2573 on_cluster: Some(Box::new(cluster)),
2574 stream: false,
2575 },
2576 line: stage_line,
2577 };
2578 result = self.pipe_forward_apply(result, stage, stage_line)?;
2579 } else if matches!(self.peek(), Token::LBrace) {
2581 self.pipe_rhs_depth = self.pipe_rhs_depth.saturating_add(1);
2583 let stage = self.parse_thread_stage_with_block(&func_name, stage_line)?;
2584 self.pipe_rhs_depth = self.pipe_rhs_depth.saturating_sub(1);
2585 result = self.pipe_forward_apply(result, stage, stage_line)?;
2586 } else if matches!(self.peek(), Token::LParen) {
2587 if func_name == "join" {
2590 self.advance(); let separator = self.parse_assign_expr()?;
2592 self.expect(&Token::RParen)?;
2593 let placeholder = self.pipe_placeholder_list(stage_line);
2594 let stage = Expr {
2595 kind: ExprKind::JoinExpr {
2596 separator: Box::new(separator),
2597 list: Box::new(placeholder),
2598 },
2599 line: stage_line,
2600 };
2601 result = self.pipe_forward_apply(result, stage, stage_line)?;
2602 } else if func_name == "split" {
2603 self.advance(); let pattern = self.parse_assign_expr()?;
2605 let limit = if self.eat(&Token::Comma) {
2606 Some(Box::new(self.parse_assign_expr()?))
2607 } else {
2608 None
2609 };
2610 self.expect(&Token::RParen)?;
2611 let placeholder = Expr {
2612 kind: ExprKind::ScalarVar("_".to_string()),
2613 line: stage_line,
2614 };
2615 let stage = Expr {
2616 kind: ExprKind::SplitExpr {
2617 pattern: Box::new(pattern),
2618 string: Box::new(placeholder),
2619 limit,
2620 },
2621 line: stage_line,
2622 };
2623 result = self.pipe_forward_apply(result, stage, stage_line)?;
2624 } else {
2625 self.advance(); let mut call_args = Vec::new();
2636 while !matches!(self.peek(), Token::RParen | Token::Eof) {
2637 call_args.push(self.parse_assign_expr()?);
2638 if !self.eat(&Token::Comma) {
2639 break;
2640 }
2641 }
2642 self.expect(&Token::RParen)?;
2643 if self.thread_last_mode
2656 && !call_args.iter().any(Self::expr_contains_topic_var)
2657 {
2658 call_args.push(result);
2659 result = Expr {
2660 kind: ExprKind::FuncCall {
2661 name: func_name.clone(),
2662 args: call_args,
2663 },
2664 line: stage_line,
2665 };
2666 } else {
2667 if !call_args.iter().any(Self::expr_contains_topic_var) {
2668 let topic = Expr {
2669 kind: ExprKind::ScalarVar("_".to_string()),
2670 line: stage_line,
2671 };
2672 call_args.insert(0, topic);
2673 }
2674 let call_expr = Expr {
2675 kind: ExprKind::FuncCall {
2676 name: func_name.clone(),
2677 args: call_args,
2678 },
2679 line: stage_line,
2680 };
2681 let code_ref = Expr {
2682 kind: ExprKind::CodeRef {
2683 params: vec![],
2684 body: vec![Statement {
2685 label: None,
2686 kind: StmtKind::Expression(call_expr),
2687 line: stage_line,
2688 }],
2689 },
2690 line: stage_line,
2691 };
2692 result = self.pipe_forward_apply(result, code_ref, stage_line)?;
2693 }
2694 }
2695 } else {
2696 result = self.thread_apply_bare_func(&func_name, result, stage_line)?;
2698 }
2699 }
2700 Token::Regex(ref pattern, ref flags, delim) => {
2702 let pattern = pattern.clone();
2703 let flags = flags.clone();
2704 self.advance();
2705 result =
2706 self.thread_regex_grep_stage(result, pattern, flags, delim, stage_line);
2707 }
2708 Token::Slash => {
2711 self.advance(); if let Token::Ident(ref ident_s) = self.peek().clone() {
2717 if matches!(ident_s.as_str(), "m" | "s" | "tr" | "y" | "qr")
2718 && matches!(self.peek_at(1), Token::Regex(..))
2719 {
2720 self.advance(); if let Token::Regex(ref misparsed_pattern, ref misparsed_flags, _) =
2727 self.peek().clone()
2728 {
2729 let _ = (misparsed_pattern, misparsed_flags);
2739 }
2740 }
2741 }
2742
2743 let mut pattern = String::new();
2745 loop {
2746 match self.peek().clone() {
2747 Token::Slash => {
2748 self.advance(); break;
2750 }
2751 Token::Eof | Token::Semicolon | Token::Newline => {
2752 return Err(self
2753 .syntax_err("Unterminated regex in thread stage", stage_line));
2754 }
2755 Token::Regex(ref inner_pattern, ref inner_flags, delim) => {
2757 if pattern.is_empty()
2765 || matches!(pattern.as_str(), "m" | "s" | "tr" | "y" | "qr")
2766 {
2767 let _ = (inner_pattern, inner_flags, delim);
2773 }
2774 return Err(self.syntax_err(
2776 "Complex regex in thread stage - use m/pattern/ syntax instead",
2777 stage_line,
2778 ));
2779 }
2780 Token::Ident(ref s) => {
2781 pattern.push_str(s);
2782 self.advance();
2783 }
2784 Token::Integer(n) => {
2785 pattern.push_str(&n.to_string());
2786 self.advance();
2787 }
2788 Token::ScalarVar(ref v) => {
2789 pattern.push('$');
2790 pattern.push_str(v);
2791 self.advance();
2792 }
2793 Token::Dot => {
2794 pattern.push('.');
2795 self.advance();
2796 }
2797 Token::Star => {
2798 pattern.push('*');
2799 self.advance();
2800 }
2801 Token::Plus => {
2802 pattern.push('+');
2803 self.advance();
2804 }
2805 Token::Question => {
2806 pattern.push('?');
2807 self.advance();
2808 }
2809 Token::LParen => {
2810 pattern.push('(');
2811 self.advance();
2812 }
2813 Token::RParen => {
2814 pattern.push(')');
2815 self.advance();
2816 }
2817 Token::LBracket => {
2818 pattern.push('[');
2819 self.advance();
2820 }
2821 Token::RBracket => {
2822 pattern.push(']');
2823 self.advance();
2824 }
2825 Token::Backslash => {
2826 pattern.push('\\');
2827 self.advance();
2828 }
2829 Token::BitOr => {
2830 pattern.push('|');
2831 self.advance();
2832 }
2833 Token::Power => {
2834 pattern.push_str("**");
2835 self.advance();
2836 }
2837 Token::BitXor => {
2838 pattern.push('^');
2839 self.advance();
2840 }
2841 Token::Minus => {
2842 pattern.push('-');
2843 self.advance();
2844 }
2845 _ => {
2846 return Err(self.syntax_err(
2847 format!("Unexpected token in regex pattern: {:?}", self.peek()),
2848 stage_line,
2849 ));
2850 }
2851 }
2852 }
2853 let mut flags = String::new();
2857 if let Token::Ident(ref s) = self.peek().clone() {
2858 let is_flag_only =
2859 s.chars().all(|c| "gimsxecor".contains(c)) && s.len() <= 6;
2860 let followed_by_brace = matches!(self.peek_at(1), Token::LBrace);
2861 if is_flag_only && !followed_by_brace {
2862 flags.push_str(s);
2863 self.advance();
2864 }
2865 }
2866 result = self.thread_regex_grep_stage(result, pattern, flags, '/', stage_line);
2867 }
2868 tok => {
2869 return Err(self.syntax_err(
2870 format!(
2871 "thread: expected stage (ident, fn {{}}, s///, tr///, or /re/), got {:?}",
2872 tok
2873 ),
2874 stage_line,
2875 ));
2876 }
2877 };
2878 last_stage_end_line = self.prev_line();
2879 if let Some(stages) = parallel_collector.as_mut() {
2884 let stage_body = std::mem::replace(
2885 &mut result,
2886 Expr {
2887 kind: ExprKind::ScalarVar("_".into()),
2888 line: stage_line,
2889 },
2890 );
2891 stages.push(stage_body);
2892 }
2893 }
2894
2895 self.thread_last_mode = saved_thread_last;
2897
2898 if let Some(stages) = parallel_collector {
2904 let source_expr = source_for_par.unwrap_or(result);
2905 if stages.is_empty() {
2906 return Err(self.syntax_err(
2907 "~p> / ~p>> require at least one stage after the source",
2908 _line,
2909 ));
2910 }
2911 let stage_closures: Vec<Expr> = stages
2917 .drain(..)
2918 .map(|body| {
2919 let body_line = body.line;
2920 let wrapped = Expr {
2921 kind: ExprKind::ArrayRef(vec![body]),
2922 line: body_line,
2923 };
2924 Expr {
2925 kind: ExprKind::CodeRef {
2926 params: vec![],
2927 body: vec![Statement {
2928 label: None,
2929 kind: StmtKind::Expression(wrapped),
2930 line: body_line,
2931 }],
2932 },
2933 line: body_line,
2934 }
2935 })
2936 .collect();
2937 let stages_arr = Expr {
2938 kind: ExprKind::ArrayRef(stage_closures),
2939 line: _line,
2940 };
2941 let thread_last_flag = Expr {
2942 kind: ExprKind::Integer(if thread_last { 1 } else { 0 }),
2943 line: _line,
2944 };
2945 return Ok(Expr {
2952 kind: ExprKind::FuncCall {
2953 name: "_thread_par_run".into(),
2954 args: vec![stages_arr, thread_last_flag, source_expr],
2955 },
2956 line: _line,
2957 });
2958 }
2959
2960 if pipe_rhs_wrap {
2961 let body_line = result.line;
2964 return Ok(Expr {
2965 kind: ExprKind::CodeRef {
2966 params: vec![],
2967 body: vec![Statement {
2968 label: None,
2969 kind: StmtKind::Expression(result),
2970 line: body_line,
2971 }],
2972 },
2973 line: _line,
2974 });
2975 }
2976 Ok(result)
2977 }
2978
2979 fn thread_regex_grep_stage(
2981 &self,
2982 list: Expr,
2983 pattern: String,
2984 flags: String,
2985 delim: char,
2986 line: usize,
2987 ) -> Expr {
2988 let topic = Expr {
2989 kind: ExprKind::ScalarVar("_".to_string()),
2990 line,
2991 };
2992 let match_expr = Expr {
2993 kind: ExprKind::Match {
2994 expr: Box::new(topic),
2995 pattern,
2996 flags,
2997 scalar_g: false,
2998 delim,
2999 },
3000 line,
3001 };
3002 let block = vec![Statement {
3003 label: None,
3004 kind: StmtKind::Expression(match_expr),
3005 line,
3006 }];
3007 Expr {
3008 kind: ExprKind::GrepExpr {
3009 block,
3010 list: Box::new(list),
3011 keyword: crate::ast::GrepBuiltinKeyword::Grep,
3012 },
3013 line,
3014 }
3015 }
3016
3017 fn expr_contains_topic_var(e: &Expr) -> bool {
3028 format!("{:?}", e).contains("ScalarVar(\"_\")")
3029 }
3030
3031 fn rhs_has_free_bare_topic_slot(&self, rhs_start: usize, rhs_end: usize) -> bool {
3047 let end = rhs_end.min(self.tokens.len());
3048 if rhs_start < end && Self::is_thread_arrow(&self.tokens[rhs_start].0) {
3049 let input = rhs_start + 1;
3053 return input < end && self.bare_positional_indices.contains(&input);
3054 }
3055 let mut brace_depth = 0i32;
3056 for i in rhs_start..end {
3057 if brace_depth == 0 && self.bare_positional_indices.contains(&i) {
3058 return true;
3059 }
3060 match &self.tokens[i].0 {
3061 Token::LBrace | Token::ArrowBrace => brace_depth += 1,
3062 Token::RBrace => brace_depth -= 1,
3063 _ => {}
3064 }
3065 }
3066 false
3067 }
3068
3069 fn is_thread_arrow(tok: &Token) -> bool {
3070 matches!(
3071 tok,
3072 Token::ThreadArrow
3073 | Token::ThreadArrowLast
3074 | Token::ThreadArrowStream
3075 | Token::ThreadArrowStreamLast
3076 | Token::ThreadArrowPar
3077 | Token::ThreadArrowParLast
3078 | Token::ThreadArrowDist
3079 | Token::ThreadArrowDistLast
3080 )
3081 }
3082
3083 fn thread_apply_bare_func(&self, name: &str, arg: Expr, line: usize) -> StrykeResult<Expr> {
3085 let kind = match name {
3086 "uc" => ExprKind::Uc(Box::new(arg)),
3088 "lc" => ExprKind::Lc(Box::new(arg)),
3089 "ucfirst" | "ufc" => ExprKind::Ucfirst(Box::new(arg)),
3090 "lcfirst" | "lfc" => ExprKind::Lcfirst(Box::new(arg)),
3091 "fc" => ExprKind::Fc(Box::new(arg)),
3092 "chomp" => ExprKind::Chomp(Box::new(arg)),
3093 "chop" => ExprKind::Chop(Box::new(arg)),
3094 "length" => ExprKind::Length(Box::new(arg)),
3095 "len" | "cnt" => ExprKind::FuncCall {
3096 name: "count".to_string(),
3097 args: vec![arg],
3098 },
3099 "quotemeta" | "qm" => ExprKind::Quotemeta(Box::new(arg)),
3100 "abs" => ExprKind::Abs(Box::new(arg)),
3102 "int" => ExprKind::Int(Box::new(arg)),
3103 "sqrt" | "sq" => ExprKind::Sqrt(Box::new(arg)),
3104 "sin" => ExprKind::Sin(Box::new(arg)),
3105 "cos" => ExprKind::Cos(Box::new(arg)),
3106 "exp" => ExprKind::Exp(Box::new(arg)),
3107 "log" => ExprKind::Log(Box::new(arg)),
3108 "hex" => ExprKind::Hex(Box::new(arg)),
3109 "oct" => ExprKind::Oct(Box::new(arg)),
3110 "chr" => ExprKind::Chr(Box::new(arg)),
3111 "ord" => ExprKind::Ord(Box::new(arg)),
3112 "rand" => ExprKind::Rand(Some(Box::new(arg))),
3113 "srand" => ExprKind::Srand(Some(Box::new(arg))),
3114 "defined" | "def" => ExprKind::Defined(Box::new(arg)),
3116 "ref" => ExprKind::Ref(Box::new(arg)),
3117 "scalar" => {
3118 if crate::no_interop_mode() {
3119 return Err(self.syntax_err(
3120 "stryke uses `len` (also `cnt` / `count`) instead of `scalar` (--no-interop)",
3121 line,
3122 ));
3123 }
3124 ExprKind::ScalarContext(Box::new(arg))
3125 }
3126 "keys" => ExprKind::Keys(Box::new(arg)),
3128 "values" => ExprKind::Values(Box::new(arg)),
3129 "each" => ExprKind::Each(Box::new(arg)),
3130 "pop" => ExprKind::Pop(Box::new(arg)),
3131 "shift" => ExprKind::Shift(Box::new(arg)),
3132 "reverse" => {
3133 if crate::no_interop_mode() {
3134 return Err(self.syntax_err(
3135 "stryke uses `rev` instead of `reverse` (--no-interop)",
3136 line,
3137 ));
3138 }
3139 ExprKind::ReverseExpr(Box::new(arg))
3140 }
3141 "reversed" | "rv" | "rev" => ExprKind::Rev(Box::new(arg)),
3142 "sort" | "so" => ExprKind::SortExpr {
3143 cmp: None,
3144 list: Box::new(arg),
3145 },
3146 "psort" => ExprKind::PSortExpr {
3147 cmp: None,
3148 list: Box::new(arg),
3149 progress: None,
3150 },
3151 "uniq" | "distinct" | "uq" => ExprKind::FuncCall {
3152 name: "uniq".to_string(),
3153 args: vec![arg],
3154 },
3155 "trim" | "tm" => ExprKind::FuncCall {
3156 name: "trim".to_string(),
3157 args: vec![arg],
3158 },
3159 "flatten" | "fl" => ExprKind::FuncCall {
3160 name: "flatten".to_string(),
3161 args: vec![arg],
3162 },
3163 "compact" | "cpt" => ExprKind::FuncCall {
3164 name: "compact".to_string(),
3165 args: vec![arg],
3166 },
3167 "shuffle" | "shuf" => ExprKind::FuncCall {
3168 name: "shuffle".to_string(),
3169 args: vec![arg],
3170 },
3171 "frequencies" | "freq" | "frq" => ExprKind::FuncCall {
3172 name: "frequencies".to_string(),
3173 args: vec![arg],
3174 },
3175 "pfrequencies" | "pfreq" | "pfrq" => ExprKind::FuncCall {
3176 name: "pfrequencies".to_string(),
3177 args: vec![arg],
3178 },
3179 "dedup" | "dup" => ExprKind::FuncCall {
3180 name: "dedup".to_string(),
3181 args: vec![arg],
3182 },
3183 "enumerate" | "en" => ExprKind::FuncCall {
3184 name: "enumerate".to_string(),
3185 args: vec![arg],
3186 },
3187 "lines" | "ln" => ExprKind::FuncCall {
3188 name: "lines".to_string(),
3189 args: vec![arg],
3190 },
3191 "words" | "wd" => ExprKind::FuncCall {
3192 name: "words".to_string(),
3193 args: vec![arg],
3194 },
3195 "chars" | "ch" => ExprKind::FuncCall {
3196 name: "chars".to_string(),
3197 args: vec![arg],
3198 },
3199 "digits" | "dg" => ExprKind::FuncCall {
3200 name: "digits".to_string(),
3201 args: vec![arg],
3202 },
3203 "letters" | "lts" => ExprKind::FuncCall {
3204 name: "letters".to_string(),
3205 args: vec![arg],
3206 },
3207 "letters_uc" => ExprKind::FuncCall {
3208 name: "letters_uc".to_string(),
3209 args: vec![arg],
3210 },
3211 "letters_lc" => ExprKind::FuncCall {
3212 name: "letters_lc".to_string(),
3213 args: vec![arg],
3214 },
3215 "punctuation" | "punct" => ExprKind::FuncCall {
3216 name: "punctuation".to_string(),
3217 args: vec![arg],
3218 },
3219 "sentences" | "sents" => ExprKind::FuncCall {
3220 name: "sentences".to_string(),
3221 args: vec![arg],
3222 },
3223 "paragraphs" | "paras" => ExprKind::FuncCall {
3224 name: "paragraphs".to_string(),
3225 args: vec![arg],
3226 },
3227 "sections" | "sects" => ExprKind::FuncCall {
3228 name: "sections".to_string(),
3229 args: vec![arg],
3230 },
3231 "numbers" | "nums" => ExprKind::FuncCall {
3232 name: "numbers".to_string(),
3233 args: vec![arg],
3234 },
3235 "graphemes" | "grs" => ExprKind::FuncCall {
3236 name: "graphemes".to_string(),
3237 args: vec![arg],
3238 },
3239 "columns" | "cols" => ExprKind::FuncCall {
3240 name: "columns".to_string(),
3241 args: vec![arg],
3242 },
3243 "slurp" | "sl" => ExprKind::Slurp(Box::new(arg)),
3245 "swallow" | "swa" => ExprKind::Swallow(Box::new(arg)),
3246 "ingest" | "ing" => ExprKind::Ingest(Box::new(arg)),
3247 "burp" => ExprKind::Burp(Box::new(arg)),
3248 "god" => ExprKind::God(Box::new(arg)),
3249 "glob" => ExprKind::Glob(vec![arg]),
3250 "chdir" => ExprKind::Chdir(Box::new(arg)),
3251 "stat" => ExprKind::Stat(Box::new(arg)),
3252 "lstat" => ExprKind::Lstat(Box::new(arg)),
3253 "readlink" => ExprKind::Readlink(Box::new(arg)),
3254 "readdir" => ExprKind::Readdir(Box::new(arg)),
3255 "close" => ExprKind::Close(Box::new(arg)),
3256 "basename" | "bn" => ExprKind::FuncCall {
3257 name: "basename".to_string(),
3258 args: vec![arg],
3259 },
3260 "dirname" | "dn" => ExprKind::FuncCall {
3261 name: "dirname".to_string(),
3262 args: vec![arg],
3263 },
3264 "realpath" | "rp" => ExprKind::FuncCall {
3265 name: "realpath".to_string(),
3266 args: vec![arg],
3267 },
3268 "which" | "wh" => ExprKind::FuncCall {
3269 name: "which".to_string(),
3270 args: vec![arg],
3271 },
3272 "eval" => ExprKind::Eval(Box::new(arg)),
3274 "require" => ExprKind::Require(Box::new(arg)),
3275 "study" => ExprKind::Study(Box::new(arg)),
3276 "snake_case" | "sc" => ExprKind::FuncCall {
3278 name: "snake_case".to_string(),
3279 args: vec![arg],
3280 },
3281 "camel_case" | "cc" => ExprKind::FuncCall {
3282 name: "camel_case".to_string(),
3283 args: vec![arg],
3284 },
3285 "kebab_case" | "kc" => ExprKind::FuncCall {
3286 name: "kebab_case".to_string(),
3287 args: vec![arg],
3288 },
3289 "to_json" | "tj" => ExprKind::FuncCall {
3291 name: "to_json".to_string(),
3292 args: vec![arg],
3293 },
3294 "to_yaml" | "ty" => ExprKind::FuncCall {
3295 name: "to_yaml".to_string(),
3296 args: vec![arg],
3297 },
3298 "to_toml" | "tt" => ExprKind::FuncCall {
3299 name: "to_toml".to_string(),
3300 args: vec![arg],
3301 },
3302 "to_csv" | "tc" => ExprKind::FuncCall {
3303 name: "to_csv".to_string(),
3304 args: vec![arg],
3305 },
3306 "to_xml" | "tx" => ExprKind::FuncCall {
3307 name: "to_xml".to_string(),
3308 args: vec![arg],
3309 },
3310 "to_html" | "th" => ExprKind::FuncCall {
3311 name: "to_html".to_string(),
3312 args: vec![arg],
3313 },
3314 "to_markdown" | "to_md" | "tmd" => ExprKind::FuncCall {
3315 name: "to_markdown".to_string(),
3316 args: vec![arg],
3317 },
3318 "xopen" | "xo" => ExprKind::FuncCall {
3319 name: "xopen".to_string(),
3320 args: vec![arg],
3321 },
3322 "clip" | "clipboard" | "pbcopy" => ExprKind::FuncCall {
3323 name: "clip".to_string(),
3324 args: vec![arg],
3325 },
3326 "to_table" | "table" | "tbl" => ExprKind::FuncCall {
3327 name: "to_table".to_string(),
3328 args: vec![arg],
3329 },
3330 "sparkline" | "spark" => ExprKind::FuncCall {
3331 name: "sparkline".to_string(),
3332 args: vec![arg],
3333 },
3334 "bar_chart" | "bars" => ExprKind::FuncCall {
3335 name: "bar_chart".to_string(),
3336 args: vec![arg],
3337 },
3338 "flame" | "flamechart" => ExprKind::FuncCall {
3339 name: "flame".to_string(),
3340 args: vec![arg],
3341 },
3342 "ddump" | "dd" => ExprKind::FuncCall {
3343 name: "ddump".to_string(),
3344 args: vec![arg],
3345 },
3346 "say" => {
3347 if crate::no_interop_mode() {
3348 return Err(
3349 self.syntax_err("stryke uses `p` instead of `say` (--no-interop)", line)
3350 );
3351 }
3352 ExprKind::Say {
3353 handle: None,
3354 args: vec![arg],
3355 }
3356 }
3357 "p" => ExprKind::Say {
3358 handle: None,
3359 args: vec![arg],
3360 },
3361 "print" => ExprKind::Print {
3362 handle: None,
3363 args: vec![arg],
3364 },
3365 "warn" => ExprKind::Warn(vec![arg]),
3366 "die" => ExprKind::Die(vec![arg]),
3367 "stringify" | "str" => ExprKind::FuncCall {
3368 name: "stringify".to_string(),
3369 args: vec![arg],
3370 },
3371 "json_decode" | "jd" => ExprKind::FuncCall {
3372 name: "json_decode".to_string(),
3373 args: vec![arg],
3374 },
3375 "yaml_decode" | "yd" => ExprKind::FuncCall {
3376 name: "yaml_decode".to_string(),
3377 args: vec![arg],
3378 },
3379 "toml_decode" | "td" => ExprKind::FuncCall {
3380 name: "toml_decode".to_string(),
3381 args: vec![arg],
3382 },
3383 "xml_decode" | "xd" => ExprKind::FuncCall {
3384 name: "xml_decode".to_string(),
3385 args: vec![arg],
3386 },
3387 "json_encode" | "je" => ExprKind::FuncCall {
3388 name: "json_encode".to_string(),
3389 args: vec![arg],
3390 },
3391 "yaml_encode" | "ye" => ExprKind::FuncCall {
3392 name: "yaml_encode".to_string(),
3393 args: vec![arg],
3394 },
3395 "toml_encode" | "te" => ExprKind::FuncCall {
3396 name: "toml_encode".to_string(),
3397 args: vec![arg],
3398 },
3399 "xml_encode" | "xe" => ExprKind::FuncCall {
3400 name: "xml_encode".to_string(),
3401 args: vec![arg],
3402 },
3403 "base64_encode" | "b64e" => ExprKind::FuncCall {
3405 name: "base64_encode".to_string(),
3406 args: vec![arg],
3407 },
3408 "base64_decode" | "b64d" => ExprKind::FuncCall {
3409 name: "base64_decode".to_string(),
3410 args: vec![arg],
3411 },
3412 "hex_encode" | "hxe" => ExprKind::FuncCall {
3413 name: "hex_encode".to_string(),
3414 args: vec![arg],
3415 },
3416 "hex_decode" | "hxd" => ExprKind::FuncCall {
3417 name: "hex_decode".to_string(),
3418 args: vec![arg],
3419 },
3420 "url_encode" | "uri_escape" | "ue" => ExprKind::FuncCall {
3421 name: "url_encode".to_string(),
3422 args: vec![arg],
3423 },
3424 "url_decode" | "uri_unescape" | "ud" => ExprKind::FuncCall {
3425 name: "url_decode".to_string(),
3426 args: vec![arg],
3427 },
3428 "gzip" | "gz" => ExprKind::FuncCall {
3429 name: "gzip".to_string(),
3430 args: vec![arg],
3431 },
3432 "gunzip" | "ugz" => ExprKind::FuncCall {
3433 name: "gunzip".to_string(),
3434 args: vec![arg],
3435 },
3436 "zstd" | "zst" => ExprKind::FuncCall {
3437 name: "zstd".to_string(),
3438 args: vec![arg],
3439 },
3440 "zstd_decode" | "uzst" => ExprKind::FuncCall {
3441 name: "zstd_decode".to_string(),
3442 args: vec![arg],
3443 },
3444 "sha256" | "s256" => ExprKind::FuncCall {
3446 name: "sha256".to_string(),
3447 args: vec![arg],
3448 },
3449 "sha1" | "s1" => ExprKind::FuncCall {
3450 name: "sha1".to_string(),
3451 args: vec![arg],
3452 },
3453 "md5" | "m5" => ExprKind::FuncCall {
3454 name: "md5".to_string(),
3455 args: vec![arg],
3456 },
3457 "uuid" | "uid" => ExprKind::FuncCall {
3458 name: "uuid".to_string(),
3459 args: vec![arg],
3460 },
3461 "datetime_utc" | "utc" => ExprKind::FuncCall {
3463 name: "datetime_utc".to_string(),
3464 args: vec![arg],
3465 },
3466 "e" | "fore" | "ep" => ExprKind::ForEachExpr {
3469 block: vec![Statement {
3470 label: None,
3471 kind: StmtKind::Expression(Expr {
3472 kind: ExprKind::Say {
3473 handle: None,
3474 args: vec![Expr {
3475 kind: ExprKind::ScalarVar("_".into()),
3476 line,
3477 }],
3478 },
3479 line,
3480 }),
3481 line,
3482 }],
3483 list: Box::new(arg),
3484 },
3485 _ => ExprKind::FuncCall {
3487 name: name.to_string(),
3488 args: vec![arg],
3489 },
3490 };
3491 Ok(Expr { kind, line })
3492 }
3493
3494 fn parse_thread_stage_with_block(&mut self, name: &str, line: usize) -> StrykeResult<Expr> {
3497 let block = self.parse_block()?;
3498 let placeholder = self.pipe_placeholder_list(line);
3500
3501 match name {
3502 "map" | "flat_map" | "maps" | "flat_maps" => {
3503 let flatten_array_refs = matches!(name, "flat_map" | "flat_maps");
3504 let stream = matches!(name, "maps" | "flat_maps");
3505 Ok(Expr {
3506 kind: ExprKind::MapExpr {
3507 block,
3508 list: Box::new(placeholder),
3509 flatten_array_refs,
3510 stream,
3511 },
3512 line,
3513 })
3514 }
3515 "grep" | "greps" | "filter" | "fi" | "find_all" | "gr" => {
3516 let keyword = match name {
3517 "grep" | "gr" => crate::ast::GrepBuiltinKeyword::Grep,
3518 "greps" => crate::ast::GrepBuiltinKeyword::Greps,
3519 "filter" | "fi" => crate::ast::GrepBuiltinKeyword::Filter,
3520 "find_all" => crate::ast::GrepBuiltinKeyword::FindAll,
3521 _ => unreachable!(),
3522 };
3523 Ok(Expr {
3524 kind: ExprKind::GrepExpr {
3525 block,
3526 list: Box::new(placeholder),
3527 keyword,
3528 },
3529 line,
3530 })
3531 }
3532 "sort" | "so" => Ok(Expr {
3533 kind: ExprKind::SortExpr {
3534 cmp: Some(SortComparator::Block(block)),
3535 list: Box::new(placeholder),
3536 },
3537 line,
3538 }),
3539 "reduce" | "rd" => Ok(Expr {
3540 kind: ExprKind::ReduceExpr {
3541 block,
3542 list: Box::new(placeholder),
3543 },
3544 line,
3545 }),
3546 "fore" | "e" | "ep" => Ok(Expr {
3547 kind: ExprKind::ForEachExpr {
3548 block,
3549 list: Box::new(placeholder),
3550 },
3551 line,
3552 }),
3553 "par" => Ok(Expr {
3554 kind: ExprKind::ParExpr {
3555 block,
3556 list: Box::new(placeholder),
3557 },
3558 line,
3559 }),
3560 "pmap" | "pflat_map" | "pmaps" | "pflat_maps" => Ok(Expr {
3561 kind: ExprKind::PMapExpr {
3562 block,
3563 list: Box::new(placeholder),
3564 progress: None,
3565 flat_outputs: name == "pflat_map" || name == "pflat_maps",
3566 on_cluster: None,
3567 stream: name == "pmaps" || name == "pflat_maps",
3568 },
3569 line,
3570 }),
3571 "pgrep" | "pgreps" => Ok(Expr {
3572 kind: ExprKind::PGrepExpr {
3573 block,
3574 list: Box::new(placeholder),
3575 progress: None,
3576 stream: name == "pgreps",
3577 },
3578 line,
3579 }),
3580 "pfor" | "pforeach" => Ok(Expr {
3581 kind: ExprKind::PForExpr {
3582 block,
3583 list: Box::new(placeholder),
3584 progress: None,
3585 },
3586 line,
3587 }),
3588 "preduce" => Ok(Expr {
3589 kind: ExprKind::PReduceExpr {
3590 block,
3591 list: Box::new(placeholder),
3592 progress: None,
3593 },
3594 line,
3595 }),
3596 "pcache" => Ok(Expr {
3597 kind: ExprKind::PcacheExpr {
3598 block,
3599 list: Box::new(placeholder),
3600 progress: None,
3601 },
3602 line,
3603 }),
3604 "psort" => Ok(Expr {
3605 kind: ExprKind::PSortExpr {
3606 cmp: Some(block),
3607 list: Box::new(placeholder),
3608 progress: None,
3609 },
3610 line,
3611 }),
3612 _ => {
3613 let code_ref = Expr {
3620 kind: ExprKind::CodeRef {
3621 params: vec![],
3622 body: block,
3623 },
3624 line,
3625 };
3626 let args = if Self::is_block_then_list_pipe_builtin(name) {
3627 vec![code_ref, placeholder]
3628 } else {
3629 vec![code_ref]
3630 };
3631 Ok(Expr {
3632 kind: ExprKind::FuncCall {
3633 name: name.to_string(),
3634 args,
3635 },
3636 line,
3637 })
3638 }
3639 }
3640 }
3641
3642 fn parse_tie_stmt(&mut self) -> StrykeResult<Statement> {
3644 let line = self.peek_line();
3645 self.advance(); let mut implicit_decl: Option<Statement> = None;
3652 if let Token::Ident(kw) = self.peek().clone() {
3653 if is_lexical_or_our_decl(&kw) {
3654 let kw_line = self.peek_line();
3655 self.advance(); let (decl_sigil, decl_name) = match self.peek().clone() {
3658 Token::ScalarVar(s) => (Sigil::Scalar, s),
3659 Token::ArrayVar(a) => (Sigil::Array, a),
3660 Token::HashVar(h) => (Sigil::Hash, h),
3661 tok => {
3662 return Err(self.syntax_err(
3663 format!("expected variable after `tie {}`, got {:?}", kw, tok),
3664 self.peek_line(),
3665 ));
3666 }
3667 };
3668 let decls = vec![VarDecl {
3669 sigil: decl_sigil,
3670 name: decl_name.clone(),
3671 initializer: None,
3672 frozen: is_frozen_decl(&kw),
3675 type_annotation: None,
3676 list_context: false,
3677 }];
3678 implicit_decl = Some(Statement {
3679 label: None,
3680 kind: if is_lexical_decl(&kw) {
3681 StmtKind::My(decls)
3682 } else {
3683 StmtKind::Our(decls)
3684 },
3685 line: kw_line,
3686 });
3687 }
3692 }
3693 let target = match self.peek().clone() {
3694 Token::HashVar(h) => {
3695 self.advance();
3696 TieTarget::Hash(h)
3697 }
3698 Token::ArrayVar(a) => {
3699 self.advance();
3700 TieTarget::Array(a)
3701 }
3702 Token::ScalarVar(s) => {
3703 self.advance();
3704 TieTarget::Scalar(s)
3705 }
3706 tok => {
3707 return Err(self.syntax_err(
3708 format!("tie expects $scalar, @array, or %hash, got {:?}", tok),
3709 self.peek_line(),
3710 ));
3711 }
3712 };
3713 self.expect(&Token::Comma)?;
3714 let class = self.parse_assign_expr()?;
3715 let mut args = Vec::new();
3716 while self.eat(&Token::Comma) {
3717 if matches!(self.peek(), Token::Semicolon | Token::RBrace | Token::Eof) {
3718 break;
3719 }
3720 args.push(self.parse_assign_expr()?);
3721 }
3722 self.eat(&Token::Semicolon);
3723 let tie_stmt = Statement {
3724 label: None,
3725 kind: StmtKind::Tie {
3726 target,
3727 class,
3728 args,
3729 },
3730 line,
3731 };
3732 if let Some(decl) = implicit_decl {
3733 Ok(Statement {
3738 label: None,
3739 kind: StmtKind::StmtGroup(vec![decl, tie_stmt]),
3740 line,
3741 })
3742 } else {
3743 Ok(tie_stmt)
3744 }
3745 }
3746
3747 fn parse_given(&mut self) -> StrykeResult<Statement> {
3749 let line = self.peek_line();
3750 self.advance();
3751 self.expect(&Token::LParen)?;
3752 let topic = self.parse_expression()?;
3753 self.expect(&Token::RParen)?;
3754 let body = self.parse_block()?;
3755 self.eat(&Token::Semicolon);
3756 Ok(Statement {
3757 label: None,
3758 kind: StmtKind::Given { topic, body },
3759 line,
3760 })
3761 }
3762
3763 fn parse_when_stmt(&mut self) -> StrykeResult<Statement> {
3765 let line = self.peek_line();
3766 self.advance();
3767 self.expect(&Token::LParen)?;
3768 let cond = self.parse_expression()?;
3769 self.expect(&Token::RParen)?;
3770 let body = self.parse_block()?;
3771 self.eat(&Token::Semicolon);
3772 Ok(Statement {
3773 label: None,
3774 kind: StmtKind::When { cond, body },
3775 line,
3776 })
3777 }
3778
3779 fn parse_default_stmt(&mut self) -> StrykeResult<Statement> {
3781 let line = self.peek_line();
3782 self.advance();
3783 let body = self.parse_block()?;
3784 self.eat(&Token::Semicolon);
3785 Ok(Statement {
3786 label: None,
3787 kind: StmtKind::DefaultCase { body },
3788 line,
3789 })
3790 }
3791
3792 fn parse_cond_expr(&mut self, line: usize) -> StrykeResult<Expr> {
3798 self.expect(&Token::LBrace)?;
3799
3800 let mut arms: Vec<(Expr, Block)> = Vec::new();
3801 let mut else_block: Option<Block> = None;
3802
3803 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
3804 let arm_line = self.peek_line();
3805
3806 let is_default = matches!(self.peek(), Token::Ident(ref s) if s == "default")
3808 && matches!(self.peek_at(1), Token::FatArrow);
3809
3810 if is_default {
3811 self.advance(); self.advance(); let body = if matches!(self.peek(), Token::LBrace) {
3814 self.parse_block()?
3815 } else {
3816 let expr = self.parse_assign_expr()?;
3817 vec![Statement {
3818 label: None,
3819 kind: StmtKind::Expression(expr),
3820 line: arm_line,
3821 }]
3822 };
3823 else_block = Some(body);
3824 self.eat(&Token::Comma);
3825 break; }
3827
3828 let condition = self.parse_assign_expr()?;
3830 self.expect(&Token::FatArrow)?;
3831
3832 let body = if matches!(self.peek(), Token::LBrace) {
3833 self.parse_block()?
3834 } else {
3835 let expr = self.parse_assign_expr()?;
3836 vec![Statement {
3837 label: None,
3838 kind: StmtKind::Expression(expr),
3839 line: arm_line,
3840 }]
3841 };
3842
3843 arms.push((condition, body));
3844 self.eat(&Token::Comma);
3845 }
3846
3847 self.expect(&Token::RBrace)?;
3848
3849 if arms.is_empty() {
3850 return Err(self.syntax_err("cond requires at least one condition arm", line));
3851 }
3852
3853 let (first_cond, first_body) = arms.remove(0);
3855 let elsifs: Vec<(Expr, Block)> = arms;
3856
3857 let if_stmt = Statement {
3859 label: None,
3860 kind: StmtKind::If {
3861 condition: first_cond,
3862 body: first_body,
3863 elsifs,
3864 else_block,
3865 },
3866 line,
3867 };
3868 let inner = Expr {
3869 kind: ExprKind::CodeRef {
3870 params: vec![],
3871 body: vec![if_stmt],
3872 },
3873 line,
3874 };
3875 Ok(Expr {
3876 kind: ExprKind::Do(Box::new(inner)),
3877 line,
3878 })
3879 }
3880
3881 fn parse_algebraic_match_expr(&mut self, line: usize) -> StrykeResult<Expr> {
3883 self.expect(&Token::LParen)?;
3884 let subject = self.parse_expression()?;
3885 self.expect(&Token::RParen)?;
3886 self.expect(&Token::LBrace)?;
3887 let mut arms = Vec::new();
3888 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
3889 if self.eat(&Token::Semicolon) {
3890 continue;
3891 }
3892 let pattern = self.parse_match_pattern()?;
3893 let guard = if matches!(self.peek(), Token::Ident(ref s) if s == "if") {
3894 self.advance();
3895 Some(Box::new(self.parse_assign_expr()?))
3898 } else {
3899 None
3900 };
3901 self.expect(&Token::FatArrow)?;
3902 let body = self.parse_assign_expr()?;
3904 arms.push(MatchArm {
3905 pattern,
3906 guard,
3907 body,
3908 });
3909 self.eat(&Token::Comma);
3910 }
3911 self.expect(&Token::RBrace)?;
3912 Ok(Expr {
3913 kind: ExprKind::AlgebraicMatch {
3914 subject: Box::new(subject),
3915 arms,
3916 },
3917 line,
3918 })
3919 }
3920
3921 fn parse_match_pattern(&mut self) -> StrykeResult<MatchPattern> {
3922 match self.peek().clone() {
3923 Token::Regex(pattern, flags, _delim) => {
3924 self.advance();
3925 Ok(MatchPattern::Regex { pattern, flags })
3926 }
3927 Token::Ident(ref s) if s == "_" => {
3928 self.advance();
3929 Ok(MatchPattern::Any)
3930 }
3931 Token::Ident(ref s) if s == "Some" => {
3932 self.advance();
3933 self.expect(&Token::LParen)?;
3934 let name = self.parse_scalar_var_name()?;
3935 self.expect(&Token::RParen)?;
3936 Ok(MatchPattern::OptionSome(name))
3937 }
3938 Token::LBracket => self.parse_match_array_pattern(),
3939 Token::LBrace => self.parse_match_hash_pattern(),
3940 Token::LParen => {
3941 self.advance();
3942 let e = self.parse_expression()?;
3943 self.expect(&Token::RParen)?;
3944 Ok(MatchPattern::Value(Box::new(e)))
3945 }
3946 _ => {
3947 let e = self.parse_assign_expr()?;
3948 Ok(MatchPattern::Value(Box::new(e)))
3949 }
3950 }
3951 }
3952
3953 fn parse_match_array_elems_until_rbracket(&mut self) -> StrykeResult<Vec<MatchArrayElem>> {
3955 let mut elems = Vec::new();
3956 if self.eat(&Token::RBracket) {
3957 return Ok(vec![]);
3958 }
3959 loop {
3960 if matches!(self.peek(), Token::Star) {
3961 self.advance();
3962 elems.push(MatchArrayElem::Rest);
3963 self.eat(&Token::Comma);
3964 if !matches!(self.peek(), Token::RBracket) {
3965 return Err(self.syntax_err(
3966 "`*` must be the last element in an array match pattern",
3967 self.peek_line(),
3968 ));
3969 }
3970 self.expect(&Token::RBracket)?;
3971 return Ok(elems);
3972 }
3973 if let Token::ArrayVar(name) = self.peek().clone() {
3974 self.advance();
3975 elems.push(MatchArrayElem::RestBind(name));
3976 self.eat(&Token::Comma);
3977 if !matches!(self.peek(), Token::RBracket) {
3978 return Err(self.syntax_err(
3979 "`@name` rest bind must be the last element in an array match pattern",
3980 self.peek_line(),
3981 ));
3982 }
3983 self.expect(&Token::RBracket)?;
3984 return Ok(elems);
3985 }
3986 if let Token::ScalarVar(name) = self.peek().clone() {
3987 self.advance();
3988 elems.push(MatchArrayElem::CaptureScalar(name));
3989 if self.eat(&Token::Comma) {
3990 if matches!(self.peek(), Token::RBracket) {
3991 break;
3992 }
3993 continue;
3994 }
3995 break;
3996 }
3997 let e = self.parse_assign_expr()?;
3998 elems.push(MatchArrayElem::Expr(e));
3999 if self.eat(&Token::Comma) {
4000 if matches!(self.peek(), Token::RBracket) {
4001 break;
4002 }
4003 continue;
4004 }
4005 break;
4006 }
4007 self.expect(&Token::RBracket)?;
4008 Ok(elems)
4009 }
4010
4011 fn parse_match_array_pattern(&mut self) -> StrykeResult<MatchPattern> {
4012 self.expect(&Token::LBracket)?;
4013 let elems = self.parse_match_array_elems_until_rbracket()?;
4014 Ok(MatchPattern::Array(elems))
4015 }
4016
4017 fn parse_match_hash_pattern(&mut self) -> StrykeResult<MatchPattern> {
4018 self.expect(&Token::LBrace)?;
4019 let mut pairs = Vec::new();
4020 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
4021 if self.eat(&Token::Semicolon) {
4022 continue;
4023 }
4024 let key = self.parse_assign_expr()?;
4025 self.expect(&Token::FatArrow)?;
4026 match self.advance().0 {
4027 Token::Ident(ref s) if s == "_" => {
4028 pairs.push(MatchHashPair::KeyOnly { key });
4029 }
4030 Token::ScalarVar(name) => {
4031 pairs.push(MatchHashPair::Capture { key, name });
4032 }
4033 tok => {
4034 return Err(self.syntax_err(
4035 format!(
4036 "hash match pattern must bind with `=> $name` or `=> _`, got {:?}",
4037 tok
4038 ),
4039 self.peek_line(),
4040 ));
4041 }
4042 }
4043 self.eat(&Token::Comma);
4044 }
4045 self.expect(&Token::RBrace)?;
4046 Ok(MatchPattern::Hash(pairs))
4047 }
4048
4049 fn parse_eval_timeout(&mut self) -> StrykeResult<Statement> {
4051 let line = self.peek_line();
4052 self.advance();
4053 let timeout = self.parse_postfix()?;
4054 let body = self.parse_block_or_bareword_block_no_args()?;
4055 self.eat(&Token::Semicolon);
4056 Ok(Statement {
4057 label: None,
4058 kind: StmtKind::EvalTimeout { timeout, body },
4059 line,
4060 })
4061 }
4062
4063 fn mark_match_scalar_g_for_boolean_condition(cond: &mut Expr) {
4064 match &mut cond.kind {
4065 ExprKind::Match {
4066 flags, scalar_g, ..
4067 } if flags.contains('g') => {
4068 *scalar_g = true;
4069 }
4070 ExprKind::UnaryOp {
4071 op: UnaryOp::LogNot,
4072 expr,
4073 } => {
4074 if let ExprKind::Match {
4075 flags, scalar_g, ..
4076 } = &mut expr.kind
4077 {
4078 if flags.contains('g') {
4079 *scalar_g = true;
4080 }
4081 }
4082 }
4083 _ => {}
4084 }
4085 }
4086
4087 fn parse_if(&mut self) -> StrykeResult<Statement> {
4088 let line = self.peek_line();
4089 self.advance(); if matches!(self.peek(), Token::Ident(ref s) if s == "let") {
4091 if crate::compat_mode() {
4092 return Err(self.syntax_err(
4093 "`if let` is a stryke extension (disabled by --compat)",
4094 line,
4095 ));
4096 }
4097 return self.parse_if_let(line);
4098 }
4099 self.expect(&Token::LParen)?;
4100 let mut cond = self.parse_expression()?;
4101 Self::mark_match_scalar_g_for_boolean_condition(&mut cond);
4102 self.expect(&Token::RParen)?;
4103 let body = self.parse_block()?;
4104
4105 let mut elsifs = Vec::new();
4106 let mut else_block = None;
4107
4108 loop {
4109 if let Token::Ident(ref kw) = self.peek().clone() {
4110 if kw == "elsif" {
4111 self.advance();
4112 self.expect(&Token::LParen)?;
4113 let mut c = self.parse_expression()?;
4114 Self::mark_match_scalar_g_for_boolean_condition(&mut c);
4115 self.expect(&Token::RParen)?;
4116 let b = self.parse_block()?;
4117 elsifs.push((c, b));
4118 continue;
4119 }
4120 if kw == "else" {
4121 self.advance();
4122 else_block = Some(self.parse_block()?);
4123 }
4124 }
4125 break;
4126 }
4127
4128 Ok(Statement {
4129 label: None,
4130 kind: StmtKind::If {
4131 condition: cond,
4132 body,
4133 elsifs,
4134 else_block,
4135 },
4136 line,
4137 })
4138 }
4139
4140 fn parse_if_let(&mut self, line: usize) -> StrykeResult<Statement> {
4142 self.advance(); let pattern = self.parse_match_pattern()?;
4144 self.expect(&Token::Assign)?;
4145 self.suppress_scalar_hash_brace = self.suppress_scalar_hash_brace.saturating_add(1);
4147 let rhs = self.parse_assign_expr();
4148 self.suppress_scalar_hash_brace = self.suppress_scalar_hash_brace.saturating_sub(1);
4149 let rhs = rhs?;
4150 let then_block = self.parse_block()?;
4151 let else_block_opt = match self.peek().clone() {
4152 Token::Ident(ref kw) if kw == "else" => {
4153 self.advance();
4154 Some(self.parse_block()?)
4155 }
4156 Token::Ident(ref kw) if kw == "elsif" => {
4157 return Err(self.syntax_err(
4158 "`if let` does not support `elsif`; use `else { }` or a full `match`",
4159 self.peek_line(),
4160 ));
4161 }
4162 _ => None,
4163 };
4164 let then_expr = Self::expr_do_anon_block(then_block, line);
4165 let else_expr = if let Some(eb) = else_block_opt {
4166 Self::expr_do_anon_block(eb, line)
4167 } else {
4168 Expr {
4169 kind: ExprKind::Undef,
4170 line,
4171 }
4172 };
4173 let arms = vec![
4174 MatchArm {
4175 pattern,
4176 guard: None,
4177 body: then_expr,
4178 },
4179 MatchArm {
4180 pattern: MatchPattern::Any,
4181 guard: None,
4182 body: else_expr,
4183 },
4184 ];
4185 Ok(Statement {
4186 label: None,
4187 kind: StmtKind::Expression(Expr {
4188 kind: ExprKind::AlgebraicMatch {
4189 subject: Box::new(rhs),
4190 arms,
4191 },
4192 line,
4193 }),
4194 line,
4195 })
4196 }
4197
4198 fn expr_do_anon_block(block: Block, outer_line: usize) -> Expr {
4199 let inner_line = block.first().map(|s| s.line).unwrap_or(outer_line);
4200 Expr {
4201 kind: ExprKind::Do(Box::new(Expr {
4202 kind: ExprKind::CodeRef {
4203 params: vec![],
4204 body: block,
4205 },
4206 line: inner_line,
4207 })),
4208 line: outer_line,
4209 }
4210 }
4211
4212 fn parse_unless(&mut self) -> StrykeResult<Statement> {
4213 let line = self.peek_line();
4214 self.advance(); self.expect(&Token::LParen)?;
4216 let mut cond = self.parse_expression()?;
4217 Self::mark_match_scalar_g_for_boolean_condition(&mut cond);
4218 self.expect(&Token::RParen)?;
4219 let body = self.parse_block()?;
4220 let else_block = if let Token::Ident(ref kw) = self.peek().clone() {
4221 if kw == "else" {
4222 self.advance();
4223 Some(self.parse_block()?)
4224 } else {
4225 None
4226 }
4227 } else {
4228 None
4229 };
4230 Ok(Statement {
4231 label: None,
4232 kind: StmtKind::Unless {
4233 condition: cond,
4234 body,
4235 else_block,
4236 },
4237 line,
4238 })
4239 }
4240
4241 fn parse_loop(&mut self) -> StrykeResult<Statement> {
4247 let line = self.peek_line();
4248 self.advance(); let body = self.parse_block()?;
4250 let continue_block = self.parse_optional_continue_block()?;
4251 Ok(Statement {
4252 label: None,
4253 kind: StmtKind::While {
4254 condition: Expr {
4255 kind: ExprKind::Integer(1),
4256 line,
4257 },
4258 body,
4259 label: None,
4260 continue_block,
4261 },
4262 line,
4263 })
4264 }
4265
4266 fn parse_ploop(&mut self) -> StrykeResult<Statement> {
4273 let line = self.peek_line();
4274 self.advance(); let count = if matches!(self.peek(), Token::LBrace) {
4276 None
4277 } else {
4278 Some(self.parse_primary()?)
4281 };
4282 let body = self.parse_block()?;
4283 let continue_block = self.parse_optional_continue_block()?;
4284 let condition = Expr {
4285 kind: ExprKind::Integer(1),
4286 line,
4287 };
4288 self.parallel_worker_loop_stmt(line, count, condition, body, continue_block)
4289 }
4290
4291 fn parse_pwhile(&mut self) -> StrykeResult<Statement> {
4298 let line = self.peek_line();
4299 self.advance(); let count = if matches!(self.peek(), Token::LParen) {
4301 None
4302 } else {
4303 Some(self.parse_primary()?)
4306 };
4307 self.expect(&Token::LParen)?;
4308 let mut condition = self.parse_expression()?;
4309 Self::mark_match_scalar_g_for_boolean_condition(&mut condition);
4310 self.expect(&Token::RParen)?;
4311 let body = self.parse_block()?;
4312 let continue_block = self.parse_optional_continue_block()?;
4313 self.parallel_worker_loop_stmt(line, count, condition, body, continue_block)
4314 }
4315
4316 fn parallel_worker_loop_stmt(
4321 &mut self,
4322 line: usize,
4323 count: Option<Expr>,
4324 condition: Expr,
4325 body: Block,
4326 continue_block: Option<Block>,
4327 ) -> StrykeResult<Statement> {
4328 let worker_loop = Statement {
4329 label: None,
4330 kind: StmtKind::While {
4331 condition,
4332 body,
4333 label: None,
4334 continue_block,
4335 },
4336 line,
4337 };
4338 let list = match count {
4339 Some(n) => Expr {
4340 kind: ExprKind::Range {
4341 from: Box::new(Expr {
4342 kind: ExprKind::Integer(1),
4343 line,
4344 }),
4345 to: Box::new(n),
4346 exclusive: false,
4347 step: None,
4348 },
4349 line,
4350 },
4351 None => parse_expression_from_str("1..thread_count()", "<ploop>")?,
4352 };
4353 Ok(Statement {
4354 label: None,
4355 kind: StmtKind::Expression(Expr {
4356 kind: ExprKind::PForExpr {
4357 block: vec![worker_loop],
4358 list: Box::new(list),
4359 progress: None,
4360 },
4361 line,
4362 }),
4363 line,
4364 })
4365 }
4366
4367 fn parse_while(&mut self) -> StrykeResult<Statement> {
4368 let line = self.peek_line();
4369 self.advance(); if matches!(self.peek(), Token::Ident(ref s) if s == "let") {
4371 if crate::compat_mode() {
4372 return Err(self.syntax_err(
4373 "`while let` is a stryke extension (disabled by --compat)",
4374 line,
4375 ));
4376 }
4377 return self.parse_while_let(line);
4378 }
4379 self.expect(&Token::LParen)?;
4380 let mut cond = self.parse_expression()?;
4381 Self::mark_match_scalar_g_for_boolean_condition(&mut cond);
4382 self.expect(&Token::RParen)?;
4383 let body = self.parse_block()?;
4384 let continue_block = self.parse_optional_continue_block()?;
4385 Ok(Statement {
4386 label: None,
4387 kind: StmtKind::While {
4388 condition: cond,
4389 body,
4390 label: None,
4391 continue_block,
4392 },
4393 line,
4394 })
4395 }
4396
4397 fn parse_while_let(&mut self, line: usize) -> StrykeResult<Statement> {
4400 self.advance(); let pattern = self.parse_match_pattern()?;
4402 self.expect(&Token::Assign)?;
4403 self.suppress_scalar_hash_brace = self.suppress_scalar_hash_brace.saturating_add(1);
4404 let rhs = self.parse_assign_expr();
4405 self.suppress_scalar_hash_brace = self.suppress_scalar_hash_brace.saturating_sub(1);
4406 let rhs = rhs?;
4407 let mut user_body = self.parse_block()?;
4408 let continue_block = self.parse_optional_continue_block()?;
4409 user_body.push(Statement::new(
4410 StmtKind::Expression(Expr {
4411 kind: ExprKind::Integer(1),
4412 line,
4413 }),
4414 line,
4415 ));
4416 let tmp = format!("__while_let_{}", self.alloc_desugar_tmp());
4417 let match_expr = Expr {
4418 kind: ExprKind::AlgebraicMatch {
4419 subject: Box::new(rhs),
4420 arms: vec![
4421 MatchArm {
4422 pattern,
4423 guard: None,
4424 body: Self::expr_do_anon_block(user_body, line),
4425 },
4426 MatchArm {
4427 pattern: MatchPattern::Any,
4428 guard: None,
4429 body: Expr {
4430 kind: ExprKind::Integer(0),
4431 line,
4432 },
4433 },
4434 ],
4435 },
4436 line,
4437 };
4438 let my_stmt = Statement::new(
4439 StmtKind::My(vec![VarDecl {
4440 sigil: Sigil::Scalar,
4441 name: tmp.clone(),
4442 initializer: Some(match_expr),
4443 frozen: false,
4444 type_annotation: None,
4445 list_context: false,
4446 }]),
4447 line,
4448 );
4449 let unless_last = Statement::new(
4450 StmtKind::Unless {
4451 condition: Expr {
4452 kind: ExprKind::ScalarVar(tmp),
4453 line,
4454 },
4455 body: vec![Statement::new(StmtKind::Last(None), line)],
4456 else_block: None,
4457 },
4458 line,
4459 );
4460 Ok(Statement::new(
4461 StmtKind::While {
4462 condition: Expr {
4463 kind: ExprKind::Integer(1),
4464 line,
4465 },
4466 body: vec![my_stmt, unless_last],
4467 label: None,
4468 continue_block,
4469 },
4470 line,
4471 ))
4472 }
4473
4474 fn parse_until(&mut self) -> StrykeResult<Statement> {
4475 let line = self.peek_line();
4476 self.advance(); self.expect(&Token::LParen)?;
4478 let mut cond = self.parse_expression()?;
4479 Self::mark_match_scalar_g_for_boolean_condition(&mut cond);
4480 self.expect(&Token::RParen)?;
4481 let body = self.parse_block()?;
4482 let continue_block = self.parse_optional_continue_block()?;
4483 Ok(Statement {
4484 label: None,
4485 kind: StmtKind::Until {
4486 condition: cond,
4487 body,
4488 label: None,
4489 continue_block,
4490 },
4491 line,
4492 })
4493 }
4494
4495 fn parse_optional_continue_block(&mut self) -> StrykeResult<Option<Block>> {
4497 if let Token::Ident(ref kw) = self.peek().clone() {
4498 if kw == "continue" {
4499 self.advance();
4500 return Ok(Some(self.parse_block()?));
4501 }
4502 }
4503 Ok(None)
4504 }
4505
4506 fn parse_for_or_foreach(&mut self) -> StrykeResult<Statement> {
4507 let line = self.peek_line();
4508 self.advance(); match self.peek() {
4514 Token::LParen => {
4515 let saved = self.pos;
4520 self.advance(); let mut depth = 1;
4523 let mut has_semi = false;
4524 let mut scan = self.pos;
4525 while scan < self.tokens.len() {
4526 match &self.tokens[scan].0 {
4527 Token::LParen => depth += 1,
4528 Token::RParen => {
4529 depth -= 1;
4530 if depth == 0 {
4531 break;
4532 }
4533 }
4534 Token::Semicolon if depth == 1 => {
4535 has_semi = true;
4536 break;
4537 }
4538 _ => {}
4539 }
4540 scan += 1;
4541 }
4542 self.pos = saved;
4543
4544 if has_semi {
4545 self.parse_c_style_for(line)
4546 } else {
4547 self.expect(&Token::LParen)?;
4549 let list = self.parse_expression()?;
4550 self.expect(&Token::RParen)?;
4551 let body = self.parse_block()?;
4552 let continue_block = self.parse_optional_continue_block()?;
4553 Ok(Statement {
4554 label: None,
4555 kind: StmtKind::Foreach {
4556 var: "_".to_string(),
4557 list,
4558 body,
4559 label: None,
4560 continue_block,
4561 },
4562 line,
4563 })
4564 }
4565 }
4566 Token::Ident(ref kw)
4576 if is_lexical_decl(kw) && matches!(self.peek_at(1), Token::ScalarVar(_)) =>
4577 {
4578 self.advance(); let var = self.parse_scalar_var_name()?;
4580 self.expect(&Token::LParen)?;
4581 let list = self.parse_expression()?;
4582 self.expect(&Token::RParen)?;
4583 let body = self.parse_block()?;
4584 let continue_block = self.parse_optional_continue_block()?;
4585 Ok(Statement {
4586 label: None,
4587 kind: StmtKind::Foreach {
4588 var,
4589 list,
4590 body,
4591 label: None,
4592 continue_block,
4593 },
4594 line,
4595 })
4596 }
4597 Token::ScalarVar(_) => {
4598 let var = self.parse_scalar_var_name()?;
4599 self.expect(&Token::LParen)?;
4600 let list = self.parse_expression()?;
4601 self.expect(&Token::RParen)?;
4602 let body = self.parse_block()?;
4603 let continue_block = self.parse_optional_continue_block()?;
4604 Ok(Statement {
4605 label: None,
4606 kind: StmtKind::Foreach {
4607 var,
4608 list,
4609 body,
4610 label: None,
4611 continue_block,
4612 },
4613 line,
4614 })
4615 }
4616 _ => self.parse_c_style_for(line),
4617 }
4618 }
4619
4620 fn parse_c_style_for(&mut self, line: usize) -> StrykeResult<Statement> {
4621 self.expect(&Token::LParen)?;
4622 let init = if self.eat(&Token::Semicolon) {
4623 None
4624 } else {
4625 let s = self.parse_statement()?;
4626 self.eat(&Token::Semicolon);
4627 Some(Box::new(s))
4628 };
4629 let mut condition = if matches!(self.peek(), Token::Semicolon) {
4630 None
4631 } else {
4632 Some(self.parse_expression()?)
4633 };
4634 if let Some(ref mut c) = condition {
4635 Self::mark_match_scalar_g_for_boolean_condition(c);
4636 }
4637 self.expect(&Token::Semicolon)?;
4638 let step = if matches!(self.peek(), Token::RParen) {
4639 None
4640 } else {
4641 Some(self.parse_expression()?)
4642 };
4643 self.expect(&Token::RParen)?;
4644 let body = self.parse_block()?;
4645 let continue_block = self.parse_optional_continue_block()?;
4646 Ok(Statement {
4647 label: None,
4648 kind: StmtKind::For {
4649 init,
4650 condition,
4651 step,
4652 body,
4653 label: None,
4654 continue_block,
4655 },
4656 line,
4657 })
4658 }
4659
4660 fn parse_foreach(&mut self) -> StrykeResult<Statement> {
4661 let line = self.peek_line();
4662 self.advance(); let var = match self.peek() {
4664 Token::Ident(ref kw)
4669 if is_lexical_decl(kw) && matches!(self.peek_at(1), Token::ScalarVar(_)) =>
4670 {
4671 self.advance();
4672 self.parse_scalar_var_name()?
4673 }
4674 Token::ScalarVar(_) => self.parse_scalar_var_name()?,
4675 _ => "_".to_string(),
4676 };
4677 self.expect(&Token::LParen)?;
4678 let list = self.parse_expression()?;
4679 self.expect(&Token::RParen)?;
4680 let body = self.parse_block()?;
4681 let continue_block = self.parse_optional_continue_block()?;
4682 Ok(Statement {
4683 label: None,
4684 kind: StmtKind::Foreach {
4685 var,
4686 list,
4687 body,
4688 label: None,
4689 continue_block,
4690 },
4691 line,
4692 })
4693 }
4694
4695 fn parse_scalar_var_name(&mut self) -> StrykeResult<String> {
4696 match self.advance() {
4697 (Token::ScalarVar(name), _) => Ok(name),
4698 (tok, line) => {
4699 Err(self.syntax_err(format!("Expected scalar variable, got {:?}", tok), line))
4700 }
4701 }
4702 }
4703
4704 fn parse_legacy_sub_prototype_tail(&mut self) -> StrykeResult<String> {
4706 let mut s = String::new();
4707 loop {
4708 match self.peek().clone() {
4709 Token::RParen => {
4710 self.advance();
4711 break;
4712 }
4713 Token::Eof => {
4714 return Err(self.syntax_err(
4715 "Unterminated sub prototype (expected ')' before end of input)",
4716 self.peek_line(),
4717 ));
4718 }
4719 Token::ScalarVar(v) if v == ")" => {
4720 self.advance();
4723 s.push('$');
4724 if matches!(self.peek(), Token::LBrace) {
4725 break;
4726 }
4727 }
4728 Token::Ident(i) => {
4729 let i = i.clone();
4730 self.advance();
4731 s.push_str(&i);
4732 }
4733 Token::Semicolon => {
4734 self.advance();
4735 s.push(';');
4736 }
4737 Token::LParen => {
4738 self.advance();
4739 s.push('(');
4740 }
4741 Token::LBracket => {
4742 self.advance();
4743 s.push('[');
4744 }
4745 Token::RBracket => {
4746 self.advance();
4747 s.push(']');
4748 }
4749 Token::Backslash => {
4750 self.advance();
4751 s.push('\\');
4752 }
4753 Token::Comma => {
4754 self.advance();
4755 s.push(',');
4756 }
4757 Token::ScalarVar(v) => {
4758 let v = v.clone();
4759 self.advance();
4760 s.push('$');
4761 s.push_str(&v);
4762 }
4763 Token::ArrayVar(v) => {
4764 let v = v.clone();
4765 self.advance();
4766 s.push('@');
4767 s.push_str(&v);
4768 }
4769 Token::ArrayAt => {
4771 self.advance();
4772 s.push('@');
4773 }
4774 Token::HashVar(v) => {
4775 let v = v.clone();
4776 self.advance();
4777 s.push('%');
4778 s.push_str(&v);
4779 }
4780 Token::HashPercent => {
4781 self.advance();
4782 s.push('%');
4783 }
4784 Token::Percent => {
4788 self.advance();
4789 s.push('%');
4790 }
4791 Token::Star => {
4794 self.advance();
4795 s.push('*');
4796 }
4797 Token::Plus => {
4798 self.advance();
4799 s.push('+');
4800 }
4801 Token::Minus => {
4802 self.advance();
4803 s.push('-');
4804 }
4805 Token::BitAnd => {
4806 self.advance();
4807 s.push('&');
4808 }
4809 tok => {
4810 return Err(self.syntax_err(
4811 format!("Unexpected token in sub prototype: {:?}", tok),
4812 self.peek_line(),
4813 ));
4814 }
4815 }
4816 }
4817 Ok(s)
4818 }
4819
4820 fn sub_signature_list_starts_here(&self) -> bool {
4821 fn is_ident_name(name: &str) -> bool {
4829 let mut chars = name.chars();
4830 match chars.next() {
4831 Some(c) if c.is_ascii_alphabetic() || c == '_' => {
4832 chars.all(|c| c.is_ascii_alphanumeric() || c == '_')
4833 }
4834 _ => false,
4835 }
4836 }
4837 match self.peek() {
4838 Token::LBrace | Token::LBracket => true,
4839 Token::ScalarVar(name) => is_ident_name(name),
4840 Token::ArrayVar(name) | Token::HashVar(name) => is_ident_name(name),
4841 _ => false,
4842 }
4843 }
4844
4845 fn parse_sub_signature_hash_key(&mut self) -> StrykeResult<String> {
4846 let (tok, line) = self.advance();
4847 match tok {
4848 Token::Ident(i) => Ok(i),
4849 Token::SingleString(s) | Token::DoubleString(s) => Ok(s),
4850 tok => Err(self.syntax_err(
4851 format!(
4852 "sub signature: expected hash key (identifier or string), got {:?}",
4853 tok
4854 ),
4855 line,
4856 )),
4857 }
4858 }
4859
4860 fn parse_sub_signature_param_list(&mut self) -> StrykeResult<Vec<SubSigParam>> {
4861 let mut params = Vec::new();
4862 loop {
4863 if matches!(self.peek(), Token::RParen) {
4864 break;
4865 }
4866 match self.peek().clone() {
4867 Token::ScalarVar(name) => {
4868 if name == "$$" || name == ")" {
4869 return Err(self.syntax_err(
4870 format!(
4871 "`{name}` cannot start a stryke sub signature (use legacy prototype `($$)` etc.)"
4872 ),
4873 self.peek_line(),
4874 ));
4875 }
4876 self.advance();
4877 let ty = if self.eat(&Token::Colon) {
4878 match self.peek() {
4879 Token::Ident(ref tname) => {
4880 let tname = tname.clone();
4881 self.advance();
4882 Some(match tname.as_str() {
4883 "Int" => PerlTypeName::Int,
4884 "Str" => PerlTypeName::Str,
4885 "Float" => PerlTypeName::Float,
4886 "Bool" => PerlTypeName::Bool,
4887 "Array" => PerlTypeName::Array,
4888 "Hash" => PerlTypeName::Hash,
4889 "Ref" => PerlTypeName::Ref,
4890 "Any" => PerlTypeName::Any,
4891 _ => PerlTypeName::Struct(tname),
4892 })
4893 }
4894 _ => {
4895 return Err(self.syntax_err(
4896 "expected type name after `:` in sub signature",
4897 self.peek_line(),
4898 ));
4899 }
4900 }
4901 } else {
4902 None
4903 };
4904 let default = if self.eat(&Token::Assign) {
4906 Some(Box::new(self.parse_ternary()?))
4907 } else {
4908 None
4909 };
4910 params.push(SubSigParam::Scalar(name, ty, default));
4911 }
4912 Token::ArrayVar(name) => {
4913 self.advance();
4914 let default = if self.eat(&Token::Assign) {
4915 Some(Box::new(self.parse_ternary()?))
4916 } else {
4917 None
4918 };
4919 params.push(SubSigParam::Array(name, default));
4920 }
4921 Token::HashVar(name) => {
4922 self.advance();
4923 let default = if self.eat(&Token::Assign) {
4924 Some(Box::new(self.parse_ternary()?))
4925 } else {
4926 None
4927 };
4928 params.push(SubSigParam::Hash(name, default));
4929 }
4930 Token::LBracket => {
4931 self.advance();
4932 let elems = self.parse_match_array_elems_until_rbracket()?;
4933 params.push(SubSigParam::ArrayDestruct(elems));
4934 }
4935 Token::LBrace => {
4936 self.advance();
4937 let mut pairs = Vec::new();
4938 loop {
4939 if matches!(self.peek(), Token::RBrace | Token::Eof) {
4940 break;
4941 }
4942 if self.eat(&Token::Comma) {
4943 continue;
4944 }
4945 let key = self.parse_sub_signature_hash_key()?;
4946 self.expect(&Token::FatArrow)?;
4947 let bind = self.parse_scalar_var_name()?;
4948 pairs.push((key, bind));
4949 self.eat(&Token::Comma);
4950 }
4951 self.expect(&Token::RBrace)?;
4952 params.push(SubSigParam::HashDestruct(pairs));
4953 }
4954 tok => {
4955 return Err(self.syntax_err(
4956 format!(
4957 "expected `$name`, `[ ... ]`, or `{{ ... }}` in sub signature, got {:?}",
4958 tok
4959 ),
4960 self.peek_line(),
4961 ));
4962 }
4963 }
4964 match self.peek() {
4965 Token::Comma => {
4966 self.advance();
4967 if matches!(self.peek(), Token::RParen) {
4968 return Err(self.syntax_err(
4969 "trailing `,` before `)` in sub signature",
4970 self.peek_line(),
4971 ));
4972 }
4973 }
4974 Token::RParen => break,
4975 _ => {
4976 return Err(self.syntax_err(
4977 format!(
4978 "expected `,` or `)` after sub signature parameter, got {:?}",
4979 self.peek()
4980 ),
4981 self.peek_line(),
4982 ));
4983 }
4984 }
4985 }
4986 Ok(params)
4987 }
4988
4989 fn parse_sub_sig_or_prototype_opt(
4991 &mut self,
4992 ) -> StrykeResult<(Vec<SubSigParam>, Option<String>)> {
4993 if !matches!(self.peek(), Token::LParen) {
4994 return Ok((vec![], None));
4995 }
4996 self.advance();
4997 if matches!(self.peek(), Token::RParen) {
4998 self.advance();
4999 return Ok((vec![], Some(String::new())));
5000 }
5001 if self.sub_signature_list_starts_here() {
5002 let params = self.parse_sub_signature_param_list()?;
5003 self.expect(&Token::RParen)?;
5004 return Ok((params, None));
5005 }
5006 let proto = self.parse_legacy_sub_prototype_tail()?;
5007 Ok((vec![], Some(proto)))
5008 }
5009
5010 fn parse_sub_attributes(&mut self) -> StrykeResult<()> {
5012 while self.eat(&Token::Colon) {
5013 match self.advance() {
5014 (Token::Ident(_), _) => {}
5015 (tok, line) => {
5016 return Err(self.syntax_err(
5017 format!("Expected attribute name after `:`, got {:?}", tok),
5018 line,
5019 ));
5020 }
5021 }
5022 if self.eat(&Token::LParen) {
5023 let mut depth = 1usize;
5024 while depth > 0 {
5025 match self.advance().0 {
5026 Token::LParen => depth += 1,
5027 Token::RParen => {
5028 depth -= 1;
5029 }
5030 Token::Eof => {
5031 return Err(self.syntax_err(
5032 "Unterminated sub attribute argument list",
5033 self.peek_line(),
5034 ));
5035 }
5036 _ => {}
5037 }
5038 }
5039 }
5040 }
5041 Ok(())
5042 }
5043
5044 fn try_parse_fn_assign_shorthand_body(&mut self) -> StrykeResult<Option<Block>> {
5047 if !self.eat(&Token::Assign) {
5048 return Ok(None);
5049 }
5050 let expr = self.parse_assign_expr()?;
5051 if matches!(self.peek(), Token::Comma) {
5052 return Err(self.syntax_err(
5053 "`fn ... =` allows only a single expression; use `fn ... { ... }` for multiple statements",
5054 self.peek_line(),
5055 ));
5056 }
5057 let eline = expr.line;
5058 self.eat(&Token::Semicolon);
5059 let mut body = vec![Statement {
5060 label: None,
5061 kind: StmtKind::Expression(expr),
5062 line: eline,
5063 }];
5064 Self::default_topic_for_sole_bareword(&mut body);
5065 Ok(Some(body))
5066 }
5067
5068 fn parse_fn_eq_body_or_block(&mut self, is_sub_keyword: bool) -> StrykeResult<Block> {
5071 if !is_sub_keyword {
5072 if let Some(block) = self.try_parse_fn_assign_shorthand_body()? {
5073 return Ok(block);
5074 }
5075 }
5076 self.parse_block()
5077 }
5078
5079 fn parse_sub_decl(&mut self, is_sub_keyword: bool) -> StrykeResult<Statement> {
5080 let line = self.peek_line();
5081 self.advance(); match self.peek().clone() {
5083 Token::Ident(_) => {
5084 let name = self.parse_package_qualified_identifier()?;
5085 let bare = name.rsplit("::").next().unwrap_or(&name);
5094 if Self::is_underscore_topic_slot(bare) {
5095 return Err(self.syntax_err(
5096 format!(
5097 "`fn {}` would shadow the topic-slot scalar; pick a different name",
5098 name
5099 ),
5100 line,
5101 ));
5102 }
5103 if Self::is_reserved_special_var_name(bare) {
5104 return Err(self.syntax_err(
5105 format!(
5106 "`fn {}` would shadow a Perl special variable / filehandle / compile-time token; pick a different name",
5107 name
5108 ),
5109 line,
5110 ));
5111 }
5112 let allow_shadow =
5119 crate::compat_mode() || (self.parsing_module && !crate::no_interop_mode());
5120 if !allow_shadow {
5121 self.check_udf_shadows_builtin(&name, line)?;
5122 }
5123 self.declared_subs.insert(name.clone());
5124 let (params, prototype) = self.parse_sub_sig_or_prototype_opt()?;
5125 self.parse_sub_attributes()?;
5126 let body = self.parse_fn_eq_body_or_block(is_sub_keyword)?;
5127 Ok(Statement {
5128 label: None,
5129 kind: StmtKind::SubDecl {
5130 name,
5131 params,
5132 body,
5133 prototype,
5134 },
5135 line,
5136 })
5137 }
5138 Token::LParen | Token::LBrace | Token::Colon => {
5139 if is_sub_keyword && crate::no_interop_mode() {
5141 return Err(self.syntax_err(
5142 "stryke uses `fn {}` instead of `sub {}` (--no-interop)",
5143 line,
5144 ));
5145 }
5146 let (params, _prototype) = self.parse_sub_sig_or_prototype_opt()?;
5148 self.parse_sub_attributes()?;
5149 let body = self.parse_fn_eq_body_or_block(is_sub_keyword)?;
5150 Ok(Statement {
5151 label: None,
5152 kind: StmtKind::Expression(Expr {
5153 kind: ExprKind::CodeRef { params, body },
5154 line,
5155 }),
5156 line,
5157 })
5158 }
5159 tok => {
5160 let topic_name = match &tok {
5165 Token::ScalarVar(n) | Token::ArrayVar(n) | Token::HashVar(n)
5166 if Self::is_underscore_topic_slot(n) =>
5167 {
5168 Some((
5169 match &tok {
5170 Token::ScalarVar(_) => '$',
5171 Token::ArrayVar(_) => '@',
5172 Token::HashVar(_) => '%',
5173 _ => unreachable!(),
5174 },
5175 n.clone(),
5176 ))
5177 }
5178 _ => None,
5179 };
5180 if let Some((sigil, n)) = topic_name {
5181 return Err(self.syntax_err(
5182 format!(
5183 "`fn {}{}` would shadow the topic-slot scalar; pick a different name",
5184 sigil, n
5185 ),
5186 self.peek_line(),
5187 ));
5188 }
5189 let special_var = match &tok {
5194 Token::ScalarVar(n) | Token::ArrayVar(n) | Token::HashVar(n) => Some((
5195 match &tok {
5196 Token::ScalarVar(_) => '$',
5197 Token::ArrayVar(_) => '@',
5198 Token::HashVar(_) => '%',
5199 _ => unreachable!(),
5200 },
5201 n.clone(),
5202 )),
5203 _ => None,
5204 };
5205 if let Some((sigil, n)) = special_var {
5206 return Err(self.syntax_err(
5207 format!(
5208 "`fn {}{}` would shadow a Perl special variable / global; pick a different name",
5209 sigil, n
5210 ),
5211 self.peek_line(),
5212 ));
5213 }
5214 if matches!(tok, Token::Percent) {
5219 return Err(self.syntax_err(
5220 "`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 { ... }`",
5221 self.peek_line(),
5222 ));
5223 }
5224 Err(self.syntax_err(
5225 format!("Expected sub name, `(`, `{{`, or `:`, got {:?}", tok),
5226 self.peek_line(),
5227 ))
5228 }
5229 }
5230 }
5231
5232 fn parse_advice_decl(&mut self, kind: crate::ast::AdviceKind) -> StrykeResult<Statement> {
5235 let line = self.peek_line();
5236 self.advance(); let pattern = match self.advance() {
5238 (Token::SingleString(s), _) | (Token::DoubleString(s), _) => s,
5239 (tok, err_line) => {
5240 return Err(self.syntax_err(
5241 format!(
5242 "Expected string-literal pattern after `{}`, got {:?}",
5243 match kind {
5244 crate::ast::AdviceKind::Before => "before",
5245 crate::ast::AdviceKind::After => "after",
5246 crate::ast::AdviceKind::Around => "around",
5247 },
5248 tok
5249 ),
5250 err_line,
5251 ));
5252 }
5253 };
5254 let body = self.parse_block()?;
5255 Ok(Statement {
5256 label: None,
5257 kind: StmtKind::AdviceDecl {
5258 kind,
5259 pattern,
5260 body,
5261 },
5262 line,
5263 })
5264 }
5265
5266 fn parse_struct_decl(&mut self) -> StrykeResult<Statement> {
5268 let line = self.peek_line();
5269 self.advance(); let raw_name = self.parse_package_qualified_identifier().map_err(|_| {
5271 self.syntax_err(
5272 format!("Expected struct name, got {:?}", self.peek()),
5273 self.peek_line(),
5274 )
5275 })?;
5276 let name = if raw_name.contains("::") || self.current_package == "main" {
5277 raw_name
5278 } else {
5279 format!("{}::{}", self.current_package, raw_name)
5280 };
5281 self.expect(&Token::LBrace)?;
5282 let mut fields = Vec::new();
5283 let mut methods = Vec::new();
5284 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
5285 let is_method = match self.peek() {
5287 Token::Ident(s) => s == "fn" || s == "sub",
5288 _ => false,
5289 };
5290 if is_method {
5291 let is_sub_keyword = matches!(self.peek(), Token::Ident(ref s) if s == "sub");
5292 self.advance(); let method_name = match self.advance() {
5294 (Token::Ident(n), _) => n,
5295 (tok, err_line) => {
5296 return Err(self
5297 .syntax_err(format!("Expected method name, got {:?}", tok), err_line))
5298 }
5299 };
5300 let params = if self.eat(&Token::LParen) {
5302 let p = self.parse_sub_signature_param_list()?;
5303 self.expect(&Token::RParen)?;
5304 p
5305 } else {
5306 Vec::new()
5307 };
5308 let body = if is_sub_keyword {
5309 self.parse_block()?
5310 } else {
5311 self.parse_fn_eq_body_or_block(false)?
5312 };
5313 methods.push(crate::ast::StructMethod {
5314 name: method_name,
5315 params,
5316 body,
5317 });
5318 self.eat(&Token::Comma);
5320 self.eat(&Token::Semicolon);
5321 continue;
5322 }
5323
5324 let field_name = match self.advance() {
5325 (Token::Ident(n), _) => n,
5326 (tok, err_line) => {
5327 return Err(
5328 self.syntax_err(format!("Expected field name, got {:?}", tok), err_line)
5329 )
5330 }
5331 };
5332 let ty = if self.eat(&Token::FatArrow) || self.eat(&Token::Colon) {
5337 self.parse_type_name()?
5338 } else {
5339 crate::ast::PerlTypeName::Any
5340 };
5341 let default = if self.eat(&Token::Assign) {
5342 Some(self.parse_ternary()?)
5344 } else {
5345 None
5346 };
5347 fields.push(StructField {
5348 name: field_name,
5349 ty,
5350 default,
5351 });
5352 if !self.eat(&Token::Comma) {
5353 self.eat(&Token::Semicolon);
5355 }
5356 }
5357 self.expect(&Token::RBrace)?;
5358 self.eat(&Token::Semicolon);
5359 Ok(Statement {
5360 label: None,
5361 kind: StmtKind::StructDecl {
5362 def: StructDef {
5363 name,
5364 fields,
5365 methods,
5366 },
5367 },
5368 line,
5369 })
5370 }
5371
5372 fn parse_enum_decl(&mut self) -> StrykeResult<Statement> {
5374 let line = self.peek_line();
5375 self.advance(); let raw_name = self.parse_package_qualified_identifier().map_err(|_| {
5377 self.syntax_err(
5378 format!("Expected enum name, got {:?}", self.peek()),
5379 self.peek_line(),
5380 )
5381 })?;
5382 let name = if raw_name.contains("::") || self.current_package == "main" {
5383 raw_name
5384 } else {
5385 format!("{}::{}", self.current_package, raw_name)
5386 };
5387 self.expect(&Token::LBrace)?;
5388 let mut variants = Vec::new();
5389 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
5390 let variant_name = match self.advance() {
5391 (Token::Ident(n), _) => n,
5392 (tok, err_line) => {
5393 return Err(
5394 self.syntax_err(format!("Expected variant name, got {:?}", tok), err_line)
5395 )
5396 }
5397 };
5398 let ty = if self.eat(&Token::FatArrow) {
5399 Some(self.parse_type_name()?)
5400 } else {
5401 None
5402 };
5403 variants.push(EnumVariant {
5404 name: variant_name,
5405 ty,
5406 });
5407 if !self.eat(&Token::Comma) {
5408 self.eat(&Token::Semicolon);
5409 }
5410 }
5411 self.expect(&Token::RBrace)?;
5412 self.eat(&Token::Semicolon);
5413 Ok(Statement {
5414 label: None,
5415 kind: StmtKind::EnumDecl {
5416 def: EnumDef { name, variants },
5417 },
5418 line,
5419 })
5420 }
5421
5422 fn parse_class_decl(&mut self, is_abstract: bool, is_final: bool) -> StrykeResult<Statement> {
5424 use crate::ast::{ClassDef, ClassField, ClassMethod, ClassStaticField, Visibility};
5425 let line = self.peek_line();
5426 self.advance(); let raw_name = self.parse_package_qualified_identifier().map_err(|_| {
5428 self.syntax_err(
5429 format!("Expected class name, got {:?}", self.peek()),
5430 self.peek_line(),
5431 )
5432 })?;
5433 let name = if raw_name.contains("::") || self.current_package == "main" {
5438 raw_name
5439 } else {
5440 format!("{}::{}", self.current_package, raw_name)
5441 };
5442
5443 let mut extends = Vec::new();
5445 if matches!(self.peek(), Token::Ident(ref s) if s == "extends") {
5446 self.advance(); loop {
5448 let parent = self.parse_package_qualified_identifier().map_err(|_| {
5449 self.syntax_err(
5450 format!(
5451 "Expected parent class name after `extends`, got {:?}",
5452 self.peek()
5453 ),
5454 self.peek_line(),
5455 )
5456 })?;
5457 extends.push(parent);
5458 if !self.eat(&Token::Comma) {
5459 break;
5460 }
5461 }
5462 }
5463
5464 let mut implements = Vec::new();
5466 if matches!(self.peek(), Token::Ident(ref s) if s == "impl") {
5467 self.advance(); loop {
5469 let trait_name = self.parse_package_qualified_identifier().map_err(|_| {
5470 self.syntax_err(
5471 format!("Expected trait name after `impl`, got {:?}", self.peek()),
5472 self.peek_line(),
5473 )
5474 })?;
5475 implements.push(trait_name);
5476 if !self.eat(&Token::Comma) {
5477 break;
5478 }
5479 }
5480 }
5481
5482 self.expect(&Token::LBrace)?;
5483 let mut fields = Vec::new();
5484 let mut methods = Vec::new();
5485 let mut static_fields = Vec::new();
5486
5487 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
5488 let visibility = match self.peek() {
5490 Token::Ident(ref s) if s == "pub" => {
5491 self.advance();
5492 Visibility::Public
5493 }
5494 Token::Ident(ref s) if s == "priv" => {
5495 self.advance();
5496 Visibility::Private
5497 }
5498 Token::Ident(ref s) if s == "prot" => {
5499 self.advance();
5500 Visibility::Protected
5501 }
5502 _ => Visibility::Public, };
5504
5505 if matches!(self.peek(), Token::Ident(ref s) if s == "static") {
5507 self.advance(); if matches!(self.peek(), Token::Ident(ref s) if s == "fn" || s == "sub") {
5511 return Err(self.syntax_err(
5513 "use `fn Self.name` for static methods, not `static fn`",
5514 self.peek_line(),
5515 ));
5516 }
5517
5518 let field_name = match self.advance() {
5519 (Token::Ident(n), _) => n,
5520 (tok, err_line) => {
5521 return Err(self.syntax_err(
5522 format!("Expected static field name, got {:?}", tok),
5523 err_line,
5524 ))
5525 }
5526 };
5527
5528 let ty = if self.eat(&Token::Colon) {
5529 self.parse_type_name()?
5530 } else {
5531 crate::ast::PerlTypeName::Any
5532 };
5533
5534 let default = if self.eat(&Token::Assign) {
5535 Some(self.parse_ternary()?)
5536 } else {
5537 None
5538 };
5539
5540 static_fields.push(ClassStaticField {
5541 name: field_name,
5542 ty,
5543 visibility,
5544 default,
5545 });
5546
5547 if !self.eat(&Token::Comma) {
5548 self.eat(&Token::Semicolon);
5549 }
5550 continue;
5551 }
5552
5553 let method_is_final = matches!(self.peek(), Token::Ident(ref s) if s == "final");
5555 if method_is_final {
5556 self.advance(); }
5558
5559 let is_method = matches!(self.peek(), Token::Ident(ref s) if s == "fn" || s == "sub");
5561 if is_method {
5562 self.advance(); let is_static = matches!(self.peek(), Token::Ident(ref s) if s == "Self");
5566 if is_static {
5567 self.advance(); self.expect(&Token::Dot)?;
5569 }
5570
5571 let method_name = match self.advance() {
5572 (Token::Ident(n), _) => n,
5573 (tok, err_line) => {
5574 return Err(self
5575 .syntax_err(format!("Expected method name, got {:?}", tok), err_line))
5576 }
5577 };
5578
5579 let params = if self.eat(&Token::LParen) {
5581 let p = self.parse_sub_signature_param_list()?;
5582 self.expect(&Token::RParen)?;
5583 p
5584 } else {
5585 Vec::new()
5586 };
5587
5588 let body = if let Some(b) = self.try_parse_fn_assign_shorthand_body()? {
5590 Some(b)
5591 } else if matches!(self.peek(), Token::LBrace) {
5592 Some(self.parse_block()?)
5593 } else {
5594 None
5595 };
5596
5597 methods.push(ClassMethod {
5598 name: method_name,
5599 params,
5600 body,
5601 visibility,
5602 is_static,
5603 is_final: method_is_final,
5604 });
5605 self.eat(&Token::Comma);
5606 self.eat(&Token::Semicolon);
5607 continue;
5608 } else if method_is_final {
5609 return Err(self.syntax_err("`final` must be followed by `fn`", self.peek_line()));
5610 }
5611
5612 let field_name = match self.advance() {
5614 (Token::Ident(n), _) => n,
5615 (tok, err_line) => {
5616 return Err(
5617 self.syntax_err(format!("Expected field name, got {:?}", tok), err_line)
5618 )
5619 }
5620 };
5621
5622 let ty = if self.eat(&Token::Colon) || self.eat(&Token::FatArrow) {
5626 self.parse_type_name()?
5627 } else {
5628 crate::ast::PerlTypeName::Any
5629 };
5630
5631 let default = if self.eat(&Token::Assign) {
5633 Some(self.parse_ternary()?)
5634 } else {
5635 None
5636 };
5637
5638 fields.push(ClassField {
5639 name: field_name,
5640 ty,
5641 visibility,
5642 default,
5643 });
5644
5645 if !self.eat(&Token::Comma) {
5646 self.eat(&Token::Semicolon);
5647 }
5648 }
5649
5650 self.expect(&Token::RBrace)?;
5651 self.eat(&Token::Semicolon);
5652
5653 Ok(Statement {
5654 label: None,
5655 kind: StmtKind::ClassDecl {
5656 def: ClassDef {
5657 name,
5658 is_abstract,
5659 is_final,
5660 extends,
5661 implements,
5662 fields,
5663 methods,
5664 static_fields,
5665 },
5666 },
5667 line,
5668 })
5669 }
5670
5671 fn parse_trait_decl(&mut self) -> StrykeResult<Statement> {
5673 use crate::ast::{ClassMethod, TraitDef, Visibility};
5674 let line = self.peek_line();
5675 self.advance(); let raw_name = self.parse_package_qualified_identifier().map_err(|_| {
5677 self.syntax_err(
5678 format!("Expected trait name, got {:?}", self.peek()),
5679 self.peek_line(),
5680 )
5681 })?;
5682 let name = if raw_name.contains("::") || self.current_package == "main" {
5683 raw_name
5684 } else {
5685 format!("{}::{}", self.current_package, raw_name)
5686 };
5687
5688 self.expect(&Token::LBrace)?;
5689 let mut methods = Vec::new();
5690
5691 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
5692 let visibility = match self.peek() {
5694 Token::Ident(ref s) if s == "pub" => {
5695 self.advance();
5696 Visibility::Public
5697 }
5698 Token::Ident(ref s) if s == "priv" => {
5699 self.advance();
5700 Visibility::Private
5701 }
5702 Token::Ident(ref s) if s == "prot" => {
5703 self.advance();
5704 Visibility::Protected
5705 }
5706 _ => Visibility::Public,
5707 };
5708
5709 if !matches!(self.peek(), Token::Ident(ref s) if s == "fn" || s == "sub") {
5711 return Err(self.syntax_err("Expected `fn` in trait definition", self.peek_line()));
5712 }
5713 self.advance(); let method_name = match self.advance() {
5716 (Token::Ident(n), _) => n,
5717 (tok, err_line) => {
5718 return Err(
5719 self.syntax_err(format!("Expected method name, got {:?}", tok), err_line)
5720 )
5721 }
5722 };
5723
5724 let params = if self.eat(&Token::LParen) {
5726 let p = self.parse_sub_signature_param_list()?;
5727 self.expect(&Token::RParen)?;
5728 p
5729 } else {
5730 Vec::new()
5731 };
5732
5733 let body = if let Some(b) = self.try_parse_fn_assign_shorthand_body()? {
5735 Some(b)
5736 } else if matches!(self.peek(), Token::LBrace) {
5737 Some(self.parse_block()?)
5738 } else {
5739 None
5740 };
5741
5742 methods.push(ClassMethod {
5743 name: method_name,
5744 params,
5745 body,
5746 visibility,
5747 is_static: false,
5748 is_final: false,
5749 });
5750
5751 self.eat(&Token::Comma);
5752 self.eat(&Token::Semicolon);
5753 }
5754
5755 self.expect(&Token::RBrace)?;
5756 self.eat(&Token::Semicolon);
5757
5758 Ok(Statement {
5759 label: None,
5760 kind: StmtKind::TraitDecl {
5761 def: TraitDef { name, methods },
5762 },
5763 line,
5764 })
5765 }
5766
5767 fn local_simple_target_to_var_decl(target: &Expr) -> Option<VarDecl> {
5768 match &target.kind {
5769 ExprKind::ScalarVar(name) => Some(VarDecl {
5770 sigil: Sigil::Scalar,
5771 name: name.clone(),
5772 initializer: None,
5773 frozen: false,
5774 type_annotation: None,
5775 list_context: false,
5776 }),
5777 ExprKind::ArrayVar(name) => Some(VarDecl {
5778 sigil: Sigil::Array,
5779 name: name.clone(),
5780 initializer: None,
5781 frozen: false,
5782 type_annotation: None,
5783 list_context: false,
5784 }),
5785 ExprKind::HashVar(name) => Some(VarDecl {
5786 sigil: Sigil::Hash,
5787 name: name.clone(),
5788 initializer: None,
5789 frozen: false,
5790 type_annotation: None,
5791 list_context: false,
5792 }),
5793 ExprKind::Typeglob(name) => Some(VarDecl {
5794 sigil: Sigil::Typeglob,
5795 name: name.clone(),
5796 initializer: None,
5797 frozen: false,
5798 type_annotation: None,
5799 list_context: false,
5800 }),
5801 _ => None,
5802 }
5803 }
5804
5805 fn parse_decl_array_destructure(
5806 &mut self,
5807 keyword: &str,
5808 line: usize,
5809 ) -> StrykeResult<Statement> {
5810 self.expect(&Token::LBracket)?;
5811 let elems = self.parse_match_array_elems_until_rbracket()?;
5812 self.expect(&Token::Assign)?;
5813 self.suppress_scalar_hash_brace += 1;
5814 let rhs = self.parse_expression()?;
5815 self.suppress_scalar_hash_brace -= 1;
5816 let stmt = self.desugar_array_destructure(keyword, line, elems, rhs)?;
5817 self.parse_stmt_postfix_modifier(stmt)
5818 }
5819
5820 fn parse_decl_hash_destructure(
5821 &mut self,
5822 keyword: &str,
5823 line: usize,
5824 ) -> StrykeResult<Statement> {
5825 let MatchPattern::Hash(pairs) = self.parse_match_hash_pattern()? else {
5826 unreachable!("parse_match_hash_pattern returns Hash");
5827 };
5828 self.expect(&Token::Assign)?;
5829 self.suppress_scalar_hash_brace += 1;
5830 let rhs = self.parse_expression()?;
5831 self.suppress_scalar_hash_brace -= 1;
5832 let stmt = self.desugar_hash_destructure(keyword, line, pairs, rhs)?;
5833 self.parse_stmt_postfix_modifier(stmt)
5834 }
5835
5836 fn desugar_array_destructure(
5837 &mut self,
5838 keyword: &str,
5839 line: usize,
5840 elems: Vec<MatchArrayElem>,
5841 rhs: Expr,
5842 ) -> StrykeResult<Statement> {
5843 let tmp = format!("__stryke_ds_{}", self.alloc_desugar_tmp());
5844 let mut stmts: Vec<Statement> = Vec::new();
5845 stmts.push(destructure_stmt_from_var_decls(
5846 keyword,
5847 vec![VarDecl {
5848 sigil: Sigil::Scalar,
5849 name: tmp.clone(),
5850 initializer: Some(rhs),
5851 frozen: false,
5852 type_annotation: None,
5853 list_context: false,
5854 }],
5855 line,
5856 ));
5857
5858 let has_rest = elems
5859 .iter()
5860 .any(|e| matches!(e, MatchArrayElem::Rest | MatchArrayElem::RestBind(_)));
5861 let fixed_slots = elems
5862 .iter()
5863 .filter(|e| {
5864 matches!(
5865 e,
5866 MatchArrayElem::CaptureScalar(_) | MatchArrayElem::Expr(_)
5867 )
5868 })
5869 .count();
5870 if !has_rest {
5871 let cond = Expr {
5872 kind: ExprKind::BinOp {
5873 left: Box::new(destructure_expr_array_len(&tmp, line)),
5874 op: BinOp::NumEq,
5875 right: Box::new(Expr {
5876 kind: ExprKind::Integer(fixed_slots as i64),
5877 line,
5878 }),
5879 },
5880 line,
5881 };
5882 stmts.push(destructure_stmt_unless_die(
5883 line,
5884 cond,
5885 "array destructure: length mismatch",
5886 ));
5887 }
5888
5889 let mut idx: i64 = 0;
5890 for elem in elems {
5891 match elem {
5892 MatchArrayElem::Rest => break,
5893 MatchArrayElem::RestBind(name) => {
5894 let list_source = Expr {
5895 kind: ExprKind::Deref {
5896 expr: Box::new(destructure_expr_scalar_tmp(&tmp, line)),
5897 kind: Sigil::Array,
5898 },
5899 line,
5900 };
5901 let last_ix = Expr {
5902 kind: ExprKind::BinOp {
5903 left: Box::new(destructure_expr_array_len(&tmp, line)),
5904 op: BinOp::Sub,
5905 right: Box::new(Expr {
5906 kind: ExprKind::Integer(1),
5907 line,
5908 }),
5909 },
5910 line,
5911 };
5912 let range = Expr {
5913 kind: ExprKind::Range {
5914 from: Box::new(Expr {
5915 kind: ExprKind::Integer(idx),
5916 line,
5917 }),
5918 to: Box::new(last_ix),
5919 exclusive: false,
5920 step: None,
5921 },
5922 line,
5923 };
5924 let slice = Expr {
5925 kind: ExprKind::AnonymousListSlice {
5926 source: Box::new(list_source),
5927 indices: vec![range],
5928 },
5929 line,
5930 };
5931 stmts.push(destructure_stmt_from_var_decls(
5932 keyword,
5933 vec![VarDecl {
5934 sigil: Sigil::Array,
5935 name,
5936 initializer: Some(slice),
5937 frozen: false,
5938 type_annotation: None,
5939 list_context: false,
5940 }],
5941 line,
5942 ));
5943 break;
5944 }
5945 MatchArrayElem::CaptureScalar(name) => {
5946 let arrow = Expr {
5947 kind: ExprKind::ArrowDeref {
5948 expr: Box::new(destructure_expr_scalar_tmp(&tmp, line)),
5949 index: Box::new(Expr {
5950 kind: ExprKind::Integer(idx),
5951 line,
5952 }),
5953 kind: DerefKind::Array,
5954 },
5955 line,
5956 };
5957 stmts.push(destructure_stmt_from_var_decls(
5958 keyword,
5959 vec![VarDecl {
5960 sigil: Sigil::Scalar,
5961 name,
5962 initializer: Some(arrow),
5963 frozen: false,
5964 list_context: false,
5965 type_annotation: None,
5966 }],
5967 line,
5968 ));
5969 idx += 1;
5970 }
5971 MatchArrayElem::Expr(e) => {
5972 let elem_subj = Expr {
5973 kind: ExprKind::ArrowDeref {
5974 expr: Box::new(destructure_expr_scalar_tmp(&tmp, line)),
5975 index: Box::new(Expr {
5976 kind: ExprKind::Integer(idx),
5977 line,
5978 }),
5979 kind: DerefKind::Array,
5980 },
5981 line,
5982 };
5983 let match_expr = Expr {
5984 kind: ExprKind::AlgebraicMatch {
5985 subject: Box::new(elem_subj),
5986 arms: vec![
5987 MatchArm {
5988 pattern: MatchPattern::Value(Box::new(e.clone())),
5989 guard: None,
5990 body: Expr {
5991 kind: ExprKind::Integer(0),
5992 line,
5993 },
5994 },
5995 MatchArm {
5996 pattern: MatchPattern::Any,
5997 guard: None,
5998 body: Expr {
5999 kind: ExprKind::Die(vec![Expr {
6000 kind: ExprKind::String(
6001 "array destructure: element pattern mismatch"
6002 .to_string(),
6003 ),
6004 line,
6005 }]),
6006 line,
6007 },
6008 },
6009 ],
6010 },
6011 line,
6012 };
6013 stmts.push(Statement {
6014 label: None,
6015 kind: StmtKind::Expression(match_expr),
6016 line,
6017 });
6018 idx += 1;
6019 }
6020 }
6021 }
6022
6023 Ok(Statement {
6024 label: None,
6025 kind: StmtKind::StmtGroup(stmts),
6026 line,
6027 })
6028 }
6029
6030 fn desugar_hash_destructure(
6031 &mut self,
6032 keyword: &str,
6033 line: usize,
6034 pairs: Vec<MatchHashPair>,
6035 rhs: Expr,
6036 ) -> StrykeResult<Statement> {
6037 let tmp = format!("__stryke_ds_{}", self.alloc_desugar_tmp());
6038 let mut stmts: Vec<Statement> = Vec::new();
6039 stmts.push(destructure_stmt_from_var_decls(
6040 keyword,
6041 vec![VarDecl {
6042 sigil: Sigil::Scalar,
6043 name: tmp.clone(),
6044 initializer: Some(rhs),
6045 frozen: false,
6046 type_annotation: None,
6047 list_context: false,
6048 }],
6049 line,
6050 ));
6051
6052 for pair in pairs {
6053 match pair {
6054 MatchHashPair::KeyOnly { key } => {
6055 let exists_op = Expr {
6056 kind: ExprKind::Exists(Box::new(Expr {
6057 kind: ExprKind::ArrowDeref {
6058 expr: Box::new(destructure_expr_scalar_tmp(&tmp, line)),
6059 index: Box::new(key),
6060 kind: DerefKind::Hash,
6061 },
6062 line,
6063 })),
6064 line,
6065 };
6066 stmts.push(destructure_stmt_unless_die(
6067 line,
6068 exists_op,
6069 "hash destructure: missing required key",
6070 ));
6071 }
6072 MatchHashPair::Capture { key, name } => {
6073 let init = Expr {
6074 kind: ExprKind::ArrowDeref {
6075 expr: Box::new(destructure_expr_scalar_tmp(&tmp, line)),
6076 index: Box::new(key),
6077 kind: DerefKind::Hash,
6078 },
6079 line,
6080 };
6081 stmts.push(destructure_stmt_from_var_decls(
6082 keyword,
6083 vec![VarDecl {
6084 sigil: Sigil::Scalar,
6085 name,
6086 initializer: Some(init),
6087 frozen: false,
6088 type_annotation: None,
6089 list_context: false,
6090 }],
6091 line,
6092 ));
6093 }
6094 }
6095 }
6096
6097 Ok(Statement {
6098 label: None,
6099 kind: StmtKind::StmtGroup(stmts),
6100 line,
6101 })
6102 }
6103
6104 fn parse_my_our_local(
6105 &mut self,
6106 keyword: &str,
6107 allow_type_annotation: bool,
6108 ) -> StrykeResult<Statement> {
6109 let line = self.peek_line();
6110 self.advance(); if keyword == "local"
6113 && !matches!(self.peek(), Token::LParen | Token::LBracket | Token::LBrace)
6114 {
6115 let target = self.parse_postfix()?;
6116 let mut initializer: Option<Expr> = None;
6117 if self.eat(&Token::Assign) {
6118 initializer = Some(self.parse_expression()?);
6119 } else if matches!(
6120 self.peek(),
6121 Token::OrAssign | Token::DefinedOrAssign | Token::AndAssign
6122 ) {
6123 if matches!(&target.kind, ExprKind::Typeglob(_)) {
6124 return Err(self.syntax_err(
6125 "compound assignment on typeglob declaration is not supported",
6126 self.peek_line(),
6127 ));
6128 }
6129 let op = match self.peek().clone() {
6130 Token::OrAssign => BinOp::LogOr,
6131 Token::DefinedOrAssign => BinOp::DefinedOr,
6132 Token::AndAssign => BinOp::LogAnd,
6133 _ => unreachable!(),
6134 };
6135 self.advance();
6136 let rhs = self.parse_assign_expr()?;
6137 let tgt_line = target.line;
6138 initializer = Some(Expr {
6139 kind: ExprKind::CompoundAssign {
6140 target: Box::new(target.clone()),
6141 op,
6142 value: Box::new(rhs),
6143 },
6144 line: tgt_line,
6145 });
6146 }
6147
6148 let kind = if let Some(mut decl) = Self::local_simple_target_to_var_decl(&target) {
6149 decl.initializer = initializer;
6150 StmtKind::Local(vec![decl])
6151 } else {
6152 StmtKind::LocalExpr {
6153 target,
6154 initializer,
6155 }
6156 };
6157 let stmt = Statement {
6158 label: None,
6159 kind,
6160 line,
6161 };
6162 return self.parse_stmt_postfix_modifier(stmt);
6163 }
6164
6165 if matches!(self.peek(), Token::LBracket) {
6166 return self.parse_decl_array_destructure(keyword, line);
6167 }
6168 if matches!(self.peek(), Token::LBrace) {
6169 return self.parse_decl_hash_destructure(keyword, line);
6170 }
6171
6172 let mut decls = Vec::new();
6173 let used_parens = self.eat(&Token::LParen);
6174
6175 if used_parens {
6176 while !matches!(self.peek(), Token::RParen | Token::Eof) {
6178 let decl = self.parse_var_decl(allow_type_annotation)?;
6179 decls.push(decl);
6180 if !self.eat(&Token::Comma) {
6181 break;
6182 }
6183 }
6184 self.expect(&Token::RParen)?;
6185 } else {
6186 decls.push(self.parse_var_decl(allow_type_annotation)?);
6187 }
6188 if used_parens {
6190 for decl in &mut decls {
6191 decl.list_context = true;
6192 }
6193 }
6194
6195 if self.eat(&Token::Assign) {
6197 if keyword == "our" && decls.len() == 1 {
6198 while matches!(self.peek(), Token::Ident(ref i) if i == "our") {
6199 self.advance();
6200 decls.push(self.parse_var_decl(allow_type_annotation)?);
6201 if !self.eat(&Token::Assign) {
6202 return Err(self.syntax_err(
6203 "expected `=` after `our` in chained our-declaration",
6204 self.peek_line(),
6205 ));
6206 }
6207 }
6208 }
6209 let rhs_start_pos = self.pos;
6210 let mut val = self.parse_expression()?;
6211 let rhs_end_pos = self.pos;
6212 if !crate::compat_mode()
6233 && self.block_depth == 0
6234 && decls.len() == 1
6235 && matches!(decls[0].sigil, Sigil::Scalar)
6236 && !matches!(
6237 val.kind,
6238 ExprKind::CodeRef { .. }
6239 | ExprKind::SubroutineRef(_)
6240 | ExprKind::SubroutineCodeRef(_)
6241 | ExprKind::DynamicSubCodeRef(_)
6242 )
6243 && self.rhs_has_free_bare_topic_slot(rhs_start_pos, rhs_end_pos)
6244 {
6245 let val_line = val.line;
6246 val = Expr {
6247 kind: ExprKind::CodeRef {
6248 params: Vec::new(),
6249 body: vec![Statement {
6250 label: None,
6251 kind: StmtKind::Expression(val),
6252 line: val_line,
6253 }],
6254 },
6255 line: val_line,
6256 };
6257 }
6258 if !crate::compat_mode() && decls.len() == 1 {
6261 let decl = &decls[0];
6262 let target_kind = match decl.sigil {
6263 Sigil::Scalar => ExprKind::ScalarVar(decl.name.clone()),
6264 Sigil::Array => ExprKind::ArrayVar(decl.name.clone()),
6265 Sigil::Hash => ExprKind::HashVar(decl.name.clone()),
6266 Sigil::Typeglob => {
6267 if decls.len() == 1 {
6269 decls[0].initializer = Some(val);
6270 } else {
6271 for d in &mut decls {
6272 d.initializer = Some(val.clone());
6273 }
6274 }
6275 return Ok(Statement {
6276 label: None,
6277 kind: match keyword {
6278 "my" => StmtKind::My(decls),
6279 "mysync" => StmtKind::MySync(decls),
6280 "our" => StmtKind::Our(decls),
6281 "oursync" => StmtKind::OurSync(decls),
6282 "local" => StmtKind::Local(decls),
6283 "state" => StmtKind::State(decls),
6284 _ => unreachable!(),
6285 },
6286 line,
6287 });
6288 }
6289 };
6290 let target = Expr {
6291 kind: target_kind,
6292 line,
6293 };
6294 self.validate_assignment(&target, &val, line)?;
6295 }
6296 if decls.len() == 1 {
6297 decls[0].initializer = Some(val);
6298 } else {
6299 for decl in &mut decls {
6300 decl.initializer = Some(val.clone());
6301 }
6302 }
6303 } else if decls.len() == 1 {
6304 let op = match self.peek().clone() {
6306 Token::OrAssign => Some(BinOp::LogOr),
6307 Token::DefinedOrAssign => Some(BinOp::DefinedOr),
6308 Token::AndAssign => Some(BinOp::LogAnd),
6309 _ => None,
6310 };
6311 if let Some(op) = op {
6312 let d = &decls[0];
6313 if matches!(d.sigil, Sigil::Typeglob) {
6314 return Err(self.syntax_err(
6315 "compound assignment on typeglob declaration is not supported",
6316 self.peek_line(),
6317 ));
6318 }
6319 self.advance();
6320 let rhs = self.parse_assign_expr()?;
6321 let target = Expr {
6322 kind: match d.sigil {
6323 Sigil::Scalar => ExprKind::ScalarVar(d.name.clone()),
6324 Sigil::Array => ExprKind::ArrayVar(d.name.clone()),
6325 Sigil::Hash => ExprKind::HashVar(d.name.clone()),
6326 Sigil::Typeglob => unreachable!(),
6327 },
6328 line,
6329 };
6330 decls[0].initializer = Some(Expr {
6331 kind: ExprKind::CompoundAssign {
6332 target: Box::new(target),
6333 op,
6334 value: Box::new(rhs),
6335 },
6336 line,
6337 });
6338 }
6339 }
6340
6341 let kind = match keyword {
6342 "my" => StmtKind::My(decls),
6343 "mysync" => StmtKind::MySync(decls),
6344 "our" => StmtKind::Our(decls),
6345 "oursync" => StmtKind::OurSync(decls),
6346 "local" => StmtKind::Local(decls),
6347 "state" => StmtKind::State(decls),
6348 _ => unreachable!(),
6349 };
6350 let stmt = Statement {
6351 label: None,
6352 kind,
6353 line,
6354 };
6355 self.parse_stmt_postfix_modifier(stmt)
6357 }
6358
6359 fn parse_var_decl(&mut self, allow_type_annotation: bool) -> StrykeResult<VarDecl> {
6360 let mut decl = match self.advance() {
6361 (Token::ScalarVar(name), _) => VarDecl {
6362 sigil: Sigil::Scalar,
6363 name,
6364 initializer: None,
6365 frozen: false,
6366 type_annotation: None,
6367 list_context: false,
6368 },
6369 (Token::ArrayVar(name), _) => VarDecl {
6370 sigil: Sigil::Array,
6371 name,
6372 initializer: None,
6373 frozen: false,
6374 type_annotation: None,
6375 list_context: false,
6376 },
6377 (Token::HashVar(name), line) => {
6378 if !crate::compat_mode() {
6379 self.check_hash_shadows_reserved(&name, line)?;
6380 }
6381 VarDecl {
6382 sigil: Sigil::Hash,
6383 name,
6384 initializer: None,
6385 frozen: false,
6386 type_annotation: None,
6387 list_context: false,
6388 }
6389 }
6390 (Token::Star, _line) => {
6391 let name = match self.advance() {
6392 (Token::Ident(n), _) => n,
6393 (tok, l) => {
6394 return Err(self
6395 .syntax_err(format!("Expected identifier after *, got {:?}", tok), l));
6396 }
6397 };
6398 VarDecl {
6399 sigil: Sigil::Typeglob,
6400 name,
6401 initializer: None,
6402 frozen: false,
6403 type_annotation: None,
6404 list_context: false,
6405 }
6406 }
6407 (Token::Ident(ref kw), _)
6412 if kw == "undef" || (kw == "null" && !crate::compat_mode()) =>
6413 {
6414 VarDecl {
6415 sigil: Sigil::Scalar,
6416 name: format!("__undef_sink_{}", self.pos),
6420 initializer: None,
6421 frozen: false,
6422 type_annotation: None,
6423 list_context: false,
6424 }
6425 }
6426 (tok, line) => {
6427 return Err(self.syntax_err(
6428 format!("Expected variable in declaration, got {:?}", tok),
6429 line,
6430 ));
6431 }
6432 };
6433 if allow_type_annotation && self.eat(&Token::Colon) {
6434 let ty = self.parse_type_name()?;
6435 if decl.sigil != Sigil::Scalar {
6436 return Err(self.syntax_err(
6437 "`: Type` is only valid for scalar declarations (typed my $name : Int)",
6438 self.peek_line(),
6439 ));
6440 }
6441 decl.type_annotation = Some(ty);
6442 }
6443 Ok(decl)
6444 }
6445
6446 fn parse_type_name(&mut self) -> StrykeResult<PerlTypeName> {
6447 match self.advance() {
6448 (Token::Ident(name), _) => match name.as_str() {
6449 "Int" => Ok(PerlTypeName::Int),
6450 "Str" => Ok(PerlTypeName::Str),
6451 "Float" => Ok(PerlTypeName::Float),
6452 "Bool" => Ok(PerlTypeName::Bool),
6453 "Array" => Ok(PerlTypeName::Array),
6454 "Hash" => Ok(PerlTypeName::Hash),
6455 "Ref" => Ok(PerlTypeName::Ref),
6456 "Any" => Ok(PerlTypeName::Any),
6457 _ => Ok(PerlTypeName::Struct(name)),
6458 },
6459 (tok, err_line) => Err(self.syntax_err(
6460 format!("Expected type name after `:`, got {:?}", tok),
6461 err_line,
6462 )),
6463 }
6464 }
6465
6466 fn parse_package(&mut self) -> StrykeResult<Statement> {
6467 let line = self.peek_line();
6468 self.advance(); let name = match self.advance() {
6470 (Token::Ident(n), _) => n,
6471 (tok, line) => {
6472 return Err(self.syntax_err(format!("Expected package name, got {:?}", tok), line))
6473 }
6474 };
6475 let mut full_name = name;
6477 while self.eat(&Token::PackageSep) {
6478 if let (Token::Ident(part), _) = self.advance() {
6479 full_name = format!("{}::{}", full_name, part);
6480 }
6481 }
6482 self.eat(&Token::Semicolon);
6483 self.current_package = full_name.clone();
6486 Ok(Statement {
6487 label: None,
6488 kind: StmtKind::Package { name: full_name },
6489 line,
6490 })
6491 }
6492
6493 fn parse_use(&mut self) -> StrykeResult<Statement> {
6494 let line = self.peek_line();
6495 let kw_name = match self.peek() {
6498 Token::Ident(s) if s == "import" => "import",
6499 _ => "use",
6500 };
6501 self.advance(); let (tok, tok_line) = self.advance();
6503 match tok {
6504 Token::Float(v) => {
6505 self.eat(&Token::Semicolon);
6506 Ok(Statement {
6507 label: None,
6508 kind: StmtKind::UsePerlVersion { version: v },
6509 line,
6510 })
6511 }
6512 Token::Integer(n) => {
6513 if matches!(self.peek(), Token::Semicolon | Token::Eof) {
6514 self.eat(&Token::Semicolon);
6515 Ok(Statement {
6516 label: None,
6517 kind: StmtKind::UsePerlVersion { version: n as f64 },
6518 line,
6519 })
6520 } else {
6521 Err(self.syntax_err(
6522 format!("Expected ';' after {} VERSION (got {:?})", kw_name, self.peek()),
6523 line,
6524 ))
6525 }
6526 }
6527 Token::Ident(n) => {
6528 let mut full_name = n;
6529 while self.eat(&Token::PackageSep) {
6530 if let (Token::Ident(part), _) = self.advance() {
6531 full_name = format!("{}::{}", full_name, part);
6532 }
6533 }
6534 if full_name == "overload" {
6535 let mut pairs = Vec::new();
6536 let mut parse_overload_pairs = |this: &mut Self| -> StrykeResult<()> {
6537 loop {
6538 if matches!(this.peek(), Token::RParen | Token::Semicolon | Token::Eof)
6539 {
6540 break;
6541 }
6542 let key_e = this.parse_assign_expr()?;
6543 this.expect(&Token::FatArrow)?;
6544 let val_e = this.parse_assign_expr()?;
6545 let key = this.expr_to_overload_key(&key_e)?;
6546 let val = this.expr_to_overload_sub(&val_e)?;
6547 pairs.push((key, val));
6548 if !this.eat(&Token::Comma) {
6549 break;
6550 }
6551 }
6552 Ok(())
6553 };
6554 if self.eat(&Token::LParen) {
6555 parse_overload_pairs(self)?;
6557 self.expect(&Token::RParen)?;
6558 } else if !matches!(self.peek(), Token::Semicolon | Token::Eof) {
6559 parse_overload_pairs(self)?;
6560 }
6561 self.eat(&Token::Semicolon);
6562 return Ok(Statement {
6563 label: None,
6564 kind: StmtKind::UseOverload { pairs },
6565 line,
6566 });
6567 }
6568 let mut imports = Vec::new();
6569 let on_same_line = self.peek_line() == tok_line;
6577 let mut version: Option<String> = None;
6587 if on_same_line {
6588 match self.peek().clone() {
6589 Token::Integer(n) => {
6590 self.advance();
6591 version = Some(n.to_string());
6592 self.eat(&Token::Comma);
6593 }
6594 Token::Float(v) => {
6595 self.advance();
6596 version = Some(format_version_float(v));
6597 self.eat(&Token::Comma);
6598 }
6599 _ => {}
6600 }
6601 }
6602 if on_same_line
6603 && !matches!(self.peek(), Token::Semicolon | Token::Eof)
6604 && !self.next_is_new_statement_start(tok_line)
6605 {
6606 loop {
6607 if matches!(self.peek(), Token::Semicolon | Token::Eof) {
6608 break;
6609 }
6610 imports.push(self.parse_expression()?);
6611 if !self.eat(&Token::Comma) {
6612 break;
6613 }
6614 }
6615 }
6616 self.eat(&Token::Semicolon);
6617 Ok(Statement {
6618 label: None,
6619 kind: StmtKind::Use {
6620 module: full_name,
6621 imports,
6622 version,
6623 },
6624 line,
6625 })
6626 }
6627 other => Err(self.syntax_err(
6628 format!("Expected module name or version after {}, got {:?}", kw_name, other),
6629 tok_line,
6630 )),
6631 }
6632 }
6633
6634 fn parse_no(&mut self) -> StrykeResult<Statement> {
6635 let line = self.peek_line();
6636 self.advance(); let module = match self.advance() {
6638 (Token::Ident(n), tok_line) => (n, tok_line),
6639 (tok, line) => {
6640 return Err(self.syntax_err(
6641 format!("Expected module name after no, got {:?}", tok),
6642 line,
6643 ))
6644 }
6645 };
6646 let (module_name, tok_line) = module;
6647 let mut full_name = module_name;
6648 while self.eat(&Token::PackageSep) {
6649 if let (Token::Ident(part), _) = self.advance() {
6650 full_name = format!("{}::{}", full_name, part);
6651 }
6652 }
6653 let mut imports = Vec::new();
6654 if !matches!(self.peek(), Token::Semicolon | Token::Eof)
6655 && !self.next_is_new_statement_start(tok_line)
6656 {
6657 loop {
6658 if matches!(self.peek(), Token::Semicolon | Token::Eof) {
6659 break;
6660 }
6661 imports.push(self.parse_expression()?);
6662 if !self.eat(&Token::Comma) {
6663 break;
6664 }
6665 }
6666 }
6667 self.eat(&Token::Semicolon);
6668 Ok(Statement {
6669 label: None,
6670 kind: StmtKind::No {
6671 module: full_name,
6672 imports,
6673 },
6674 line,
6675 })
6676 }
6677
6678 fn parse_return(&mut self) -> StrykeResult<Statement> {
6679 let line = self.peek_line();
6680 self.advance(); let val = if matches!(self.peek(), Token::Semicolon | Token::RBrace | Token::Eof)
6687 || self.peek_is_postfix_stmt_modifier_keyword()
6688 {
6689 None
6690 } else {
6691 let first = self.parse_assign_expr()?;
6696 if matches!(self.peek(), Token::Comma | Token::FatArrow) {
6697 let mut items = vec![first];
6698 while self.eat(&Token::Comma) || self.eat(&Token::FatArrow) {
6699 if matches!(self.peek(), Token::Semicolon | Token::RBrace | Token::Eof)
6700 || self.peek_is_postfix_stmt_modifier_keyword()
6701 {
6702 break;
6703 }
6704 items.push(self.parse_assign_expr()?);
6705 }
6706 let line = items.first().map(|e| e.line).unwrap_or(line);
6707 Some(Expr {
6708 kind: ExprKind::List(items),
6709 line,
6710 })
6711 } else {
6712 Some(first)
6713 }
6714 };
6715 let stmt = Statement {
6717 label: None,
6718 kind: StmtKind::Return(val),
6719 line,
6720 };
6721 if let Token::Ident(ref kw) = self.peek().clone() {
6722 match kw.as_str() {
6723 "if" => {
6724 self.advance();
6725 let cond = self.parse_expression()?;
6726 self.eat(&Token::Semicolon);
6727 return Ok(Statement {
6728 label: None,
6729 kind: StmtKind::If {
6730 condition: cond,
6731 body: vec![stmt],
6732 elsifs: vec![],
6733 else_block: None,
6734 },
6735 line,
6736 });
6737 }
6738 "unless" => {
6739 self.advance();
6740 let cond = self.parse_expression()?;
6741 self.eat(&Token::Semicolon);
6742 return Ok(Statement {
6743 label: None,
6744 kind: StmtKind::Unless {
6745 condition: cond,
6746 body: vec![stmt],
6747 else_block: None,
6748 },
6749 line,
6750 });
6751 }
6752 _ => {}
6753 }
6754 }
6755 self.eat(&Token::Semicolon);
6756 Ok(stmt)
6757 }
6758
6759 fn parse_expression(&mut self) -> StrykeResult<Expr> {
6762 self.parse_comma_expr()
6763 }
6764
6765 fn parse_comma_expr(&mut self) -> StrykeResult<Expr> {
6766 let expr = self.parse_or_word()?;
6774 let mut exprs = vec![expr];
6775 while self.eat(&Token::Comma) || self.eat(&Token::FatArrow) {
6776 if matches!(
6777 self.peek(),
6778 Token::RParen | Token::RBracket | Token::RBrace | Token::Semicolon | Token::Eof
6779 ) {
6780 break; }
6782 exprs.push(self.parse_or_word()?);
6783 }
6784 if exprs.len() == 1 {
6785 return Ok(exprs.pop().unwrap());
6786 }
6787 let line = exprs[0].line;
6788 Ok(Expr {
6789 kind: ExprKind::List(exprs),
6790 line,
6791 })
6792 }
6793
6794 fn parse_assign_expr(&mut self) -> StrykeResult<Expr> {
6795 let expr = self.parse_ternary()?;
6796 let line = expr.line;
6797
6798 match self.peek().clone() {
6799 Token::Assign => {
6800 self.advance();
6801 let right = self.parse_assign_expr()?;
6802 if let ExprKind::MethodCall { ref args, .. } = expr.kind {
6804 if args.is_empty() {
6805 let ExprKind::MethodCall {
6807 object,
6808 method,
6809 super_call,
6810 ..
6811 } = expr.kind
6812 else {
6813 unreachable!()
6814 };
6815 return Ok(Expr {
6816 kind: ExprKind::MethodCall {
6817 object,
6818 method,
6819 args: vec![right],
6820 super_call,
6821 },
6822 line,
6823 });
6824 }
6825 }
6826 self.validate_assignment(&expr, &right, line)?;
6827 Ok(Expr {
6828 kind: ExprKind::Assign {
6829 target: Box::new(expr),
6830 value: Box::new(right),
6831 },
6832 line,
6833 })
6834 }
6835 Token::PlusAssign => {
6836 self.advance();
6837 let r = self.parse_assign_expr()?;
6838 Ok(Expr {
6839 kind: ExprKind::CompoundAssign {
6840 target: Box::new(expr),
6841 op: BinOp::Add,
6842 value: Box::new(r),
6843 },
6844 line,
6845 })
6846 }
6847 Token::MinusAssign => {
6848 self.advance();
6849 let r = self.parse_assign_expr()?;
6850 Ok(Expr {
6851 kind: ExprKind::CompoundAssign {
6852 target: Box::new(expr),
6853 op: BinOp::Sub,
6854 value: Box::new(r),
6855 },
6856 line,
6857 })
6858 }
6859 Token::MulAssign => {
6860 self.advance();
6861 let r = self.parse_assign_expr()?;
6862 Ok(Expr {
6863 kind: ExprKind::CompoundAssign {
6864 target: Box::new(expr),
6865 op: BinOp::Mul,
6866 value: Box::new(r),
6867 },
6868 line,
6869 })
6870 }
6871 Token::DivAssign => {
6872 self.advance();
6873 let r = self.parse_assign_expr()?;
6874 Ok(Expr {
6875 kind: ExprKind::CompoundAssign {
6876 target: Box::new(expr),
6877 op: BinOp::Div,
6878 value: Box::new(r),
6879 },
6880 line,
6881 })
6882 }
6883 Token::ModAssign => {
6884 self.advance();
6885 let r = self.parse_assign_expr()?;
6886 Ok(Expr {
6887 kind: ExprKind::CompoundAssign {
6888 target: Box::new(expr),
6889 op: BinOp::Mod,
6890 value: Box::new(r),
6891 },
6892 line,
6893 })
6894 }
6895 Token::PowAssign => {
6896 self.advance();
6897 let r = self.parse_assign_expr()?;
6898 Ok(Expr {
6899 kind: ExprKind::CompoundAssign {
6900 target: Box::new(expr),
6901 op: BinOp::Pow,
6902 value: Box::new(r),
6903 },
6904 line,
6905 })
6906 }
6907 Token::XAssign => {
6908 self.advance();
6913 let r = self.parse_assign_expr()?;
6914 let lhs_for_repeat = expr.clone();
6915 Ok(Expr {
6916 kind: ExprKind::Assign {
6917 target: Box::new(expr),
6918 value: Box::new(Expr {
6919 kind: ExprKind::Repeat {
6920 expr: Box::new(lhs_for_repeat),
6921 count: Box::new(r),
6922 list_repeat: false,
6923 },
6924 line,
6925 }),
6926 },
6927 line,
6928 })
6929 }
6930 Token::DotAssign => {
6931 self.advance();
6932 let r = self.parse_assign_expr()?;
6933 Ok(Expr {
6934 kind: ExprKind::CompoundAssign {
6935 target: Box::new(expr),
6936 op: BinOp::Concat,
6937 value: Box::new(r),
6938 },
6939 line,
6940 })
6941 }
6942 Token::BitAndAssign => {
6943 self.advance();
6944 let r = self.parse_assign_expr()?;
6945 Ok(Expr {
6946 kind: ExprKind::CompoundAssign {
6947 target: Box::new(expr),
6948 op: BinOp::BitAnd,
6949 value: Box::new(r),
6950 },
6951 line,
6952 })
6953 }
6954 Token::BitOrAssign => {
6955 self.advance();
6956 let r = self.parse_assign_expr()?;
6957 Ok(Expr {
6958 kind: ExprKind::CompoundAssign {
6959 target: Box::new(expr),
6960 op: BinOp::BitOr,
6961 value: Box::new(r),
6962 },
6963 line,
6964 })
6965 }
6966 Token::XorAssign => {
6967 self.advance();
6968 let r = self.parse_assign_expr()?;
6969 Ok(Expr {
6970 kind: ExprKind::CompoundAssign {
6971 target: Box::new(expr),
6972 op: BinOp::BitXor,
6973 value: Box::new(r),
6974 },
6975 line,
6976 })
6977 }
6978 Token::ShiftLeftAssign => {
6979 self.advance();
6980 let r = self.parse_assign_expr()?;
6981 Ok(Expr {
6982 kind: ExprKind::CompoundAssign {
6983 target: Box::new(expr),
6984 op: BinOp::ShiftLeft,
6985 value: Box::new(r),
6986 },
6987 line,
6988 })
6989 }
6990 Token::ShiftRightAssign => {
6991 self.advance();
6992 let r = self.parse_assign_expr()?;
6993 Ok(Expr {
6994 kind: ExprKind::CompoundAssign {
6995 target: Box::new(expr),
6996 op: BinOp::ShiftRight,
6997 value: Box::new(r),
6998 },
6999 line,
7000 })
7001 }
7002 Token::OrAssign => {
7003 self.advance();
7004 let r = self.parse_assign_expr()?;
7005 Ok(Expr {
7006 kind: ExprKind::CompoundAssign {
7007 target: Box::new(expr),
7008 op: BinOp::LogOr,
7009 value: Box::new(r),
7010 },
7011 line,
7012 })
7013 }
7014 Token::DefinedOrAssign => {
7015 self.advance();
7016 let r = self.parse_assign_expr()?;
7017 Ok(Expr {
7018 kind: ExprKind::CompoundAssign {
7019 target: Box::new(expr),
7020 op: BinOp::DefinedOr,
7021 value: Box::new(r),
7022 },
7023 line,
7024 })
7025 }
7026 Token::AndAssign => {
7027 self.advance();
7028 let r = self.parse_assign_expr()?;
7029 Ok(Expr {
7030 kind: ExprKind::CompoundAssign {
7031 target: Box::new(expr),
7032 op: BinOp::LogAnd,
7033 value: Box::new(r),
7034 },
7035 line,
7036 })
7037 }
7038 _ => Ok(expr),
7039 }
7040 }
7041
7042 fn parse_ternary(&mut self) -> StrykeResult<Expr> {
7043 let expr = self.parse_pipe_forward()?;
7044 if self.eat(&Token::Question) {
7045 let line = expr.line;
7046 self.suppress_colon_range = self.suppress_colon_range.saturating_add(1);
7047 let then_expr = self.parse_assign_expr();
7048 self.suppress_colon_range = self.suppress_colon_range.saturating_sub(1);
7049 let then_expr = then_expr?;
7050 self.expect(&Token::Colon)?;
7051 let else_expr = self.parse_assign_expr()?;
7052 return Ok(Expr {
7053 kind: ExprKind::Ternary {
7054 condition: Box::new(expr),
7055 then_expr: Box::new(then_expr),
7056 else_expr: Box::new(else_expr),
7057 },
7058 line,
7059 });
7060 }
7061 Ok(expr)
7062 }
7063
7064 fn parse_pipe_forward(&mut self) -> StrykeResult<Expr> {
7070 let mut left = self.parse_range()?;
7077 if self.no_pipe_forward_depth > 0 {
7083 return Ok(left);
7084 }
7085 while matches!(self.peek(), Token::PipeForward) {
7086 if crate::compat_mode() {
7087 return Err(self.syntax_err(
7088 "pipe-forward operator `|>` is a stryke extension (disabled by --compat)",
7089 left.line,
7090 ));
7091 }
7092 let line = left.line;
7093 self.advance();
7094 self.pipe_rhs_depth = self.pipe_rhs_depth.saturating_add(1);
7097 let right_result = self.parse_range();
7101 self.pipe_rhs_depth = self.pipe_rhs_depth.saturating_sub(1);
7102 let right = right_result?;
7103 left = self.pipe_forward_apply(left, right, line)?;
7104 }
7105 Ok(left)
7106 }
7107
7108 fn pipe_forward_apply(&self, lhs: Expr, rhs: Expr, line: usize) -> StrykeResult<Expr> {
7130 let Expr { kind, line: rline } = rhs;
7131 let new_kind = match kind {
7132 ExprKind::FuncCall { name, mut args } => {
7134 let dispatch_name: &str = name.strip_prefix("CORE::").unwrap_or(name.as_str());
7137 match dispatch_name {
7138 "puniq" | "uniq" | "distinct" | "flatten" | "set" | "list_count"
7139 | "list_size" | "count" | "size" | "cnt" | "len" | "with_index" | "shuffle"
7140 | "shuffled" | "frequencies" | "freq" | "pfrequencies" | "pfreq"
7141 | "interleave" | "ddump" | "stringify" | "str" | "lines" | "words"
7142 | "chars" | "digits" | "letters" | "letters_uc" | "letters_lc"
7143 | "punctuation" | "numbers" | "graphemes" | "columns" | "sentences"
7144 | "paragraphs" | "sections" | "trim" | "avg" | "to_json" | "to_csv"
7145 | "to_toml" | "to_yaml" | "to_xml" | "to_html" | "from_json" | "from_csv"
7146 | "from_toml" | "from_yaml" | "from_xml" | "to_markdown" | "to_table"
7147 | "xopen" | "clip" | "sparkline" | "bar_chart" | "flame" | "stddev"
7148 | "squared" | "sq" | "square" | "cubed" | "cb" | "cube" | "normalize"
7149 | "snake_case" | "camel_case" | "kebab_case" => {
7150 if args.is_empty() {
7151 args.push(lhs);
7152 } else {
7153 args[0] = lhs;
7154 }
7155 }
7156 "chunked" | "windowed" => {
7157 if args.is_empty() {
7158 return Err(self.syntax_err(
7159 "|>: chunked(N) / windowed(N) needs size — e.g. `@a |> windowed(2)`",
7160 line,
7161 ));
7162 }
7163 args.insert(0, lhs);
7164 }
7165 "reduce" | "fold" => {
7166 args.push(lhs);
7167 }
7168 "grep_v" | "pluck" | "tee" | "nth" | "chunk" => {
7169 args.push(lhs);
7175 }
7176 "enumerate" | "dedup" => {
7177 args.insert(0, lhs);
7180 }
7181 "clamp" => {
7182 args.push(lhs);
7184 }
7185 n if Self::is_block_then_list_pipe_builtin(n) => {
7186 if args.len() < 2 {
7187 return Err(self.syntax_err(
7188 format!(
7189 "|>: `{name}` needs {{ BLOCK }}, LIST so the list can receive the pipe"
7190 ),
7191 line,
7192 ));
7193 }
7194 args[1] = lhs;
7195 }
7196 "take" | "head" | "tail" | "drop" => {
7197 if args.is_empty() {
7198 return Err(self.syntax_err(
7199 "|>: `{name}` needs N last — e.g. `@a |> take(3)` for `take(@a, 3)`",
7200 line,
7201 ));
7202 }
7203 args.insert(0, lhs);
7205 }
7206 _ => {
7207 if self.thread_last_mode {
7208 args.push(lhs);
7209 } else {
7210 args.insert(0, lhs);
7211 }
7212 }
7213 }
7214 ExprKind::FuncCall { name, args }
7215 }
7216 ExprKind::MethodCall {
7217 object,
7218 method,
7219 mut args,
7220 super_call,
7221 } => {
7222 if self.thread_last_mode {
7223 args.push(lhs);
7224 } else {
7225 args.insert(0, lhs);
7226 }
7227 ExprKind::MethodCall {
7228 object,
7229 method,
7230 args,
7231 super_call,
7232 }
7233 }
7234 ExprKind::IndirectCall {
7235 target,
7236 mut args,
7237 ampersand,
7238 pass_caller_arglist: _,
7239 } => {
7240 if self.thread_last_mode {
7241 args.push(lhs);
7242 } else {
7243 args.insert(0, lhs);
7244 }
7245 ExprKind::IndirectCall {
7246 target,
7247 args,
7248 ampersand,
7249 pass_caller_arglist: false,
7252 }
7253 }
7254
7255 ExprKind::Print { handle, mut args } => {
7257 if self.thread_last_mode {
7258 args.push(lhs);
7259 } else {
7260 args.insert(0, lhs);
7261 }
7262 ExprKind::Print { handle, args }
7263 }
7264 ExprKind::Say { handle, mut args } => {
7265 if self.thread_last_mode {
7266 args.push(lhs);
7267 } else {
7268 args.insert(0, lhs);
7269 }
7270 ExprKind::Say { handle, args }
7271 }
7272 ExprKind::Printf { handle, mut args } => {
7273 if self.thread_last_mode {
7274 args.push(lhs);
7275 } else {
7276 args.insert(0, lhs);
7277 }
7278 ExprKind::Printf { handle, args }
7279 }
7280 ExprKind::Die(mut args) => {
7281 if self.thread_last_mode {
7282 args.push(lhs);
7283 } else {
7284 args.insert(0, lhs);
7285 }
7286 ExprKind::Die(args)
7287 }
7288 ExprKind::Warn(mut args) => {
7289 if self.thread_last_mode {
7290 args.push(lhs);
7291 } else {
7292 args.insert(0, lhs);
7293 }
7294 ExprKind::Warn(args)
7295 }
7296
7297 ExprKind::Sprintf { format, mut args } => {
7303 if self.thread_last_mode {
7304 args.push(lhs);
7305 } else {
7306 args.insert(0, lhs);
7307 }
7308 ExprKind::Sprintf { format, args }
7309 }
7310
7311 ExprKind::System(mut args) => {
7313 if self.thread_last_mode {
7314 args.push(lhs);
7315 } else {
7316 args.insert(0, lhs);
7317 }
7318 ExprKind::System(args)
7319 }
7320 ExprKind::Exec(mut args) => {
7321 if self.thread_last_mode {
7322 args.push(lhs);
7323 } else {
7324 args.insert(0, lhs);
7325 }
7326 ExprKind::Exec(args)
7327 }
7328 ExprKind::Unlink(mut args) => {
7329 if self.thread_last_mode {
7330 args.push(lhs);
7331 } else {
7332 args.insert(0, lhs);
7333 }
7334 ExprKind::Unlink(args)
7335 }
7336 ExprKind::Chmod(mut args) => {
7337 if self.thread_last_mode {
7338 args.push(lhs);
7339 } else {
7340 args.insert(0, lhs);
7341 }
7342 ExprKind::Chmod(args)
7343 }
7344 ExprKind::Chown(mut args) => {
7345 if self.thread_last_mode {
7346 args.push(lhs);
7347 } else {
7348 args.insert(0, lhs);
7349 }
7350 ExprKind::Chown(args)
7351 }
7352 ExprKind::Glob(mut args) => {
7353 if self.thread_last_mode {
7354 args.push(lhs);
7355 } else {
7356 args.insert(0, lhs);
7357 }
7358 ExprKind::Glob(args)
7359 }
7360 ExprKind::Files(mut args) => {
7361 if self.thread_last_mode {
7362 args.push(lhs);
7363 } else {
7364 args.insert(0, lhs);
7365 }
7366 ExprKind::Files(args)
7367 }
7368 ExprKind::Filesf(mut args) => {
7369 if self.thread_last_mode {
7370 args.push(lhs);
7371 } else {
7372 args.insert(0, lhs);
7373 }
7374 ExprKind::Filesf(args)
7375 }
7376 ExprKind::FilesfRecursive(mut args) => {
7377 if self.thread_last_mode {
7378 args.push(lhs);
7379 } else {
7380 args.insert(0, lhs);
7381 }
7382 ExprKind::FilesfRecursive(args)
7383 }
7384 ExprKind::Dirs(mut args) => {
7385 if self.thread_last_mode {
7386 args.push(lhs);
7387 } else {
7388 args.insert(0, lhs);
7389 }
7390 ExprKind::Dirs(args)
7391 }
7392 ExprKind::DirsRecursive(mut args) => {
7393 if self.thread_last_mode {
7394 args.push(lhs);
7395 } else {
7396 args.insert(0, lhs);
7397 }
7398 ExprKind::DirsRecursive(args)
7399 }
7400 ExprKind::SymLinks(mut args) => {
7401 if self.thread_last_mode {
7402 args.push(lhs);
7403 } else {
7404 args.insert(0, lhs);
7405 }
7406 ExprKind::SymLinks(args)
7407 }
7408 ExprKind::Sockets(mut args) => {
7409 if self.thread_last_mode {
7410 args.push(lhs);
7411 } else {
7412 args.insert(0, lhs);
7413 }
7414 ExprKind::Sockets(args)
7415 }
7416 ExprKind::Pipes(mut args) => {
7417 if self.thread_last_mode {
7418 args.push(lhs);
7419 } else {
7420 args.insert(0, lhs);
7421 }
7422 ExprKind::Pipes(args)
7423 }
7424 ExprKind::BlockDevices(mut args) => {
7425 if self.thread_last_mode {
7426 args.push(lhs);
7427 } else {
7428 args.insert(0, lhs);
7429 }
7430 ExprKind::BlockDevices(args)
7431 }
7432 ExprKind::CharDevices(mut args) => {
7433 if self.thread_last_mode {
7434 args.push(lhs);
7435 } else {
7436 args.insert(0, lhs);
7437 }
7438 ExprKind::CharDevices(args)
7439 }
7440 ExprKind::GlobPar { mut args, progress } => {
7441 if self.thread_last_mode {
7442 args.push(lhs);
7443 } else {
7444 args.insert(0, lhs);
7445 }
7446 ExprKind::GlobPar { args, progress }
7447 }
7448 ExprKind::ParSed { mut args, progress } => {
7449 if self.thread_last_mode {
7450 args.push(lhs);
7451 } else {
7452 args.insert(0, lhs);
7453 }
7454 ExprKind::ParSed { args, progress }
7455 }
7456
7457 ExprKind::Length(_) => ExprKind::Length(Box::new(lhs)),
7459 ExprKind::Abs(_) => ExprKind::Abs(Box::new(lhs)),
7460 ExprKind::Int(_) => ExprKind::Int(Box::new(lhs)),
7461 ExprKind::Sqrt(_) => ExprKind::Sqrt(Box::new(lhs)),
7462 ExprKind::Sin(_) => ExprKind::Sin(Box::new(lhs)),
7463 ExprKind::Cos(_) => ExprKind::Cos(Box::new(lhs)),
7464 ExprKind::Exp(_) => ExprKind::Exp(Box::new(lhs)),
7465 ExprKind::Log(_) => ExprKind::Log(Box::new(lhs)),
7466 ExprKind::Hex(_) => ExprKind::Hex(Box::new(lhs)),
7467 ExprKind::Oct(_) => ExprKind::Oct(Box::new(lhs)),
7468 ExprKind::Lc(_) => ExprKind::Lc(Box::new(lhs)),
7469 ExprKind::Uc(_) => ExprKind::Uc(Box::new(lhs)),
7470 ExprKind::Lcfirst(_) => ExprKind::Lcfirst(Box::new(lhs)),
7471 ExprKind::Ucfirst(_) => ExprKind::Ucfirst(Box::new(lhs)),
7472 ExprKind::Fc(_) => ExprKind::Fc(Box::new(lhs)),
7473 ExprKind::Chr(_) => ExprKind::Chr(Box::new(lhs)),
7474 ExprKind::Ord(_) => ExprKind::Ord(Box::new(lhs)),
7475 ExprKind::Chomp(_) => ExprKind::Chomp(Box::new(lhs)),
7476 ExprKind::Chop(_) => ExprKind::Chop(Box::new(lhs)),
7477 ExprKind::Defined(_) => ExprKind::Defined(Box::new(lhs)),
7478 ExprKind::Ref(_) => ExprKind::Ref(Box::new(lhs)),
7479 ExprKind::ScalarContext(_) => ExprKind::ScalarContext(Box::new(lhs)),
7480 ExprKind::Keys(_) => ExprKind::Keys(Box::new(lhs)),
7481 ExprKind::Values(_) => ExprKind::Values(Box::new(lhs)),
7482 ExprKind::Each(_) => ExprKind::Each(Box::new(lhs)),
7483 ExprKind::Pop(_) => ExprKind::Pop(Box::new(lhs)),
7484 ExprKind::Shift(_) => ExprKind::Shift(Box::new(lhs)),
7485 ExprKind::Delete(_) => ExprKind::Delete(Box::new(lhs)),
7486 ExprKind::Exists(_) => ExprKind::Exists(Box::new(lhs)),
7487 ExprKind::ReverseExpr(_) => ExprKind::ReverseExpr(Box::new(lhs)),
7488 ExprKind::Rev(_) => ExprKind::Rev(Box::new(lhs)),
7489 ExprKind::Slurp(_) => ExprKind::Slurp(Box::new(lhs)),
7490 ExprKind::Swallow(_) => ExprKind::Swallow(Box::new(lhs)),
7491 ExprKind::Ingest(_) => ExprKind::Ingest(Box::new(lhs)),
7492 ExprKind::Burp(_) => ExprKind::Burp(Box::new(lhs)),
7493 ExprKind::God(_) => ExprKind::God(Box::new(lhs)),
7494 ExprKind::Capture(_) => ExprKind::Capture(Box::new(lhs)),
7495 ExprKind::Qx(_) => ExprKind::Qx(Box::new(lhs)),
7496 ExprKind::FetchUrl(_) => ExprKind::FetchUrl(Box::new(lhs)),
7497 ExprKind::Close(_) => ExprKind::Close(Box::new(lhs)),
7498 ExprKind::Chdir(_) => ExprKind::Chdir(Box::new(lhs)),
7499 ExprKind::Readdir(_) => ExprKind::Readdir(Box::new(lhs)),
7500 ExprKind::Closedir(_) => ExprKind::Closedir(Box::new(lhs)),
7501 ExprKind::Rewinddir(_) => ExprKind::Rewinddir(Box::new(lhs)),
7502 ExprKind::Telldir(_) => ExprKind::Telldir(Box::new(lhs)),
7503 ExprKind::Stat(_) => ExprKind::Stat(Box::new(lhs)),
7504 ExprKind::Lstat(_) => ExprKind::Lstat(Box::new(lhs)),
7505 ExprKind::Readlink(_) => ExprKind::Readlink(Box::new(lhs)),
7506 ExprKind::Study(_) => ExprKind::Study(Box::new(lhs)),
7507 ExprKind::Await(_) => ExprKind::Await(Box::new(lhs)),
7508 ExprKind::Eval(_) => ExprKind::Eval(Box::new(lhs)),
7509 ExprKind::Rand(_) => ExprKind::Rand(Some(Box::new(lhs))),
7510 ExprKind::Srand(_) => ExprKind::Srand(Some(Box::new(lhs))),
7511 ExprKind::Pos(_) => ExprKind::Pos(Some(Box::new(lhs))),
7512 ExprKind::Exit(_) => ExprKind::Exit(Some(Box::new(lhs))),
7513
7514 ExprKind::MapExpr {
7516 block,
7517 list: _,
7518 flatten_array_refs,
7519 stream,
7520 } => ExprKind::MapExpr {
7521 block,
7522 list: Box::new(lhs),
7523 flatten_array_refs,
7524 stream,
7525 },
7526 ExprKind::MapExprComma {
7527 expr,
7528 list: _,
7529 flatten_array_refs,
7530 stream,
7531 } => ExprKind::MapExprComma {
7532 expr,
7533 list: Box::new(lhs),
7534 flatten_array_refs,
7535 stream,
7536 },
7537 ExprKind::GrepExpr {
7538 block,
7539 list: _,
7540 keyword,
7541 } => ExprKind::GrepExpr {
7542 block,
7543 list: Box::new(lhs),
7544 keyword,
7545 },
7546 ExprKind::GrepExprComma {
7547 expr,
7548 list: _,
7549 keyword,
7550 } => ExprKind::GrepExprComma {
7551 expr,
7552 list: Box::new(lhs),
7553 keyword,
7554 },
7555 ExprKind::ForEachExpr { block, list: _ } => ExprKind::ForEachExpr {
7556 block,
7557 list: Box::new(lhs),
7558 },
7559 ExprKind::SortExpr { cmp, list: _ } => ExprKind::SortExpr {
7560 cmp,
7561 list: Box::new(lhs),
7562 },
7563 ExprKind::JoinExpr { separator, list: _ } => ExprKind::JoinExpr {
7564 separator,
7565 list: Box::new(lhs),
7566 },
7567 ExprKind::ReduceExpr { block, list: _ } => ExprKind::ReduceExpr {
7568 block,
7569 list: Box::new(lhs),
7570 },
7571 ExprKind::PMapExpr {
7572 block,
7573 list: _,
7574 progress,
7575 flat_outputs,
7576 on_cluster,
7577 stream,
7578 } => ExprKind::PMapExpr {
7579 block,
7580 list: Box::new(lhs),
7581 progress,
7582 flat_outputs,
7583 on_cluster,
7584 stream,
7585 },
7586 ExprKind::ParExpr { block, list: _ } => ExprKind::ParExpr {
7587 block,
7588 list: Box::new(lhs),
7589 },
7590 ExprKind::ParReduceExpr {
7591 extract_block,
7592 reduce_block,
7593 list: _,
7594 } => ExprKind::ParReduceExpr {
7595 extract_block,
7596 reduce_block,
7597 list: Box::new(lhs),
7598 },
7599 ExprKind::PMapChunkedExpr {
7600 chunk_size,
7601 block,
7602 list: _,
7603 progress,
7604 } => ExprKind::PMapChunkedExpr {
7605 chunk_size,
7606 block,
7607 list: Box::new(lhs),
7608 progress,
7609 },
7610 ExprKind::PGrepExpr {
7611 block,
7612 list: _,
7613 progress,
7614 stream,
7615 } => ExprKind::PGrepExpr {
7616 block,
7617 list: Box::new(lhs),
7618 progress,
7619 stream,
7620 },
7621 ExprKind::PForExpr {
7622 block,
7623 list: _,
7624 progress,
7625 } => ExprKind::PForExpr {
7626 block,
7627 list: Box::new(lhs),
7628 progress,
7629 },
7630 ExprKind::PSortExpr {
7631 cmp,
7632 list: _,
7633 progress,
7634 } => ExprKind::PSortExpr {
7635 cmp,
7636 list: Box::new(lhs),
7637 progress,
7638 },
7639 ExprKind::PReduceExpr {
7640 block,
7641 list: _,
7642 progress,
7643 } => ExprKind::PReduceExpr {
7644 block,
7645 list: Box::new(lhs),
7646 progress,
7647 },
7648 ExprKind::PcacheExpr {
7649 block,
7650 list: _,
7651 progress,
7652 } => ExprKind::PcacheExpr {
7653 block,
7654 list: Box::new(lhs),
7655 progress,
7656 },
7657 ExprKind::PReduceInitExpr {
7658 init,
7659 block,
7660 list: _,
7661 progress,
7662 } => ExprKind::PReduceInitExpr {
7663 init,
7664 block,
7665 list: Box::new(lhs),
7666 progress,
7667 },
7668 ExprKind::PMapReduceExpr {
7669 map_block,
7670 reduce_block,
7671 list: _,
7672 progress,
7673 } => ExprKind::PMapReduceExpr {
7674 map_block,
7675 reduce_block,
7676 list: Box::new(lhs),
7677 progress,
7678 },
7679
7680 ExprKind::Push { array, mut values } => {
7685 values.insert(0, lhs);
7686 ExprKind::Push { array, values }
7687 }
7688 ExprKind::Unshift { array, mut values } => {
7689 values.insert(0, lhs);
7690 ExprKind::Unshift { array, values }
7691 }
7692
7693 ExprKind::SplitExpr {
7695 pattern,
7696 string: _,
7697 limit,
7698 } => ExprKind::SplitExpr {
7699 pattern,
7700 string: Box::new(lhs),
7701 limit,
7702 },
7703
7704 ExprKind::Substitution {
7708 pattern,
7709 replacement,
7710 mut flags,
7711 expr: _,
7712 delim,
7713 } => {
7714 if !flags.contains('r') {
7715 flags.push('r');
7716 }
7717 ExprKind::Substitution {
7718 expr: Box::new(lhs),
7719 pattern,
7720 replacement,
7721 flags,
7722 delim,
7723 }
7724 }
7725 ExprKind::Transliterate {
7726 from,
7727 to,
7728 mut flags,
7729 expr: _,
7730 delim,
7731 } => {
7732 if !flags.contains('r') {
7733 flags.push('r');
7734 }
7735 ExprKind::Transliterate {
7736 expr: Box::new(lhs),
7737 from,
7738 to,
7739 flags,
7740 delim,
7741 }
7742 }
7743 ExprKind::Match {
7744 pattern,
7745 flags,
7746 scalar_g,
7747 expr: _,
7748 delim,
7749 } => ExprKind::Match {
7750 expr: Box::new(lhs),
7751 pattern,
7752 flags,
7753 scalar_g,
7754 delim,
7755 },
7756 ExprKind::Regex(pattern, flags) => ExprKind::Match {
7758 expr: Box::new(lhs),
7759 pattern,
7760 flags,
7761 scalar_g: false,
7762 delim: '/',
7763 },
7764
7765 ExprKind::Bareword(name) => match name.as_str() {
7767 "reverse" => {
7768 if crate::no_interop_mode() {
7769 return Err(self.syntax_err(
7770 "stryke uses `rev` instead of `reverse` (--no-interop)",
7771 line,
7772 ));
7773 }
7774 ExprKind::ReverseExpr(Box::new(lhs))
7775 }
7776 "rv" | "reversed" | "rev" => ExprKind::Rev(Box::new(lhs)),
7777 "uq" | "uniq" | "distinct" => ExprKind::FuncCall {
7778 name: "uniq".to_string(),
7779 args: vec![lhs],
7780 },
7781 "fl" | "flatten" => ExprKind::FuncCall {
7782 name: "flatten".to_string(),
7783 args: vec![lhs],
7784 },
7785 _ => ExprKind::FuncCall {
7786 name,
7787 args: vec![lhs],
7788 },
7789 },
7790
7791 kind @ (ExprKind::ScalarVar(_)
7793 | ExprKind::ArrayElement { .. }
7794 | ExprKind::HashElement { .. }
7795 | ExprKind::Deref { .. }
7796 | ExprKind::ArrowDeref { .. }
7797 | ExprKind::CodeRef { .. }
7798 | ExprKind::SubroutineRef(_)
7799 | ExprKind::SubroutineCodeRef(_)
7800 | ExprKind::DynamicSubCodeRef(_)) => ExprKind::IndirectCall {
7801 target: Box::new(Expr { kind, line: rline }),
7802 args: vec![lhs],
7803 ampersand: false,
7804 pass_caller_arglist: false,
7805 },
7806
7807 ExprKind::Do(inner) if matches!(inner.kind, ExprKind::CodeRef { .. }) => {
7811 ExprKind::IndirectCall {
7812 target: inner,
7813 args: vec![lhs],
7814 ampersand: false,
7815 pass_caller_arglist: false,
7816 }
7817 }
7818
7819 other => {
7820 return Err(self.syntax_err(
7821 format!(
7822 "right-hand side of `|>` must be a call, builtin, or coderef \
7823 expression (got {})",
7824 Self::expr_kind_name(&other)
7825 ),
7826 line,
7827 ));
7828 }
7829 };
7830 Ok(Expr {
7831 kind: new_kind,
7832 line,
7833 })
7834 }
7835
7836 fn expr_kind_name(kind: &ExprKind) -> &'static str {
7838 match kind {
7839 ExprKind::Integer(_) | ExprKind::Float(_) => "numeric literal",
7840 ExprKind::String(_) | ExprKind::InterpolatedString(_) => "string literal",
7841 ExprKind::BinOp { .. } => "binary expression",
7842 ExprKind::UnaryOp { .. } => "unary expression",
7843 ExprKind::Ternary { .. } => "ternary expression",
7844 ExprKind::Assign { .. } | ExprKind::CompoundAssign { .. } => "assignment",
7845 ExprKind::List(_) => "list expression",
7846 ExprKind::Range { .. } => "range expression",
7847 _ => "expression",
7848 }
7849 }
7850
7851 fn parse_or_word(&mut self) -> StrykeResult<Expr> {
7853 let mut left = self.parse_and_word()?;
7854 while matches!(self.peek(), Token::LogOrWord) {
7855 let line = left.line;
7856 self.advance();
7857 let right = self.parse_and_word()?;
7858 left = Expr {
7859 kind: ExprKind::BinOp {
7860 left: Box::new(left),
7861 op: BinOp::LogOrWord,
7862 right: Box::new(right),
7863 },
7864 line,
7865 };
7866 }
7867 Ok(left)
7868 }
7869
7870 fn parse_and_word(&mut self) -> StrykeResult<Expr> {
7871 let mut left = self.parse_not_word()?;
7872 while matches!(self.peek(), Token::LogAndWord) {
7873 let line = left.line;
7874 self.advance();
7875 let right = self.parse_not_word()?;
7876 left = Expr {
7877 kind: ExprKind::BinOp {
7878 left: Box::new(left),
7879 op: BinOp::LogAndWord,
7880 right: Box::new(right),
7881 },
7882 line,
7883 };
7884 }
7885 Ok(left)
7886 }
7887
7888 fn parse_not_word(&mut self) -> StrykeResult<Expr> {
7889 if matches!(self.peek(), Token::LogNotWord) {
7890 let line = self.peek_line();
7891 self.advance();
7892 let expr = self.parse_not_word()?;
7893 return Ok(Expr {
7894 kind: ExprKind::UnaryOp {
7895 op: UnaryOp::LogNotWord,
7896 expr: Box::new(expr),
7897 },
7898 line,
7899 });
7900 }
7901 self.parse_assign_expr()
7904 }
7905
7906 fn parse_log_or(&mut self) -> StrykeResult<Expr> {
7907 let mut left = self.parse_log_and()?;
7908 loop {
7909 let op = match self.peek() {
7910 Token::LogOr => BinOp::LogOr,
7911 Token::DefinedOr => BinOp::DefinedOr,
7912 _ => break,
7913 };
7914 let line = left.line;
7915 self.advance();
7916 let right = self.parse_log_and()?;
7917 left = Expr {
7918 kind: ExprKind::BinOp {
7919 left: Box::new(left),
7920 op,
7921 right: Box::new(right),
7922 },
7923 line,
7924 };
7925 }
7926 Ok(left)
7927 }
7928
7929 fn parse_log_and(&mut self) -> StrykeResult<Expr> {
7930 let mut left = self.parse_bit_or()?;
7931 while matches!(self.peek(), Token::LogAnd) {
7932 let line = left.line;
7933 self.advance();
7934 let right = self.parse_bit_or()?;
7935 left = Expr {
7936 kind: ExprKind::BinOp {
7937 left: Box::new(left),
7938 op: BinOp::LogAnd,
7939 right: Box::new(right),
7940 },
7941 line,
7942 };
7943 }
7944 Ok(left)
7945 }
7946
7947 fn parse_bit_or(&mut self) -> StrykeResult<Expr> {
7948 let mut left = self.parse_bit_xor()?;
7949 while matches!(self.peek(), Token::BitOr) {
7950 let line = left.line;
7951 self.advance();
7952 let right = self.parse_bit_xor()?;
7953 left = Expr {
7954 kind: ExprKind::BinOp {
7955 left: Box::new(left),
7956 op: BinOp::BitOr,
7957 right: Box::new(right),
7958 },
7959 line,
7960 };
7961 }
7962 Ok(left)
7963 }
7964
7965 fn parse_bit_xor(&mut self) -> StrykeResult<Expr> {
7966 let mut left = self.parse_bit_and()?;
7967 while matches!(self.peek(), Token::BitXor) {
7968 let line = left.line;
7969 self.advance();
7970 let right = self.parse_bit_and()?;
7971 left = Expr {
7972 kind: ExprKind::BinOp {
7973 left: Box::new(left),
7974 op: BinOp::BitXor,
7975 right: Box::new(right),
7976 },
7977 line,
7978 };
7979 }
7980 Ok(left)
7981 }
7982
7983 fn parse_bit_and(&mut self) -> StrykeResult<Expr> {
7984 let mut left = self.parse_equality()?;
7985 while matches!(self.peek(), Token::BitAnd) {
7986 let line = left.line;
7987 self.advance();
7988 let right = self.parse_equality()?;
7989 left = Expr {
7990 kind: ExprKind::BinOp {
7991 left: Box::new(left),
7992 op: BinOp::BitAnd,
7993 right: Box::new(right),
7994 },
7995 line,
7996 };
7997 }
7998 Ok(left)
7999 }
8000
8001 fn parse_equality(&mut self) -> StrykeResult<Expr> {
8002 let mut left = self.parse_comparison()?;
8003 loop {
8004 let op = match self.peek() {
8005 Token::NumEq => BinOp::NumEq,
8006 Token::NumNe => BinOp::NumNe,
8007 Token::StrEq => BinOp::StrEq,
8008 Token::StrNe => BinOp::StrNe,
8009 Token::Spaceship => BinOp::Spaceship,
8010 Token::StrCmp => BinOp::StrCmp,
8011 _ => break,
8012 };
8013 let line = left.line;
8014 self.advance();
8015 let right = self.parse_comparison()?;
8016 left = Expr {
8017 kind: ExprKind::BinOp {
8018 left: Box::new(left),
8019 op,
8020 right: Box::new(right),
8021 },
8022 line,
8023 };
8024 }
8025 Ok(left)
8026 }
8027
8028 fn parse_comparison(&mut self) -> StrykeResult<Expr> {
8029 let left = self.parse_shift()?;
8030 let first_op = match self.peek() {
8031 Token::NumLt => BinOp::NumLt,
8032 Token::NumGt => BinOp::NumGt,
8033 Token::NumLe => BinOp::NumLe,
8034 Token::NumGe => BinOp::NumGe,
8035 Token::StrLt => BinOp::StrLt,
8036 Token::StrGt => BinOp::StrGt,
8037 Token::StrLe => BinOp::StrLe,
8038 Token::StrGe => BinOp::StrGe,
8039 _ => return Ok(left),
8040 };
8041 let line = left.line;
8042 self.advance();
8043 let middle = self.parse_shift()?;
8044
8045 let second_op = match self.peek() {
8046 Token::NumLt => Some(BinOp::NumLt),
8047 Token::NumGt => Some(BinOp::NumGt),
8048 Token::NumLe => Some(BinOp::NumLe),
8049 Token::NumGe => Some(BinOp::NumGe),
8050 Token::StrLt => Some(BinOp::StrLt),
8051 Token::StrGt => Some(BinOp::StrGt),
8052 Token::StrLe => Some(BinOp::StrLe),
8053 Token::StrGe => Some(BinOp::StrGe),
8054 _ => None,
8055 };
8056
8057 if second_op.is_none() {
8058 return Ok(Expr {
8059 kind: ExprKind::BinOp {
8060 left: Box::new(left),
8061 op: first_op,
8062 right: Box::new(middle),
8063 },
8064 line,
8065 });
8066 }
8067
8068 let mut operands = vec![left, middle];
8071 let mut ops = vec![first_op];
8072
8073 loop {
8074 let op = match self.peek() {
8075 Token::NumLt => BinOp::NumLt,
8076 Token::NumGt => BinOp::NumGt,
8077 Token::NumLe => BinOp::NumLe,
8078 Token::NumGe => BinOp::NumGe,
8079 Token::StrLt => BinOp::StrLt,
8080 Token::StrGt => BinOp::StrGt,
8081 Token::StrLe => BinOp::StrLe,
8082 Token::StrGe => BinOp::StrGe,
8083 _ => break,
8084 };
8085 self.advance();
8086 ops.push(op);
8087 operands.push(self.parse_shift()?);
8088 }
8089
8090 let mut result = Expr {
8092 kind: ExprKind::BinOp {
8093 left: Box::new(operands[0].clone()),
8094 op: ops[0],
8095 right: Box::new(operands[1].clone()),
8096 },
8097 line,
8098 };
8099
8100 for i in 1..ops.len() {
8101 let cmp = Expr {
8102 kind: ExprKind::BinOp {
8103 left: Box::new(operands[i].clone()),
8104 op: ops[i],
8105 right: Box::new(operands[i + 1].clone()),
8106 },
8107 line,
8108 };
8109 result = Expr {
8110 kind: ExprKind::BinOp {
8111 left: Box::new(result),
8112 op: BinOp::LogAnd,
8113 right: Box::new(cmp),
8114 },
8115 line,
8116 };
8117 }
8118
8119 Ok(result)
8120 }
8121
8122 fn parse_shift(&mut self) -> StrykeResult<Expr> {
8123 let mut left = self.parse_addition()?;
8124 loop {
8125 let op = match self.peek() {
8126 Token::ShiftLeft => BinOp::ShiftLeft,
8127 Token::ShiftRight => BinOp::ShiftRight,
8128 _ => break,
8129 };
8130 let line = left.line;
8131 self.advance();
8132 let right = self.parse_addition()?;
8133 left = Expr {
8134 kind: ExprKind::BinOp {
8135 left: Box::new(left),
8136 op,
8137 right: Box::new(right),
8138 },
8139 line,
8140 };
8141 }
8142 Ok(left)
8143 }
8144
8145 fn parse_addition(&mut self) -> StrykeResult<Expr> {
8146 let mut left = self.parse_multiplication()?;
8147 loop {
8148 let op = match self.peek() {
8151 Token::Plus if self.peek_line() == self.prev_line() => BinOp::Add,
8152 Token::Minus if self.peek_line() == self.prev_line() => BinOp::Sub,
8153 Token::Dot => BinOp::Concat,
8154 _ => break,
8155 };
8156 let line = left.line;
8157 self.advance();
8158 let right = self.parse_multiplication()?;
8159 left = Expr {
8160 kind: ExprKind::BinOp {
8161 left: Box::new(left),
8162 op,
8163 right: Box::new(right),
8164 },
8165 line,
8166 };
8167 }
8168 Ok(left)
8169 }
8170
8171 fn parse_multiplication(&mut self) -> StrykeResult<Expr> {
8172 let mut left = self.parse_regex_bind()?;
8173 loop {
8174 let op = match self.peek() {
8175 Token::Star => BinOp::Mul,
8176 Token::Slash if self.suppress_slash_as_div == 0 => BinOp::Div,
8177 Token::Percent if self.peek_line() == self.prev_line() => BinOp::Mod,
8180 Token::X => {
8181 let line = left.line;
8182 let list_repeat = self.list_construct_close_pos == Some(self.pos);
8189 self.advance();
8190 let right = self.parse_regex_bind()?;
8191 left = Expr {
8192 kind: ExprKind::Repeat {
8193 expr: Box::new(left),
8194 count: Box::new(right),
8195 list_repeat,
8196 },
8197 line,
8198 };
8199 continue;
8200 }
8201 _ => break,
8202 };
8203 let line = left.line;
8204 self.advance();
8205 let right = self.parse_regex_bind()?;
8206 left = Expr {
8207 kind: ExprKind::BinOp {
8208 left: Box::new(left),
8209 op,
8210 right: Box::new(right),
8211 },
8212 line,
8213 };
8214 }
8215 Ok(left)
8216 }
8217
8218 fn parse_regex_bind(&mut self) -> StrykeResult<Expr> {
8219 let left = self.parse_unary()?;
8220 match self.peek() {
8221 Token::BindMatch => {
8222 let line = left.line;
8223 self.advance();
8224 match self.peek().clone() {
8225 Token::Regex(pattern, flags, delim) => {
8226 self.advance();
8227 Ok(Expr {
8228 kind: ExprKind::Match {
8229 expr: Box::new(left),
8230 pattern,
8231 flags,
8232 scalar_g: false,
8233 delim,
8234 },
8235 line,
8236 })
8237 }
8238 Token::Ident(ref s) if s.starts_with('\x00') => {
8239 let (Token::Ident(encoded), _) = self.advance() else {
8240 unreachable!()
8241 };
8242 let parts: Vec<&str> = encoded.split('\x00').collect();
8243 if parts.len() >= 4 && parts[1] == "s" {
8244 let delim = parts.get(5).and_then(|s| s.chars().next()).unwrap_or('/');
8245 Ok(Expr {
8246 kind: ExprKind::Substitution {
8247 expr: Box::new(left),
8248 pattern: parts[2].to_string(),
8249 replacement: parts[3].to_string(),
8250 flags: parts.get(4).unwrap_or(&"").to_string(),
8251 delim,
8252 },
8253 line,
8254 })
8255 } else if parts.len() >= 4 && parts[1] == "tr" {
8256 let delim = parts.get(5).and_then(|s| s.chars().next()).unwrap_or('/');
8257 Ok(Expr {
8258 kind: ExprKind::Transliterate {
8259 expr: Box::new(left),
8260 from: parts[2].to_string(),
8261 to: parts[3].to_string(),
8262 flags: parts.get(4).unwrap_or(&"").to_string(),
8263 delim,
8264 },
8265 line,
8266 })
8267 } else {
8268 Err(self.syntax_err("Invalid regex binding", line))
8269 }
8270 }
8271 _ => {
8272 let rhs = self.parse_unary()?;
8273 Ok(Expr {
8274 kind: ExprKind::BinOp {
8275 left: Box::new(left),
8276 op: BinOp::BindMatch,
8277 right: Box::new(rhs),
8278 },
8279 line,
8280 })
8281 }
8282 }
8283 }
8284 Token::BindNotMatch => {
8285 let line = left.line;
8286 self.advance();
8287 match self.peek().clone() {
8288 Token::Regex(pattern, flags, delim) => {
8289 self.advance();
8290 Ok(Expr {
8291 kind: ExprKind::UnaryOp {
8292 op: UnaryOp::LogNot,
8293 expr: Box::new(Expr {
8294 kind: ExprKind::Match {
8295 expr: Box::new(left),
8296 pattern,
8297 flags,
8298 scalar_g: false,
8299 delim,
8300 },
8301 line,
8302 }),
8303 },
8304 line,
8305 })
8306 }
8307 Token::Ident(ref s) if s.starts_with('\x00') => {
8308 let (Token::Ident(encoded), _) = self.advance() else {
8309 unreachable!()
8310 };
8311 let parts: Vec<&str> = encoded.split('\x00').collect();
8312 if parts.len() >= 4 && parts[1] == "s" {
8313 let delim = parts.get(5).and_then(|s| s.chars().next()).unwrap_or('/');
8314 Ok(Expr {
8315 kind: ExprKind::UnaryOp {
8316 op: UnaryOp::LogNot,
8317 expr: Box::new(Expr {
8318 kind: ExprKind::Substitution {
8319 expr: Box::new(left),
8320 pattern: parts[2].to_string(),
8321 replacement: parts[3].to_string(),
8322 flags: parts.get(4).unwrap_or(&"").to_string(),
8323 delim,
8324 },
8325 line,
8326 }),
8327 },
8328 line,
8329 })
8330 } else if parts.len() >= 4 && parts[1] == "tr" {
8331 let delim = parts.get(5).and_then(|s| s.chars().next()).unwrap_or('/');
8332 Ok(Expr {
8333 kind: ExprKind::UnaryOp {
8334 op: UnaryOp::LogNot,
8335 expr: Box::new(Expr {
8336 kind: ExprKind::Transliterate {
8337 expr: Box::new(left),
8338 from: parts[2].to_string(),
8339 to: parts[3].to_string(),
8340 flags: parts.get(4).unwrap_or(&"").to_string(),
8341 delim,
8342 },
8343 line,
8344 }),
8345 },
8346 line,
8347 })
8348 } else {
8349 Err(self.syntax_err("Invalid regex binding after !~", line))
8350 }
8351 }
8352 _ => {
8353 let rhs = self.parse_unary()?;
8354 Ok(Expr {
8355 kind: ExprKind::BinOp {
8356 left: Box::new(left),
8357 op: BinOp::BindNotMatch,
8358 right: Box::new(rhs),
8359 },
8360 line,
8361 })
8362 }
8363 }
8364 }
8365 _ => Ok(left),
8366 }
8367 }
8368
8369 fn parse_thread_input(&mut self) -> StrykeResult<Expr> {
8372 self.suppress_slash_as_div = self.suppress_slash_as_div.saturating_add(1);
8373 let result = self.parse_range();
8374 self.suppress_slash_as_div = self.suppress_slash_as_div.saturating_sub(1);
8375 result
8376 }
8377
8378 fn parse_thread_macro_chunk_par(
8383 &mut self,
8384 line: usize,
8385 thread_last: bool,
8386 ) -> StrykeResult<Expr> {
8387 self.suppress_parenless_call = self.suppress_parenless_call.saturating_add(1);
8389 let source_expr = self.parse_thread_input();
8390 self.suppress_parenless_call = self.suppress_parenless_call.saturating_sub(1);
8391 let source_expr = source_expr?;
8392
8393 self.pending_thread_input = Some(Expr {
8397 kind: ExprKind::ArrayVar("_".into()),
8398 line,
8399 });
8400 let chunk_chain = self.parse_thread_macro_inner(line, thread_last, None);
8401 self.pending_thread_input = None;
8402 let chunk_chain = chunk_chain?;
8403
8404 let extract_block: Block = match chunk_chain.kind {
8409 ExprKind::CodeRef { params: _, body } => body,
8410 _ => vec![Statement {
8411 label: None,
8412 kind: StmtKind::Expression(chunk_chain),
8413 line,
8414 }],
8415 };
8416
8417 let par_reduce = Expr {
8418 kind: ExprKind::ParReduceExpr {
8419 extract_block,
8420 reduce_block: None,
8421 list: Box::new(source_expr),
8422 },
8423 line,
8424 };
8425
8426 if self.eat_chunk_par_split_boundary() {
8430 return self.parse_thread_macro_continuation(par_reduce, line, thread_last);
8431 }
8432 Ok(par_reduce)
8433 }
8434
8435 fn parse_thread_macro_dist(&mut self, line: usize, thread_last: bool) -> StrykeResult<Expr> {
8442 let on_ok = matches!(self.peek(), Token::Ident(ref s) if s == "on");
8444 if !on_ok {
8445 return Err(
8446 self.syntax_err("~d>: expected `on <cluster-expr>` after the operator", line)
8447 );
8448 }
8449 self.advance(); self.suppress_parenless_call = self.suppress_parenless_call.saturating_add(1);
8453 self.suppress_indirect_paren_call = self.suppress_indirect_paren_call.saturating_add(1);
8457 let cluster_expr = self.parse_thread_input();
8458 self.suppress_indirect_paren_call = self.suppress_indirect_paren_call.saturating_sub(1);
8459 self.suppress_parenless_call = self.suppress_parenless_call.saturating_sub(1);
8460 let cluster_expr = cluster_expr?;
8461
8462 self.suppress_parenless_call = self.suppress_parenless_call.saturating_add(1);
8464 let source_expr = self.parse_thread_input();
8465 self.suppress_parenless_call = self.suppress_parenless_call.saturating_sub(1);
8466 let source_expr = source_expr?;
8467
8468 self.pending_thread_input = Some(Expr {
8473 kind: ExprKind::ArrayVar("_".into()),
8474 line,
8475 });
8476 let chunk_chain = self.parse_thread_macro_inner(line, thread_last, None);
8477 self.pending_thread_input = None;
8478 let chunk_chain = chunk_chain?;
8479
8480 let extract_block: Block = match chunk_chain.kind {
8481 ExprKind::CodeRef { params: _, body } => body,
8482 _ => vec![Statement {
8483 label: None,
8484 kind: StmtKind::Expression(chunk_chain),
8485 line,
8486 }],
8487 };
8488
8489 let dist_reduce = Expr {
8490 kind: ExprKind::DistReduceExpr {
8491 cluster: Box::new(cluster_expr),
8492 extract_block,
8493 list: Box::new(source_expr),
8494 },
8495 line,
8496 };
8497
8498 if self.eat_chunk_par_split_boundary() {
8500 return self.parse_thread_macro_continuation(dist_reduce, line, thread_last);
8501 }
8502 Ok(dist_reduce)
8503 }
8504
8505 fn parse_thread_macro_continuation(
8511 &mut self,
8512 prior: Expr,
8513 line: usize,
8514 thread_last: bool,
8515 ) -> StrykeResult<Expr> {
8516 self.pending_thread_input = Some(prior);
8517 let res = self.parse_thread_macro_inner(line, thread_last, None);
8518 self.pending_thread_input = None;
8519 res
8520 }
8521
8522 fn eat_chunk_par_split_boundary(&mut self) -> bool {
8526 if matches!(self.peek(), Token::LogOr) && matches!(self.peek_at(1), Token::NumGt) {
8528 self.advance(); self.advance(); return true;
8531 }
8532 if matches!(self.peek(), Token::BitOr) {
8534 if let Token::Ident(name) = self.peek_at(1).clone() {
8535 if name == "then" && matches!(self.peek_at(2), Token::BitOr) {
8536 self.advance(); self.advance(); self.advance(); return true;
8540 }
8541 }
8542 }
8543 false
8544 }
8545
8546 fn parse_range(&mut self) -> StrykeResult<Expr> {
8553 let left = self.parse_log_or()?;
8554 let line = left.line;
8555 let (exclusive, _colon_style) = if self.eat(&Token::RangeExclusive) {
8562 (true, false)
8563 } else if self.eat(&Token::Range) {
8564 (false, false)
8565 } else if self.suppress_colon_range == 0 && self.eat(&Token::Colon) {
8566 (false, true)
8569 } else if self.suppress_tilde_range == 0 && self.eat(&Token::BitNot) {
8570 (false, true)
8571 } else {
8572 return Ok(left);
8573 };
8574 let right = self.parse_log_or()?;
8575 let step = if self.eat(&Token::Colon)
8579 || (self.suppress_tilde_range == 0 && self.eat(&Token::BitNot))
8580 {
8581 Some(Box::new(self.parse_unary()?))
8582 } else {
8583 None
8584 };
8585 Ok(Expr {
8586 kind: ExprKind::Range {
8587 from: Box::new(left),
8588 to: Box::new(right),
8589 exclusive,
8590 step,
8591 },
8592 line,
8593 })
8594 }
8595
8596 fn parse_package_qualified_identifier(&mut self) -> StrykeResult<String> {
8598 let mut name = match self.advance() {
8599 (Token::Ident(n), _) => n,
8600 (tok, l) => {
8601 return Err(self.syntax_err(format!("Expected identifier, got {:?}", tok), l));
8602 }
8603 };
8604 while self.eat(&Token::PackageSep) {
8605 match self.advance() {
8606 (Token::Ident(part), _) => {
8607 name.push_str("::");
8608 name.push_str(&part);
8609 }
8610 (Token::ScalarVar(part), _) if Self::is_underscore_topic_slot(&part) => {
8618 name.push_str("::");
8619 name.push_str(&part);
8620 }
8621 (tok, l) => {
8622 return Err(self
8623 .syntax_err(format!("Expected identifier after `::`, got {:?}", tok), l));
8624 }
8625 }
8626 }
8627 Ok(name)
8628 }
8629
8630 fn parse_qualified_subroutine_name(&mut self) -> StrykeResult<String> {
8632 self.parse_package_qualified_identifier()
8633 }
8634
8635 fn parse_unary(&mut self) -> StrykeResult<Expr> {
8636 let line = self.peek_line();
8637 match self.peek().clone() {
8638 Token::Minus => {
8639 self.advance();
8640 let expr = self.parse_power()?;
8641 Ok(Expr {
8642 kind: ExprKind::UnaryOp {
8643 op: UnaryOp::Negate,
8644 expr: Box::new(expr),
8645 },
8646 line,
8647 })
8648 }
8649 Token::Plus => {
8656 self.advance();
8657 if matches!(self.peek(), Token::LBrace) {
8658 let line = self.peek_line();
8659 self.advance(); return self.parse_forced_hashref_body(line);
8661 }
8662 self.parse_unary()
8663 }
8664 Token::LogNot => {
8665 self.advance();
8666 let expr = self.parse_unary()?;
8667 Ok(Expr {
8668 kind: ExprKind::UnaryOp {
8669 op: UnaryOp::LogNot,
8670 expr: Box::new(expr),
8671 },
8672 line,
8673 })
8674 }
8675 Token::BitNot => {
8676 self.advance();
8677 let expr = self.parse_unary()?;
8678 Ok(Expr {
8679 kind: ExprKind::UnaryOp {
8680 op: UnaryOp::BitNot,
8681 expr: Box::new(expr),
8682 },
8683 line,
8684 })
8685 }
8686 Token::Increment => {
8687 self.advance();
8688 let expr = self.parse_postfix()?;
8689 Ok(Expr {
8690 kind: ExprKind::UnaryOp {
8691 op: UnaryOp::PreIncrement,
8692 expr: Box::new(expr),
8693 },
8694 line,
8695 })
8696 }
8697 Token::Decrement => {
8698 self.advance();
8699 let expr = self.parse_postfix()?;
8700 Ok(Expr {
8701 kind: ExprKind::UnaryOp {
8702 op: UnaryOp::PreDecrement,
8703 expr: Box::new(expr),
8704 },
8705 line,
8706 })
8707 }
8708 Token::BitAnd => {
8709 self.advance();
8712 if matches!(self.peek(), Token::LBrace) {
8713 self.advance();
8714 let inner = self.parse_expression()?;
8715 self.expect(&Token::RBrace)?;
8716 return Ok(Expr {
8717 kind: ExprKind::DynamicSubCodeRef(Box::new(inner)),
8718 line,
8719 });
8720 }
8721 if matches!(self.peek(), Token::Ident(_)) {
8722 let name = self.parse_qualified_subroutine_name()?;
8723 return Ok(Expr {
8724 kind: ExprKind::SubroutineRef(name),
8725 line,
8726 });
8727 }
8728 let target = self.parse_primary()?;
8729 if matches!(self.peek(), Token::LParen) {
8730 self.advance();
8731 let args = self.parse_arg_list()?;
8732 self.expect(&Token::RParen)?;
8733 return Ok(Expr {
8734 kind: ExprKind::IndirectCall {
8735 target: Box::new(target),
8736 args,
8737 ampersand: true,
8738 pass_caller_arglist: false,
8739 },
8740 line,
8741 });
8742 }
8743 Ok(Expr {
8745 kind: ExprKind::IndirectCall {
8746 target: Box::new(target),
8747 args: vec![],
8748 ampersand: true,
8749 pass_caller_arglist: true,
8750 },
8751 line,
8752 })
8753 }
8754 Token::Backslash => {
8755 self.advance();
8756 let expr = self.parse_unary()?;
8757 if let ExprKind::SubroutineRef(name) = expr.kind {
8758 return Ok(Expr {
8759 kind: ExprKind::SubroutineCodeRef(name),
8760 line,
8761 });
8762 }
8763 if matches!(expr.kind, ExprKind::DynamicSubCodeRef(_)) {
8764 return Ok(expr);
8765 }
8766 Ok(Expr {
8768 kind: ExprKind::ScalarRef(Box::new(expr)),
8769 line,
8770 })
8771 }
8772 Token::FileTest(op) => {
8773 self.advance();
8774 let expr = if Self::filetest_allows_implicit_topic(self.peek()) {
8776 Expr {
8777 kind: ExprKind::ScalarVar("_".into()),
8778 line: self.peek_line(),
8779 }
8780 } else {
8781 self.parse_unary()?
8782 };
8783 Ok(Expr {
8784 kind: ExprKind::FileTest {
8785 op,
8786 expr: Box::new(expr),
8787 },
8788 line,
8789 })
8790 }
8791 _ => self.parse_power(),
8792 }
8793 }
8794
8795 fn parse_power(&mut self) -> StrykeResult<Expr> {
8796 let left = self.parse_postfix()?;
8797 if matches!(self.peek(), Token::Power) {
8798 let line = left.line;
8799 self.advance();
8800 let right = self.parse_unary()?; return Ok(Expr {
8802 kind: ExprKind::BinOp {
8803 left: Box::new(left),
8804 op: BinOp::Pow,
8805 right: Box::new(right),
8806 },
8807 line,
8808 });
8809 }
8810 Ok(left)
8811 }
8812
8813 fn parse_postfix(&mut self) -> StrykeResult<Expr> {
8814 let mut expr = self.parse_primary()?;
8815 loop {
8816 match self.peek().clone() {
8817 Token::Increment => {
8818 if self.peek_line() > self.prev_line() {
8821 break;
8822 }
8823 let line = expr.line;
8824 self.advance();
8825 expr = Expr {
8826 kind: ExprKind::PostfixOp {
8827 expr: Box::new(expr),
8828 op: PostfixOp::Increment,
8829 },
8830 line,
8831 };
8832 }
8833 Token::Decrement => {
8834 if self.peek_line() > self.prev_line() {
8837 break;
8838 }
8839 let line = expr.line;
8840 self.advance();
8841 expr = Expr {
8842 kind: ExprKind::PostfixOp {
8843 expr: Box::new(expr),
8844 op: PostfixOp::Decrement,
8845 },
8846 line,
8847 };
8848 }
8849 Token::LParen => {
8850 if self.suppress_indirect_paren_call > 0 {
8851 break;
8852 }
8853 if self.peek_line() > self.prev_line() {
8857 break;
8858 }
8859 let line = expr.line;
8860 self.advance();
8861 let args = self.parse_arg_list()?;
8862 self.expect(&Token::RParen)?;
8863 expr = Expr {
8864 kind: ExprKind::IndirectCall {
8865 target: Box::new(expr),
8866 args,
8867 ampersand: false,
8868 pass_caller_arglist: false,
8869 },
8870 line,
8871 };
8872 }
8873 Token::Arrow => {
8874 let line = expr.line;
8875 self.advance();
8876 match self.peek().clone() {
8877 Token::LBracket => {
8878 self.advance();
8879 let index = self.parse_expression()?;
8880 self.expect(&Token::RBracket)?;
8881 expr = Expr {
8882 kind: ExprKind::ArrowDeref {
8883 expr: Box::new(expr),
8884 index: Box::new(index),
8885 kind: DerefKind::Array,
8886 },
8887 line,
8888 };
8889 }
8890 Token::LBrace => {
8891 self.advance();
8892 let key = self.parse_hash_subscript_key()?;
8893 self.expect(&Token::RBrace)?;
8894 expr = Expr {
8895 kind: ExprKind::ArrowDeref {
8896 expr: Box::new(expr),
8897 index: Box::new(key),
8898 kind: DerefKind::Hash,
8899 },
8900 line,
8901 };
8902 }
8903 Token::LParen => {
8904 self.advance();
8905 let args = self.parse_arg_list()?;
8906 self.expect(&Token::RParen)?;
8907 expr = Expr {
8908 kind: ExprKind::ArrowDeref {
8909 expr: Box::new(expr),
8910 index: Box::new(Expr {
8911 kind: ExprKind::List(args),
8912 line,
8913 }),
8914 kind: DerefKind::Call,
8915 },
8916 line,
8917 };
8918 }
8919 Token::Ident(method) => {
8920 self.advance();
8921 if method == "SUPER" {
8922 self.expect(&Token::PackageSep)?;
8923 let real_method = match self.advance() {
8924 (Token::Ident(n), _) => n,
8925 (tok, l) => {
8926 return Err(self.syntax_err(
8927 format!(
8928 "Expected method name after SUPER::, got {:?}",
8929 tok
8930 ),
8931 l,
8932 ));
8933 }
8934 };
8935 let args = if self.eat(&Token::LParen) {
8936 let a = self.parse_arg_list()?;
8937 self.expect(&Token::RParen)?;
8938 a
8939 } else {
8940 self.parse_method_arg_list_no_paren()?
8941 };
8942 expr = Expr {
8943 kind: ExprKind::MethodCall {
8944 object: Box::new(expr),
8945 method: real_method,
8946 args,
8947 super_call: true,
8948 },
8949 line,
8950 };
8951 } else {
8952 let mut method_name = method;
8953 while self.eat(&Token::PackageSep) {
8954 match self.advance() {
8955 (Token::Ident(part), _) => {
8956 method_name.push_str("::");
8957 method_name.push_str(&part);
8958 }
8959 (tok, l) => {
8960 return Err(self.syntax_err(
8961 format!(
8962 "Expected identifier after :: in method name, got {:?}",
8963 tok
8964 ),
8965 l,
8966 ));
8967 }
8968 }
8969 }
8970 let args = if self.eat(&Token::LParen) {
8971 let a = self.parse_arg_list()?;
8972 self.expect(&Token::RParen)?;
8973 a
8974 } else {
8975 self.parse_method_arg_list_no_paren()?
8976 };
8977 expr = Expr {
8978 kind: ExprKind::MethodCall {
8979 object: Box::new(expr),
8980 method: method_name,
8981 args,
8982 super_call: false,
8983 },
8984 line,
8985 };
8986 }
8987 }
8988 Token::ArrayAt => {
8994 self.advance(); match self.peek().clone() {
8996 Token::Star => {
8997 self.advance();
8998 expr = Expr {
8999 kind: ExprKind::Deref {
9000 expr: Box::new(expr),
9001 kind: Sigil::Array,
9002 },
9003 line,
9004 };
9005 }
9006 Token::LBracket => {
9007 self.advance();
9008 let indices = self.parse_slice_arg_list(false)?;
9009 self.expect(&Token::RBracket)?;
9010 let source = Expr {
9011 kind: ExprKind::Deref {
9012 expr: Box::new(expr),
9013 kind: Sigil::Array,
9014 },
9015 line,
9016 };
9017 expr = Expr {
9018 kind: ExprKind::AnonymousListSlice {
9019 source: Box::new(source),
9020 indices,
9021 },
9022 line,
9023 };
9024 }
9025 Token::LBrace => {
9026 self.advance();
9027 let keys = self.parse_slice_arg_list(true)?;
9028 self.expect(&Token::RBrace)?;
9029 expr = Expr {
9030 kind: ExprKind::HashSliceDeref {
9031 container: Box::new(expr),
9032 keys,
9033 },
9034 line,
9035 };
9036 }
9037 tok => {
9038 return Err(self.syntax_err(
9039 format!(
9040 "Expected `*`, `[…]`, or `{{…}}` after `->@`, got {:?}",
9041 tok
9042 ),
9043 line,
9044 ));
9045 }
9046 }
9047 }
9048 Token::HashPercent => {
9049 self.advance(); match self.peek().clone() {
9051 Token::Star => {
9052 self.advance();
9053 expr = Expr {
9054 kind: ExprKind::Deref {
9055 expr: Box::new(expr),
9056 kind: Sigil::Hash,
9057 },
9058 line,
9059 };
9060 }
9061 tok => {
9062 return Err(self.syntax_err(
9063 format!("Expected `*` after `->%`, got {:?}", tok),
9064 line,
9065 ));
9066 }
9067 }
9068 }
9069 Token::X => {
9071 self.advance();
9072 let args = if self.eat(&Token::LParen) {
9073 let a = self.parse_arg_list()?;
9074 self.expect(&Token::RParen)?;
9075 a
9076 } else {
9077 self.parse_method_arg_list_no_paren()?
9078 };
9079 expr = Expr {
9080 kind: ExprKind::MethodCall {
9081 object: Box::new(expr),
9082 method: "x".to_string(),
9083 args,
9084 super_call: false,
9085 },
9086 line,
9087 };
9088 }
9089 _ => break,
9090 }
9091 }
9092 Token::LBracket => {
9093 if self.peek_line() > self.prev_line() {
9096 break;
9097 }
9098 let line = expr.line;
9100 if matches!(expr.kind, ExprKind::ScalarVar(_)) {
9101 if let ExprKind::ScalarVar(ref name) = expr.kind {
9102 let name = name.clone();
9103 self.advance();
9104 let index = self.parse_expression()?;
9107 self.expect(&Token::RBracket)?;
9108 expr = Expr {
9109 kind: ExprKind::ArrayElement {
9110 array: name,
9111 index: Box::new(index),
9112 },
9113 line,
9114 };
9115 }
9116 } else if postfix_lbracket_is_arrow_container(&expr) {
9117 self.advance();
9118 let indices = self.parse_arg_list()?;
9119 self.expect(&Token::RBracket)?;
9120 expr = Expr {
9121 kind: ExprKind::ArrowDeref {
9122 expr: Box::new(expr),
9123 index: Box::new(Expr {
9124 kind: ExprKind::List(indices),
9125 line,
9126 }),
9127 kind: DerefKind::Array,
9128 },
9129 line,
9130 };
9131 } else {
9132 self.advance();
9133 let indices = self.parse_arg_list()?;
9134 self.expect(&Token::RBracket)?;
9135 expr = Expr {
9136 kind: ExprKind::AnonymousListSlice {
9137 source: Box::new(expr),
9138 indices,
9139 },
9140 line,
9141 };
9142 }
9143 }
9144 Token::LBrace => {
9145 if self.suppress_scalar_hash_brace > 0 {
9146 break;
9147 }
9148 if self.peek_line() > self.prev_line() {
9151 break;
9152 }
9153 let line = expr.line;
9156 let is_scalar_named_hash = matches!(expr.kind, ExprKind::ScalarVar(_));
9157 let is_chainable_hash_subscript = is_scalar_named_hash
9158 || matches!(
9159 expr.kind,
9160 ExprKind::HashElement { .. }
9161 | ExprKind::ArrayElement { .. }
9162 | ExprKind::ArrowDeref { .. }
9163 | ExprKind::Deref {
9164 kind: Sigil::Scalar,
9165 ..
9166 }
9167 );
9168 if !is_chainable_hash_subscript {
9169 break;
9170 }
9171 self.advance();
9172 let key = self.parse_hash_subscript_key()?;
9173 self.expect(&Token::RBrace)?;
9174 expr = if is_scalar_named_hash {
9175 if let ExprKind::ScalarVar(ref name) = expr.kind {
9176 let name = name.clone();
9177 if name == "_" {
9179 Expr {
9180 kind: ExprKind::ArrowDeref {
9181 expr: Box::new(Expr {
9182 kind: ExprKind::ScalarVar("_".into()),
9183 line,
9184 }),
9185 index: Box::new(key),
9186 kind: DerefKind::Hash,
9187 },
9188 line,
9189 }
9190 } else {
9191 Expr {
9192 kind: ExprKind::HashElement {
9193 hash: name,
9194 key: Box::new(key),
9195 },
9196 line,
9197 }
9198 }
9199 } else {
9200 unreachable!("is_scalar_named_hash implies ScalarVar");
9201 }
9202 } else {
9203 Expr {
9204 kind: ExprKind::ArrowDeref {
9205 expr: Box::new(expr),
9206 index: Box::new(key),
9207 kind: DerefKind::Hash,
9208 },
9209 line,
9210 }
9211 };
9212 }
9213 Token::LogNot | Token::BitNot => {
9214 if !matches!(expr.kind, ExprKind::ScalarVar(_)) {
9229 break;
9230 }
9231 if self.peek_line() > self.prev_line() {
9232 break;
9233 }
9234 let opener = self.peek().clone();
9235 let line = expr.line;
9236 let name = if let ExprKind::ScalarVar(ref n) = expr.kind {
9237 n.clone()
9238 } else {
9239 unreachable!()
9240 };
9241 self.advance(); self.suppress_tilde_range = self.suppress_tilde_range.saturating_add(1);
9247 let index_result = self.parse_expression();
9248 self.suppress_tilde_range = self.suppress_tilde_range.saturating_sub(1);
9249 let index = index_result?;
9250 let close_match = matches!(
9251 (&opener, self.peek()),
9252 (Token::LogNot, Token::LogNot) | (Token::BitNot, Token::BitNot)
9253 );
9254 if !close_match {
9255 let want = if matches!(opener, Token::LogNot) {
9256 "!"
9257 } else {
9258 "~"
9259 };
9260 return Err(self.syntax_err(
9261 format!("expected closing `{}` for string subscript", want),
9262 self.peek_line(),
9263 ));
9264 }
9265 self.advance(); expr = Expr {
9267 kind: ExprKind::ArrayElement {
9268 array: format!("__topicstr__{}", name),
9269 index: Box::new(index),
9270 },
9271 line,
9272 };
9273 }
9274 _ => break,
9275 }
9276 }
9277 Ok(expr)
9278 }
9279
9280 fn parse_primary(&mut self) -> StrykeResult<Expr> {
9281 let line = self.peek_line();
9282 if let Token::Ident(ref kw) = self.peek().clone() {
9287 let is_var_val_decl_start = is_post_style_decl(kw)
9296 && matches!(
9297 self.peek_at(1),
9298 Token::ScalarVar(_)
9299 | Token::ArrayVar(_)
9300 | Token::HashVar(_)
9301 | Token::Star
9302 | Token::LParen
9303 ) || is_post_style_decl(kw)
9304 && matches!(
9305 self.peek_at(1),
9306 Token::Ident(ref kw) if kw == "typed"
9307 );
9308 let eager_kw = matches!(kw.as_str(), "my" | "our" | "state" | "local");
9309 if eager_kw || is_var_val_decl_start {
9310 let raw_kw = kw.clone();
9311 let kw_owned: String = if is_post_style_decl(&raw_kw) {
9317 "my".to_string()
9318 } else {
9319 raw_kw.clone()
9320 };
9321 let allow_type = is_frozen_decl(&raw_kw);
9322 let mark_frozen = is_frozen_decl(&raw_kw);
9323 let saved_pos = self.pos;
9328 let stmt = self.parse_my_our_local(&kw_owned, allow_type)?;
9329 let mut decls = match stmt.kind {
9330 StmtKind::My(d)
9331 | StmtKind::Our(d)
9332 | StmtKind::State(d)
9333 | StmtKind::Local(d) => d,
9334 _ => {
9335 self.pos = saved_pos;
9340 return Err(self.syntax_err(
9341 "`my`/`our`/`local` in expression must declare variables",
9342 line,
9343 ));
9344 }
9345 };
9346 if mark_frozen {
9347 for d in decls.iter_mut() {
9348 d.frozen = true;
9349 }
9350 }
9351 return Ok(Expr {
9352 kind: ExprKind::MyExpr {
9353 keyword: kw_owned,
9354 decls,
9355 },
9356 line,
9357 });
9358 }
9359 }
9360 match self.peek().clone() {
9361 Token::Integer(n) => {
9362 self.advance();
9363 Ok(Expr {
9364 kind: ExprKind::Integer(n),
9365 line,
9366 })
9367 }
9368 Token::Float(f) => {
9369 self.advance();
9370 Ok(Expr {
9371 kind: ExprKind::Float(f),
9372 line,
9373 })
9374 }
9375 Token::ArrowBrace => {
9381 self.advance();
9382 let mut stmts = Vec::new();
9383 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
9384 if self.eat(&Token::Semicolon) {
9385 continue;
9386 }
9387 stmts.push(self.parse_statement()?);
9388 }
9389 self.expect(&Token::RBrace)?;
9390 let inner_line = stmts.first().map(|s| s.line).unwrap_or(line);
9391 let inner = Expr {
9392 kind: ExprKind::CodeRef {
9393 params: vec![],
9394 body: stmts,
9395 },
9396 line: inner_line,
9397 };
9398 Ok(Expr {
9399 kind: ExprKind::Do(Box::new(inner)),
9400 line,
9401 })
9402 }
9403 Token::Star => {
9404 self.advance();
9405 if matches!(self.peek(), Token::LBrace) {
9406 self.advance();
9407 let inner = self.parse_expression()?;
9408 self.expect(&Token::RBrace)?;
9409 return Ok(Expr {
9410 kind: ExprKind::Deref {
9411 expr: Box::new(inner),
9412 kind: Sigil::Typeglob,
9413 },
9414 line,
9415 });
9416 }
9417 if matches!(
9419 self.peek(),
9420 Token::ScalarVar(_)
9421 | Token::ArrayVar(_)
9422 | Token::HashVar(_)
9423 | Token::DerefScalarVar(_)
9424 | Token::HashPercent
9425 ) {
9426 let inner = self.parse_postfix()?;
9427 return Ok(Expr {
9428 kind: ExprKind::TypeglobExpr(Box::new(inner)),
9429 line,
9430 });
9431 }
9432 let mut full_name = match self.advance() {
9434 (Token::Ident(n), _) => n,
9435 (Token::X, _) => "x".to_string(),
9436 (tok, l) => {
9437 return Err(self
9438 .syntax_err(format!("Expected identifier after *, got {:?}", tok), l));
9439 }
9440 };
9441 while self.eat(&Token::PackageSep) {
9442 match self.advance() {
9443 (Token::Ident(part), _) => {
9444 full_name = format!("{}::{}", full_name, part);
9445 }
9446 (Token::X, _) => {
9447 full_name = format!("{}::x", full_name);
9448 }
9449 (tok, l) => {
9450 return Err(self.syntax_err(
9451 format!("Expected identifier after :: in typeglob, got {:?}", tok),
9452 l,
9453 ));
9454 }
9455 }
9456 }
9457 Ok(Expr {
9458 kind: ExprKind::Typeglob(full_name),
9459 line,
9460 })
9461 }
9462 Token::SingleString(s) => {
9463 self.advance();
9464 Ok(Expr {
9465 kind: ExprKind::String(s),
9466 line,
9467 })
9468 }
9469 Token::DoubleString(s) => {
9470 self.advance();
9471 self.parse_interpolated_string(&s, line)
9472 }
9473 Token::BacktickString(s) => {
9474 self.advance();
9475 let inner = self.parse_interpolated_string(&s, line)?;
9476 Ok(Expr {
9477 kind: ExprKind::Qx(Box::new(inner)),
9478 line,
9479 })
9480 }
9481 Token::HereDoc(_, body, interpolate) => {
9482 self.advance();
9483 if interpolate {
9484 self.parse_interpolated_string(&body, line)
9485 } else {
9486 Ok(Expr {
9487 kind: ExprKind::String(body),
9488 line,
9489 })
9490 }
9491 }
9492 Token::Regex(pattern, flags, _delim) => {
9493 self.advance();
9494 Ok(Expr {
9495 kind: ExprKind::Regex(pattern, flags),
9496 line,
9497 })
9498 }
9499 Token::QW(words) => {
9500 self.advance();
9501 self.list_construct_close_pos = Some(self.pos);
9504 Ok(Expr {
9505 kind: ExprKind::QW(words),
9506 line,
9507 })
9508 }
9509 Token::DerefScalarVar(name) => {
9510 self.advance();
9511 Ok(Expr {
9512 kind: ExprKind::Deref {
9513 expr: Box::new(Expr {
9514 kind: ExprKind::ScalarVar(name),
9515 line,
9516 }),
9517 kind: Sigil::Scalar,
9518 },
9519 line,
9520 })
9521 }
9522 Token::ScalarVar(name) => {
9523 self.advance();
9524 Ok(Expr {
9525 kind: ExprKind::ScalarVar(name),
9526 line,
9527 })
9528 }
9529 Token::ArrayVar(name) => {
9530 self.advance();
9531 match self.peek() {
9533 Token::LBracket => {
9534 self.advance();
9535 let indices = self.parse_slice_arg_list(false)?;
9536 self.expect(&Token::RBracket)?;
9537 Ok(Expr {
9538 kind: ExprKind::ArraySlice {
9539 array: name,
9540 indices,
9541 },
9542 line,
9543 })
9544 }
9545 Token::LBrace if self.suppress_scalar_hash_brace == 0 => {
9546 self.advance();
9547 let keys = self.parse_slice_arg_list(true)?;
9548 self.expect(&Token::RBrace)?;
9549 Ok(Expr {
9550 kind: ExprKind::HashSlice { hash: name, keys },
9551 line,
9552 })
9553 }
9554 _ => Ok(Expr {
9555 kind: ExprKind::ArrayVar(name),
9556 line,
9557 }),
9558 }
9559 }
9560 Token::HashVar(name) => {
9561 self.advance();
9562 if matches!(self.peek(), Token::LBrace) && self.suppress_scalar_hash_brace == 0 {
9567 self.advance(); let keys = self.parse_slice_arg_list(true)?;
9569 self.expect(&Token::RBrace)?;
9570 return Ok(Expr {
9571 kind: ExprKind::HashKvSlice { hash: name, keys },
9572 line,
9573 });
9574 }
9575 Ok(Expr {
9576 kind: ExprKind::HashVar(name),
9577 line,
9578 })
9579 }
9580 Token::HashPercent => {
9581 self.advance();
9583 if matches!(self.peek(), Token::ScalarVar(_)) {
9584 let n = match self.advance() {
9585 (Token::ScalarVar(n), _) => n,
9586 (tok, l) => {
9587 return Err(self.syntax_err(
9588 format!("Expected scalar variable after %%, got {:?}", tok),
9589 l,
9590 ));
9591 }
9592 };
9593 return Ok(Expr {
9594 kind: ExprKind::Deref {
9595 expr: Box::new(Expr {
9596 kind: ExprKind::ScalarVar(n),
9597 line,
9598 }),
9599 kind: Sigil::Hash,
9600 },
9601 line,
9602 });
9603 }
9604 if matches!(self.peek(), Token::LBracket) {
9609 self.advance();
9610 let pairs = self.parse_hashref_pairs_until(&Token::RBracket)?;
9611 self.expect(&Token::RBracket)?;
9612 let href = Expr {
9613 kind: ExprKind::HashRef(pairs),
9614 line,
9615 };
9616 return Ok(Expr {
9617 kind: ExprKind::Deref {
9618 expr: Box::new(href),
9619 kind: Sigil::Hash,
9620 },
9621 line,
9622 });
9623 }
9624 self.expect(&Token::LBrace)?;
9625 let looks_like_pair = matches!(
9631 self.peek(),
9632 Token::Ident(_) | Token::SingleString(_) | Token::DoubleString(_)
9633 ) && matches!(self.peek_at(1), Token::FatArrow);
9634 let inner = if looks_like_pair {
9635 let pairs = self.parse_hashref_pairs_until(&Token::RBrace)?;
9636 Expr {
9637 kind: ExprKind::HashRef(pairs),
9638 line,
9639 }
9640 } else {
9641 self.parse_expression()?
9642 };
9643 self.expect(&Token::RBrace)?;
9644 Ok(Expr {
9645 kind: ExprKind::Deref {
9646 expr: Box::new(inner),
9647 kind: Sigil::Hash,
9648 },
9649 line,
9650 })
9651 }
9652 Token::ArrayAt => {
9653 self.advance();
9654 if matches!(self.peek(), Token::LBrace) {
9656 self.advance();
9657 let inner = self.parse_expression()?;
9658 self.expect(&Token::RBrace)?;
9659 if matches!(self.peek(), Token::LBrace) {
9664 self.advance();
9665 let keys = self.parse_slice_arg_list(true)?;
9666 self.expect(&Token::RBrace)?;
9667 return Ok(Expr {
9668 kind: ExprKind::HashSliceDeref {
9669 container: Box::new(inner),
9670 keys,
9671 },
9672 line,
9673 });
9674 }
9675 if matches!(self.peek(), Token::LBracket) {
9676 self.advance();
9677 let indices = self.parse_slice_arg_list(false)?;
9678 self.expect(&Token::RBracket)?;
9679 let source = Expr {
9680 kind: ExprKind::Deref {
9681 expr: Box::new(inner),
9682 kind: Sigil::Array,
9683 },
9684 line,
9685 };
9686 return Ok(Expr {
9687 kind: ExprKind::AnonymousListSlice {
9688 source: Box::new(source),
9689 indices,
9690 },
9691 line,
9692 });
9693 }
9694 return Ok(Expr {
9695 kind: ExprKind::Deref {
9696 expr: Box::new(inner),
9697 kind: Sigil::Array,
9698 },
9699 line,
9700 });
9701 }
9702 if matches!(self.peek(), Token::LBracket) {
9706 self.advance();
9707 let mut elems = Vec::new();
9708 if !matches!(self.peek(), Token::RBracket) {
9709 elems.push(self.parse_assign_expr()?);
9710 while self.eat(&Token::Comma) {
9711 if matches!(self.peek(), Token::RBracket) {
9712 break;
9713 }
9714 elems.push(self.parse_assign_expr()?);
9715 }
9716 }
9717 self.expect(&Token::RBracket)?;
9718 let aref = Expr {
9719 kind: ExprKind::ArrayRef(elems),
9720 line,
9721 };
9722 return Ok(Expr {
9723 kind: ExprKind::Deref {
9724 expr: Box::new(aref),
9725 kind: Sigil::Array,
9726 },
9727 line,
9728 });
9729 }
9730 let container = match self.peek().clone() {
9732 Token::ScalarVar(n) => {
9733 self.advance();
9734 Expr {
9735 kind: ExprKind::ScalarVar(n),
9736 line,
9737 }
9738 }
9739 _ => {
9740 return Err(self.syntax_err(
9741 "Expected `$name`, `{`, or `[` after `@` (e.g. `@$aref`, `@{expr}`, `@[1,2,3]`, or `@$href{keys}`)",
9742 line,
9743 ));
9744 }
9745 };
9746 if matches!(self.peek(), Token::LBrace) {
9747 self.advance();
9748 let keys = self.parse_slice_arg_list(true)?;
9749 self.expect(&Token::RBrace)?;
9750 return Ok(Expr {
9751 kind: ExprKind::HashSliceDeref {
9752 container: Box::new(container),
9753 keys,
9754 },
9755 line,
9756 });
9757 }
9758 Ok(Expr {
9759 kind: ExprKind::Deref {
9760 expr: Box::new(container),
9761 kind: Sigil::Array,
9762 },
9763 line,
9764 })
9765 }
9766 Token::LParen => {
9767 self.advance();
9768 if matches!(self.peek(), Token::RParen) {
9769 self.advance();
9770 self.list_construct_close_pos = Some(self.pos);
9773 return Ok(Expr {
9774 kind: ExprKind::List(vec![]),
9775 line,
9776 });
9777 }
9778 let saved_no_pipe = self.no_pipe_forward_depth;
9781 self.no_pipe_forward_depth = 0;
9782 let saved_indirect = self.suppress_indirect_paren_call;
9786 self.suppress_indirect_paren_call = 0;
9787 let expr = self.parse_expression();
9788 self.no_pipe_forward_depth = saved_no_pipe;
9789 self.suppress_indirect_paren_call = saved_indirect;
9790 let expr = expr?;
9791 self.expect(&Token::RParen)?;
9792 self.list_construct_close_pos = Some(self.pos);
9797 Ok(expr)
9798 }
9799 Token::LBracket => {
9800 self.advance();
9801 let elems = self.parse_arg_list()?;
9802 self.expect(&Token::RBracket)?;
9803 Ok(Expr {
9804 kind: ExprKind::ArrayRef(elems),
9805 line,
9806 })
9807 }
9808 Token::LBrace => {
9809 self.advance();
9811 let saved = self.pos;
9813 match self.try_parse_hash_ref() {
9814 Ok(pairs) => Ok(Expr {
9815 kind: ExprKind::HashRef(pairs),
9816 line,
9817 }),
9818 Err(_) => {
9819 self.pos = saved;
9820 let mut stmts = Vec::new();
9822 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
9823 if self.eat(&Token::Semicolon) {
9824 continue;
9825 }
9826 stmts.push(self.parse_statement()?);
9827 }
9828 self.expect(&Token::RBrace)?;
9829 Ok(Expr {
9830 kind: ExprKind::CodeRef {
9831 params: vec![],
9832 body: stmts,
9833 },
9834 line,
9835 })
9836 }
9837 }
9838 }
9839 Token::Diamond => {
9840 self.advance();
9841 Ok(Expr {
9842 kind: ExprKind::ReadLine(None),
9843 line,
9844 })
9845 }
9846 Token::ReadLine(handle) => {
9847 self.advance();
9848 Ok(Expr {
9849 kind: ExprKind::ReadLine(Some(handle)),
9850 line,
9851 })
9852 }
9853
9854 Token::ThreadArrow => {
9856 self.advance();
9857 self.parse_thread_macro(line, false)
9858 }
9859 Token::ThreadArrowLast => {
9860 self.advance();
9861 self.parse_thread_macro(line, true)
9862 }
9863 Token::ThreadArrowStream => {
9864 self.advance();
9865 let mut stages = Vec::new();
9866 self.parse_thread_macro_inner(line, false, Some(&mut stages))
9867 }
9868 Token::ThreadArrowStreamLast => {
9869 self.advance();
9870 let mut stages = Vec::new();
9871 self.parse_thread_macro_inner(line, true, Some(&mut stages))
9872 }
9873 Token::ThreadArrowPar => {
9874 self.advance();
9875 self.parse_thread_macro_chunk_par(line, false)
9876 }
9877 Token::ThreadArrowParLast => {
9878 self.advance();
9879 self.parse_thread_macro_chunk_par(line, true)
9880 }
9881 Token::ThreadArrowDist => {
9882 self.advance();
9883 self.parse_thread_macro_dist(line, false)
9884 }
9885 Token::ThreadArrowDistLast => {
9886 self.advance();
9887 self.parse_thread_macro_dist(line, true)
9888 }
9889 Token::Ident(ref name) => {
9890 let name = name.clone();
9891 if name.starts_with('\x00') {
9893 self.advance();
9894 let parts: Vec<&str> = name.split('\x00').collect();
9895 if parts.len() >= 4 && parts[1] == "s" {
9896 let delim = parts.get(5).and_then(|s| s.chars().next()).unwrap_or('/');
9897 return Ok(Expr {
9898 kind: ExprKind::Substitution {
9899 expr: Box::new(Expr {
9900 kind: ExprKind::ScalarVar("_".into()),
9901 line,
9902 }),
9903 pattern: parts[2].to_string(),
9904 replacement: parts[3].to_string(),
9905 flags: parts.get(4).unwrap_or(&"").to_string(),
9906 delim,
9907 },
9908 line,
9909 });
9910 }
9911 if parts.len() >= 4 && parts[1] == "tr" {
9912 let delim = parts.get(5).and_then(|s| s.chars().next()).unwrap_or('/');
9913 return Ok(Expr {
9914 kind: ExprKind::Transliterate {
9915 expr: Box::new(Expr {
9916 kind: ExprKind::ScalarVar("_".into()),
9917 line,
9918 }),
9919 from: parts[2].to_string(),
9920 to: parts[3].to_string(),
9921 flags: parts.get(4).unwrap_or(&"").to_string(),
9922 delim,
9923 },
9924 line,
9925 });
9926 }
9927 return Err(self.syntax_err("Unexpected encoded token", line));
9928 }
9929 self.parse_named_expr(name)
9930 }
9931
9932 Token::Percent => {
9935 self.advance();
9936 match self.peek().clone() {
9937 Token::Ident(name) => {
9938 self.advance();
9939 Ok(Expr {
9940 kind: ExprKind::HashVar(name),
9941 line,
9942 })
9943 }
9944 Token::ScalarVar(n) => {
9945 self.advance();
9946 Ok(Expr {
9947 kind: ExprKind::Deref {
9948 expr: Box::new(Expr {
9949 kind: ExprKind::ScalarVar(n),
9950 line,
9951 }),
9952 kind: Sigil::Hash,
9953 },
9954 line,
9955 })
9956 }
9957 Token::LBrace => {
9958 self.advance();
9959 let looks_like_pair = matches!(
9960 self.peek(),
9961 Token::Ident(_) | Token::SingleString(_) | Token::DoubleString(_)
9962 ) && matches!(self.peek_at(1), Token::FatArrow);
9963 let inner = if looks_like_pair {
9964 let pairs = self.parse_hashref_pairs_until(&Token::RBrace)?;
9965 Expr {
9966 kind: ExprKind::HashRef(pairs),
9967 line,
9968 }
9969 } else {
9970 self.parse_expression()?
9971 };
9972 self.expect(&Token::RBrace)?;
9973 Ok(Expr {
9974 kind: ExprKind::Deref {
9975 expr: Box::new(inner),
9976 kind: Sigil::Hash,
9977 },
9978 line,
9979 })
9980 }
9981 Token::LBracket => {
9982 self.advance();
9983 let pairs = self.parse_hashref_pairs_until(&Token::RBracket)?;
9984 self.expect(&Token::RBracket)?;
9985 let href = Expr {
9986 kind: ExprKind::HashRef(pairs),
9987 line,
9988 };
9989 Ok(Expr {
9990 kind: ExprKind::Deref {
9991 expr: Box::new(href),
9992 kind: Sigil::Hash,
9993 },
9994 line,
9995 })
9996 }
9997 tok => Err(self.syntax_err(
9998 format!(
9999 "Expected identifier, `$`, `{{`, or `[` after `%`, got {:?}",
10000 tok
10001 ),
10002 line,
10003 )),
10004 }
10005 }
10006
10007 tok => Err(self.syntax_err(format!("Unexpected token {:?}", tok), line)),
10008 }
10009 }
10010
10011 fn parse_named_expr(&mut self, mut name: String) -> StrykeResult<Expr> {
10012 let line = self.peek_line();
10013 self.advance(); while self.eat(&Token::PackageSep) {
10015 match self.advance() {
10016 (Token::Ident(part), _) => {
10017 name = format!("{}::{}", name, part);
10018 }
10019 (tok, err_line) => {
10020 return Err(self.syntax_err(
10021 format!("Expected identifier after `::`, got {:?}", tok),
10022 err_line,
10023 ));
10024 }
10025 }
10026 }
10027
10028 if matches!(self.peek(), Token::FatArrow) && !Self::is_underscore_topic_slot(&name) {
10035 return Ok(Expr {
10036 kind: ExprKind::String(name),
10037 line,
10038 });
10039 }
10040
10041 if crate::compat_mode() {
10042 if let Some(ext) = Self::stryke_extension_name(&name) {
10043 if !self.declared_subs.contains(&name) {
10044 return Err(self.syntax_err(
10045 format!("`{ext}` is a stryke extension (disabled by --compat)"),
10046 line,
10047 ));
10048 }
10049 }
10050 }
10051
10052 if let Some(rest) = name.strip_prefix("CORE::") {
10059 name = rest.to_string();
10060 }
10061
10062 if name == "null" && !crate::compat_mode() {
10067 name = "undef".to_string();
10068 }
10069
10070 match name.as_str() {
10071 "__FILE__" => Ok(Expr {
10072 kind: ExprKind::MagicConst(MagicConstKind::File),
10073 line,
10074 }),
10075 "__LINE__" => Ok(Expr {
10076 kind: ExprKind::MagicConst(MagicConstKind::Line),
10077 line,
10078 }),
10079 "__SUB__" => Ok(Expr {
10080 kind: ExprKind::MagicConst(MagicConstKind::Sub),
10081 line,
10082 }),
10083 "__PACKAGE__" => Ok(Expr {
10088 kind: ExprKind::String(self.current_package.clone()),
10089 line,
10090 }),
10091 "stdin" => Ok(Expr {
10092 kind: ExprKind::FuncCall {
10093 name: "stdin".into(),
10094 args: vec![],
10095 },
10096 line,
10097 }),
10098 "range" => {
10099 let args = self.parse_builtin_args()?;
10100 Ok(Expr {
10101 kind: ExprKind::FuncCall {
10102 name: "range".into(),
10103 args,
10104 },
10105 line,
10106 })
10107 }
10108 "print" | "pr" => self.parse_print_like(|h, a| ExprKind::Print { handle: h, args: a }),
10109 "say" => {
10110 if crate::no_interop_mode() {
10111 return Err(
10112 self.syntax_err("stryke uses `p` instead of `say` (--no-interop)", line)
10113 );
10114 }
10115 self.parse_print_like(|h, a| ExprKind::Say { handle: h, args: a })
10116 }
10117 "p" => self.parse_print_like(|h, a| ExprKind::Say { handle: h, args: a }),
10118 "printf" => self.parse_print_like(|h, a| ExprKind::Printf { handle: h, args: a }),
10119 "die" => {
10120 let args = self.parse_list_until_terminator()?;
10121 Ok(Expr {
10122 kind: ExprKind::Die(args),
10123 line,
10124 })
10125 }
10126 "warn" => {
10127 let args = self.parse_list_until_terminator()?;
10128 Ok(Expr {
10129 kind: ExprKind::Warn(args),
10130 line,
10131 })
10132 }
10133 "croak" | "confess" => {
10138 let args = self.parse_list_until_terminator()?;
10139 Ok(Expr {
10140 kind: ExprKind::Die(args),
10141 line,
10142 })
10143 }
10144 "carp" | "cluck" => {
10146 let args = self.parse_list_until_terminator()?;
10147 Ok(Expr {
10148 kind: ExprKind::Warn(args),
10149 line,
10150 })
10151 }
10152 "chomp" => {
10153 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10154 return Ok(e);
10155 }
10156 let a = self.parse_one_arg_or_default()?;
10157 Ok(Expr {
10158 kind: ExprKind::Chomp(Box::new(a)),
10159 line,
10160 })
10161 }
10162 "chop" => {
10163 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10164 return Ok(e);
10165 }
10166 let a = self.parse_one_arg_or_default()?;
10167 Ok(Expr {
10168 kind: ExprKind::Chop(Box::new(a)),
10169 line,
10170 })
10171 }
10172 "length" => {
10173 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10174 return Ok(e);
10175 }
10176 let a = self.parse_one_arg_or_default()?;
10177 Ok(Expr {
10178 kind: ExprKind::Length(Box::new(a)),
10179 line,
10180 })
10181 }
10182 "defined" => {
10183 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10184 return Ok(e);
10185 }
10186 let a = if matches!(
10194 self.peek(),
10195 Token::Semicolon
10196 | Token::RBrace
10197 | Token::RParen
10198 | Token::RBracket
10199 | Token::Eof
10200 | Token::Comma
10201 | Token::FatArrow
10202 | Token::PipeForward
10203 | Token::Question
10204 | Token::Colon
10205 | Token::NumEq
10206 | Token::NumNe
10207 | Token::NumLt
10208 | Token::NumGt
10209 | Token::NumLe
10210 | Token::NumGe
10211 | Token::Spaceship
10212 | Token::StrEq
10213 | Token::StrNe
10214 | Token::StrLt
10215 | Token::StrGt
10216 | Token::StrLe
10217 | Token::StrGe
10218 | Token::StrCmp
10219 | Token::LogAnd
10220 | Token::LogOr
10221 | Token::LogNot
10222 | Token::LogAndWord
10223 | Token::LogOrWord
10224 | Token::LogNotWord
10225 | Token::DefinedOr
10226 | Token::Range
10227 | Token::RangeExclusive
10228 | Token::Assign
10229 | Token::PlusAssign
10230 | Token::MinusAssign
10231 | Token::MulAssign
10232 | Token::DivAssign
10233 | Token::ModAssign
10234 | Token::PowAssign
10235 | Token::DotAssign
10236 | Token::AndAssign
10237 | Token::OrAssign
10238 | Token::XorAssign
10239 | Token::DefinedOrAssign
10240 | Token::ShiftLeftAssign
10241 | Token::ShiftRightAssign
10242 | Token::BitAndAssign
10243 | Token::BitOrAssign
10244 ) {
10245 Expr {
10246 kind: ExprKind::ScalarVar("_".into()),
10247 line: self.peek_line(),
10248 }
10249 } else if matches!(self.peek(), Token::LParen)
10250 && matches!(self.peek_at(1), Token::RParen)
10251 {
10252 let pl = self.peek_line();
10253 self.advance();
10254 self.advance();
10255 Expr {
10256 kind: ExprKind::ScalarVar("_".into()),
10257 line: pl,
10258 }
10259 } else {
10260 self.parse_named_unary_arg()?
10261 };
10262 Ok(Expr {
10263 kind: ExprKind::Defined(Box::new(a)),
10264 line,
10265 })
10266 }
10267 "ref" => {
10268 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10269 return Ok(e);
10270 }
10271 let a = self.parse_one_arg_or_default()?;
10272 Ok(Expr {
10273 kind: ExprKind::Ref(Box::new(a)),
10274 line,
10275 })
10276 }
10277 "undef" => {
10278 if self.peek_line() == self.prev_line()
10281 && matches!(
10282 self.peek(),
10283 Token::ScalarVar(_) | Token::ArrayVar(_) | Token::HashVar(_)
10284 )
10285 {
10286 let target = self.parse_primary()?;
10287 return Ok(Expr {
10288 kind: ExprKind::Assign {
10289 target: Box::new(target),
10290 value: Box::new(Expr {
10291 kind: ExprKind::Undef,
10292 line,
10293 }),
10294 },
10295 line,
10296 });
10297 }
10298 Ok(Expr {
10299 kind: ExprKind::Undef,
10300 line,
10301 })
10302 }
10303 "scalar" => {
10304 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10305 return Ok(e);
10306 }
10307 if crate::no_interop_mode() {
10308 return Err(self.syntax_err(
10309 "stryke uses `len` (also `cnt` / `count`) instead of `scalar` (--no-interop)",
10310 line,
10311 ));
10312 }
10313 let a = self.parse_one_arg_or_default()?;
10314 Ok(Expr {
10315 kind: ExprKind::ScalarContext(Box::new(a)),
10316 line,
10317 })
10318 }
10319 "abs" => {
10320 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10321 return Ok(e);
10322 }
10323 let a = self.parse_one_arg_or_default()?;
10324 Ok(Expr {
10325 kind: ExprKind::Abs(Box::new(a)),
10326 line,
10327 })
10328 }
10329 "inc" | "dec" => {
10334 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10335 return Ok(e);
10336 }
10337 let a = self.parse_one_arg_or_default()?;
10338 Ok(Expr {
10339 kind: ExprKind::FuncCall {
10340 name,
10341 args: vec![a],
10342 },
10343 line,
10344 })
10345 }
10346 "int" => {
10347 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10348 return Ok(e);
10349 }
10350 let a = self.parse_one_arg_or_default()?;
10351 Ok(Expr {
10352 kind: ExprKind::Int(Box::new(a)),
10353 line,
10354 })
10355 }
10356 "sqrt" => {
10357 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10358 return Ok(e);
10359 }
10360 let a = self.parse_one_arg_or_default()?;
10361 Ok(Expr {
10362 kind: ExprKind::Sqrt(Box::new(a)),
10363 line,
10364 })
10365 }
10366 "sin" => {
10367 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10368 return Ok(e);
10369 }
10370 let a = self.parse_one_arg_or_default()?;
10371 Ok(Expr {
10372 kind: ExprKind::Sin(Box::new(a)),
10373 line,
10374 })
10375 }
10376 "cos" => {
10377 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10378 return Ok(e);
10379 }
10380 let a = self.parse_one_arg_or_default()?;
10381 Ok(Expr {
10382 kind: ExprKind::Cos(Box::new(a)),
10383 line,
10384 })
10385 }
10386 "atan2" => {
10387 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10388 return Ok(e);
10389 }
10390 let args = self.parse_builtin_args()?;
10391 if args.len() != 2 {
10392 return Err(self.syntax_err("atan2 requires two arguments", line));
10393 }
10394 Ok(Expr {
10395 kind: ExprKind::Atan2 {
10396 y: Box::new(args[0].clone()),
10397 x: Box::new(args[1].clone()),
10398 },
10399 line,
10400 })
10401 }
10402 "exp" => {
10403 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10404 return Ok(e);
10405 }
10406 let a = self.parse_one_arg_or_default()?;
10407 Ok(Expr {
10408 kind: ExprKind::Exp(Box::new(a)),
10409 line,
10410 })
10411 }
10412 "log" => {
10413 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10414 return Ok(e);
10415 }
10416 let a = self.parse_one_arg_or_default()?;
10417 Ok(Expr {
10418 kind: ExprKind::Log(Box::new(a)),
10419 line,
10420 })
10421 }
10422 "input" => {
10423 let args = if matches!(
10424 self.peek(),
10425 Token::Semicolon
10426 | Token::RBrace
10427 | Token::RParen
10428 | Token::Eof
10429 | Token::Comma
10430 | Token::PipeForward
10431 ) {
10432 vec![]
10433 } else if matches!(self.peek(), Token::LParen) {
10434 self.advance();
10435 if matches!(self.peek(), Token::RParen) {
10436 self.advance();
10437 vec![]
10438 } else {
10439 let a = self.parse_expression()?;
10440 self.expect(&Token::RParen)?;
10441 vec![a]
10442 }
10443 } else {
10444 let a = self.parse_one_arg()?;
10445 vec![a]
10446 };
10447 Ok(Expr {
10448 kind: ExprKind::FuncCall {
10449 name: "input".to_string(),
10450 args,
10451 },
10452 line,
10453 })
10454 }
10455 "rand" => {
10456 if matches!(
10457 self.peek(),
10458 Token::Semicolon
10459 | Token::RBrace
10460 | Token::RParen
10461 | Token::Eof
10462 | Token::Comma
10463 | Token::PipeForward
10464 ) {
10465 Ok(Expr {
10466 kind: ExprKind::Rand(None),
10467 line,
10468 })
10469 } else if matches!(self.peek(), Token::LParen) {
10470 self.advance();
10471 if matches!(self.peek(), Token::RParen) {
10472 self.advance();
10473 Ok(Expr {
10474 kind: ExprKind::Rand(None),
10475 line,
10476 })
10477 } else {
10478 let a = self.parse_expression()?;
10479 self.expect(&Token::RParen)?;
10480 Ok(Expr {
10481 kind: ExprKind::Rand(Some(Box::new(a))),
10482 line,
10483 })
10484 }
10485 } else {
10486 let a = self.parse_one_arg()?;
10487 Ok(Expr {
10488 kind: ExprKind::Rand(Some(Box::new(a))),
10489 line,
10490 })
10491 }
10492 }
10493 "srand" => {
10494 if matches!(
10495 self.peek(),
10496 Token::Semicolon
10497 | Token::RBrace
10498 | Token::RParen
10499 | Token::Eof
10500 | Token::Comma
10501 | Token::PipeForward
10502 ) {
10503 Ok(Expr {
10504 kind: ExprKind::Srand(None),
10505 line,
10506 })
10507 } else if matches!(self.peek(), Token::LParen) {
10508 self.advance();
10509 if matches!(self.peek(), Token::RParen) {
10510 self.advance();
10511 Ok(Expr {
10512 kind: ExprKind::Srand(None),
10513 line,
10514 })
10515 } else {
10516 let a = self.parse_expression()?;
10517 self.expect(&Token::RParen)?;
10518 Ok(Expr {
10519 kind: ExprKind::Srand(Some(Box::new(a))),
10520 line,
10521 })
10522 }
10523 } else {
10524 let a = self.parse_one_arg()?;
10525 Ok(Expr {
10526 kind: ExprKind::Srand(Some(Box::new(a))),
10527 line,
10528 })
10529 }
10530 }
10531 "hex" => {
10532 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10533 return Ok(e);
10534 }
10535 let a = self.parse_one_arg_or_default()?;
10536 Ok(Expr {
10537 kind: ExprKind::Hex(Box::new(a)),
10538 line,
10539 })
10540 }
10541 "oct" => {
10542 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10543 return Ok(e);
10544 }
10545 let a = self.parse_one_arg_or_default()?;
10546 Ok(Expr {
10547 kind: ExprKind::Oct(Box::new(a)),
10548 line,
10549 })
10550 }
10551 "chr" => {
10552 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10553 return Ok(e);
10554 }
10555 let a = self.parse_one_arg_or_default()?;
10556 Ok(Expr {
10557 kind: ExprKind::Chr(Box::new(a)),
10558 line,
10559 })
10560 }
10561 "ord" => {
10562 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10563 return Ok(e);
10564 }
10565 let a = self.parse_one_arg_or_default()?;
10566 Ok(Expr {
10567 kind: ExprKind::Ord(Box::new(a)),
10568 line,
10569 })
10570 }
10571 "lc" => {
10572 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10573 return Ok(e);
10574 }
10575 let a = self.parse_one_arg_or_default()?;
10576 Ok(Expr {
10577 kind: ExprKind::Lc(Box::new(a)),
10578 line,
10579 })
10580 }
10581 "uc" => {
10582 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10583 return Ok(e);
10584 }
10585 let a = self.parse_one_arg_or_default()?;
10586 Ok(Expr {
10587 kind: ExprKind::Uc(Box::new(a)),
10588 line,
10589 })
10590 }
10591 "lcfirst" => {
10592 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10593 return Ok(e);
10594 }
10595 let a = self.parse_one_arg_or_default()?;
10596 Ok(Expr {
10597 kind: ExprKind::Lcfirst(Box::new(a)),
10598 line,
10599 })
10600 }
10601 "ucfirst" => {
10602 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10603 return Ok(e);
10604 }
10605 let a = self.parse_one_arg_or_default()?;
10606 Ok(Expr {
10607 kind: ExprKind::Ucfirst(Box::new(a)),
10608 line,
10609 })
10610 }
10611 "fc" => {
10612 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10613 return Ok(e);
10614 }
10615 let a = self.parse_one_arg_or_default()?;
10616 Ok(Expr {
10617 kind: ExprKind::Fc(Box::new(a)),
10618 line,
10619 })
10620 }
10621 "crypt" => {
10622 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10623 return Ok(e);
10624 }
10625 let args = self.parse_builtin_args()?;
10626 if args.len() != 2 {
10627 return Err(self.syntax_err("crypt requires two arguments", line));
10628 }
10629 Ok(Expr {
10630 kind: ExprKind::Crypt {
10631 plaintext: Box::new(args[0].clone()),
10632 salt: Box::new(args[1].clone()),
10633 },
10634 line,
10635 })
10636 }
10637 "pos" => {
10638 if matches!(
10639 self.peek(),
10640 Token::Semicolon
10641 | Token::RBrace
10642 | Token::RParen
10643 | Token::Eof
10644 | Token::Comma
10645 | Token::PipeForward
10646 ) {
10647 Ok(Expr {
10648 kind: ExprKind::Pos(None),
10649 line,
10650 })
10651 } else if matches!(self.peek(), Token::Assign) {
10652 self.advance();
10654 let rhs = self.parse_assign_expr()?;
10655 Ok(Expr {
10656 kind: ExprKind::Assign {
10657 target: Box::new(Expr {
10658 kind: ExprKind::Pos(Some(Box::new(Expr {
10659 kind: ExprKind::ScalarVar("_".into()),
10660 line,
10661 }))),
10662 line,
10663 }),
10664 value: Box::new(rhs),
10665 },
10666 line,
10667 })
10668 } else if matches!(self.peek(), Token::LParen) {
10669 self.advance();
10670 if matches!(self.peek(), Token::RParen) {
10671 self.advance();
10672 Ok(Expr {
10673 kind: ExprKind::Pos(None),
10674 line,
10675 })
10676 } else {
10677 let a = self.parse_expression()?;
10678 self.expect(&Token::RParen)?;
10679 Ok(Expr {
10680 kind: ExprKind::Pos(Some(Box::new(a))),
10681 line,
10682 })
10683 }
10684 } else {
10685 let saved = self.pos;
10686 let subj = self.parse_unary()?;
10687 if matches!(self.peek(), Token::Assign) {
10688 self.advance();
10689 let rhs = self.parse_assign_expr()?;
10690 Ok(Expr {
10691 kind: ExprKind::Assign {
10692 target: Box::new(Expr {
10693 kind: ExprKind::Pos(Some(Box::new(subj))),
10694 line,
10695 }),
10696 value: Box::new(rhs),
10697 },
10698 line,
10699 })
10700 } else {
10701 self.pos = saved;
10702 let a = self.parse_one_arg()?;
10703 Ok(Expr {
10704 kind: ExprKind::Pos(Some(Box::new(a))),
10705 line,
10706 })
10707 }
10708 }
10709 }
10710 "study" => {
10711 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10712 return Ok(e);
10713 }
10714 let a = self.parse_one_arg_or_default()?;
10715 Ok(Expr {
10716 kind: ExprKind::Study(Box::new(a)),
10717 line,
10718 })
10719 }
10720 "push" => {
10721 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10722 return Ok(e);
10723 }
10724 let args = self.parse_builtin_args()?;
10725 let (first, rest) = args
10726 .split_first()
10727 .ok_or_else(|| self.syntax_err("push requires arguments", line))?;
10728 if matches!(
10733 first.kind,
10734 ExprKind::ScalarVar(_)
10735 | ExprKind::Integer(_)
10736 | ExprKind::Float(_)
10737 | ExprKind::String(_)
10738 ) {
10739 return Err(self
10740 .syntax_err("Experimental push on scalar is now forbidden", line)
10741 .with_near("at EOF"));
10742 }
10743 Ok(Expr {
10744 kind: ExprKind::Push {
10745 array: Box::new(first.clone()),
10746 values: rest.to_vec(),
10747 },
10748 line,
10749 })
10750 }
10751 "pop" => {
10752 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10753 return Ok(e);
10754 }
10755 let a = self.parse_one_arg_or_argv()?;
10756 Ok(Expr {
10757 kind: ExprKind::Pop(Box::new(a)),
10758 line,
10759 })
10760 }
10761 "shift" => {
10762 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10763 return Ok(e);
10764 }
10765 let a = self.parse_one_arg_or_argv()?;
10766 Ok(Expr {
10767 kind: ExprKind::Shift(Box::new(a)),
10768 line,
10769 })
10770 }
10771 "unshift" => {
10772 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10773 return Ok(e);
10774 }
10775 let args = self.parse_builtin_args()?;
10776 let (first, rest) = args
10777 .split_first()
10778 .ok_or_else(|| self.syntax_err("unshift requires arguments", line))?;
10779 Ok(Expr {
10780 kind: ExprKind::Unshift {
10781 array: Box::new(first.clone()),
10782 values: rest.to_vec(),
10783 },
10784 line,
10785 })
10786 }
10787 "splice" => {
10788 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10789 return Ok(e);
10790 }
10791 let args = self.parse_builtin_args()?;
10792 let mut iter = args.into_iter();
10793 let array = Box::new(
10794 iter.next()
10795 .ok_or_else(|| self.syntax_err("splice requires arguments", line))?,
10796 );
10797 let offset = iter.next().map(Box::new);
10798 let length = iter.next().map(Box::new);
10799 let replacement: Vec<Expr> = iter.collect();
10800 Ok(Expr {
10801 kind: ExprKind::Splice {
10802 array,
10803 offset,
10804 length,
10805 replacement,
10806 },
10807 line,
10808 })
10809 }
10810 "splice_last" | "splice1" | "spl_last" => {
10815 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10816 return Ok(e);
10817 }
10818 let args = self.parse_builtin_args()?;
10819 let mut iter = args.into_iter();
10820 let array = Box::new(
10821 iter.next()
10822 .ok_or_else(|| self.syntax_err("splice_last requires arguments", line))?,
10823 );
10824 let offset = iter.next().map(Box::new);
10825 let length = iter.next().map(Box::new);
10826 let replacement: Vec<Expr> = iter.collect();
10827 let splice_expr = Expr {
10828 kind: ExprKind::Splice {
10829 array,
10830 offset,
10831 length,
10832 replacement,
10833 },
10834 line,
10835 };
10836 Ok(Expr {
10837 kind: ExprKind::FuncCall {
10838 name: "tail".to_string(),
10839 args: vec![splice_expr],
10840 },
10841 line,
10842 })
10843 }
10844 "delete" => {
10845 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10846 return Ok(e);
10847 }
10848 let a = self.parse_postfix()?;
10849 Ok(Expr {
10850 kind: ExprKind::Delete(Box::new(a)),
10851 line,
10852 })
10853 }
10854 "exists" => {
10855 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10856 return Ok(e);
10857 }
10858 let a = self.parse_unary()?;
10863 Ok(Expr {
10864 kind: ExprKind::Exists(Box::new(a)),
10865 line,
10866 })
10867 }
10868 "keys" => {
10869 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10870 return Ok(e);
10871 }
10872 let a = self.parse_one_arg_or_default()?;
10873 Ok(Expr {
10874 kind: ExprKind::Keys(Box::new(a)),
10875 line,
10876 })
10877 }
10878 "values" => {
10879 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10880 return Ok(e);
10881 }
10882 let a = self.parse_one_arg_or_default()?;
10883 Ok(Expr {
10884 kind: ExprKind::Values(Box::new(a)),
10885 line,
10886 })
10887 }
10888 "each" => {
10889 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10890 return Ok(e);
10891 }
10892 let a = self.parse_one_arg_or_default()?;
10893 Ok(Expr {
10894 kind: ExprKind::Each(Box::new(a)),
10895 line,
10896 })
10897 }
10898 "fore" | "e" | "ep" => {
10899 if matches!(self.peek(), Token::LBrace) {
10901 let (block, list) = self.parse_block_list()?;
10902 Ok(Expr {
10903 kind: ExprKind::ForEachExpr {
10904 block,
10905 list: Box::new(list),
10906 },
10907 line,
10908 })
10909 } else if self.in_pipe_rhs() {
10910 let is_terminal = matches!(
10913 self.peek(),
10914 Token::Semicolon
10915 | Token::RParen
10916 | Token::Eof
10917 | Token::PipeForward
10918 | Token::RBrace
10919 );
10920 let block = if name == "ep" && is_terminal {
10921 vec![Statement {
10922 label: None,
10923 kind: StmtKind::Expression(Expr {
10924 kind: ExprKind::Say {
10925 handle: None,
10926 args: vec![Expr {
10927 kind: ExprKind::ScalarVar("_".into()),
10928 line,
10929 }],
10930 },
10931 line,
10932 }),
10933 line,
10934 }]
10935 } else {
10936 let expr = self.parse_assign_expr_stop_at_pipe()?;
10937 let expr = Self::lift_bareword_to_topic_call(expr);
10938 vec![Statement {
10939 label: None,
10940 kind: StmtKind::Expression(expr),
10941 line,
10942 }]
10943 };
10944 let list = self.pipe_placeholder_list(line);
10945 Ok(Expr {
10946 kind: ExprKind::ForEachExpr {
10947 block,
10948 list: Box::new(list),
10949 },
10950 line,
10951 })
10952 } else {
10953 let expr = self.parse_assign_expr()?;
10962 let expr = Self::lift_bareword_to_topic_call(expr);
10963 if !matches!(self.peek(), Token::Comma) && name == "ep" {
10964 let block = vec![Statement {
10965 label: None,
10966 kind: StmtKind::Expression(Expr {
10967 kind: ExprKind::Say {
10968 handle: None,
10969 args: vec![Expr {
10970 kind: ExprKind::ScalarVar("_".into()),
10971 line,
10972 }],
10973 },
10974 line,
10975 }),
10976 line,
10977 }];
10978 return Ok(Expr {
10979 kind: ExprKind::ForEachExpr {
10980 block,
10981 list: Box::new(expr),
10982 },
10983 line,
10984 });
10985 }
10986 self.expect(&Token::Comma)?;
10987 let list_parts = self.parse_list_until_terminator()?;
10988 let list_expr = if list_parts.len() == 1 {
10989 list_parts.into_iter().next().unwrap()
10990 } else {
10991 Expr {
10992 kind: ExprKind::List(list_parts),
10993 line,
10994 }
10995 };
10996 let block = vec![Statement {
10997 label: None,
10998 kind: StmtKind::Expression(expr),
10999 line,
11000 }];
11001 Ok(Expr {
11002 kind: ExprKind::ForEachExpr {
11003 block,
11004 list: Box::new(list_expr),
11005 },
11006 line,
11007 })
11008 }
11009 }
11010 "rev" => {
11011 let prev = self.prev_line();
11017 let a = if self.in_pipe_rhs()
11018 && (matches!(
11019 self.peek(),
11020 Token::Semicolon | Token::RParen | Token::Eof | Token::PipeForward
11021 ) || self.peek_line() > prev)
11022 {
11023 self.pipe_placeholder_list(line)
11024 } else if self.peek_line() > prev {
11025 Expr {
11031 kind: ExprKind::ScalarVar("_".into()),
11032 line: prev,
11033 }
11034 } else if matches!(
11035 self.peek(),
11036 Token::Semicolon
11037 | Token::RBrace
11038 | Token::RParen
11039 | Token::RBracket
11040 | Token::Eof
11041 | Token::Comma
11042 | Token::FatArrow
11043 | Token::PipeForward
11044 ) {
11045 Expr {
11046 kind: ExprKind::ScalarVar("_".into()),
11047 line: self.peek_line(),
11048 }
11049 } else if matches!(self.peek(), Token::LParen)
11050 && matches!(self.peek_at(1), Token::RParen)
11051 {
11052 let pl = self.peek_line();
11055 self.advance(); self.advance(); Expr {
11058 kind: ExprKind::ScalarVar("_".into()),
11059 line: pl,
11060 }
11061 } else {
11062 self.parse_one_arg()?
11063 };
11064 Ok(Expr {
11065 kind: ExprKind::Rev(Box::new(a)),
11066 line,
11067 })
11068 }
11069 "reverse" => {
11070 if crate::no_interop_mode() {
11071 return Err(self.syntax_err(
11072 "stryke uses `rev` instead of `reverse` (--no-interop)",
11073 line,
11074 ));
11075 }
11076 let a = if self.in_pipe_rhs()
11078 && matches!(
11079 self.peek(),
11080 Token::Semicolon
11081 | Token::RBrace
11082 | Token::RParen
11083 | Token::Eof
11084 | Token::PipeForward
11085 ) {
11086 self.pipe_placeholder_list(line)
11087 } else if matches!(self.peek(), Token::LParen)
11088 && matches!(self.peek_at(1), Token::RParen)
11089 {
11090 self.advance();
11092 self.advance();
11093 Expr {
11094 kind: ExprKind::List(Vec::new()),
11095 line,
11096 }
11097 } else {
11098 self.parse_one_arg()?
11099 };
11100 Ok(Expr {
11101 kind: ExprKind::ReverseExpr(Box::new(a)),
11102 line,
11103 })
11104 }
11105 "reversed" | "rv" => {
11106 let a = if self.in_pipe_rhs()
11108 && matches!(
11109 self.peek(),
11110 Token::Semicolon
11111 | Token::RBrace
11112 | Token::RParen
11113 | Token::Eof
11114 | Token::PipeForward
11115 ) {
11116 self.pipe_placeholder_list(line)
11117 } else {
11118 self.parse_one_arg()?
11119 };
11120 Ok(Expr {
11121 kind: ExprKind::Rev(Box::new(a)),
11122 line,
11123 })
11124 }
11125 "join" => {
11126 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
11127 return Ok(e);
11128 }
11129 let args = self.parse_builtin_args()?;
11130 if args.is_empty() {
11131 return Err(self.syntax_err("join requires separator and list", line));
11132 }
11133 if args.len() < 2 && !self.in_pipe_rhs() {
11135 return Err(self.syntax_err("join requires separator and list", line));
11136 }
11137 Ok(Expr {
11138 kind: ExprKind::JoinExpr {
11139 separator: Box::new(args[0].clone()),
11140 list: Box::new(Expr {
11141 kind: ExprKind::List(args[1..].to_vec()),
11142 line,
11143 }),
11144 },
11145 line,
11146 })
11147 }
11148 "split" => {
11149 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
11150 return Ok(e);
11151 }
11152 let args = self.parse_builtin_args()?;
11153 let pattern = args.first().cloned().unwrap_or(Expr {
11154 kind: ExprKind::String(" ".into()),
11155 line,
11156 });
11157 let string = args.get(1).cloned().unwrap_or(Expr {
11158 kind: ExprKind::ScalarVar("_".into()),
11159 line,
11160 });
11161 let limit = args.get(2).cloned().map(Box::new);
11162 Ok(Expr {
11163 kind: ExprKind::SplitExpr {
11164 pattern: Box::new(pattern),
11165 string: Box::new(string),
11166 limit,
11167 },
11168 line,
11169 })
11170 }
11171 "substr" => {
11172 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
11173 return Ok(e);
11174 }
11175 let args = self.parse_builtin_args()?;
11176 Ok(Expr {
11177 kind: ExprKind::Substr {
11178 string: Box::new(args[0].clone()),
11179 offset: Box::new(args[1].clone()),
11180 length: args.get(2).cloned().map(Box::new),
11181 replacement: args.get(3).cloned().map(Box::new),
11182 },
11183 line,
11184 })
11185 }
11186 "index" => {
11187 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
11188 return Ok(e);
11189 }
11190 let args = self.parse_builtin_args()?;
11191 Ok(Expr {
11192 kind: ExprKind::Index {
11193 string: Box::new(args[0].clone()),
11194 substr: Box::new(args[1].clone()),
11195 position: args.get(2).cloned().map(Box::new),
11196 },
11197 line,
11198 })
11199 }
11200 "rindex" => {
11201 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
11202 return Ok(e);
11203 }
11204 let args = self.parse_builtin_args()?;
11205 Ok(Expr {
11206 kind: ExprKind::Rindex {
11207 string: Box::new(args[0].clone()),
11208 substr: Box::new(args[1].clone()),
11209 position: args.get(2).cloned().map(Box::new),
11210 },
11211 line,
11212 })
11213 }
11214 "sprintf" => {
11215 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
11216 return Ok(e);
11217 }
11218 let args = self.parse_builtin_args()?;
11219 let (first, rest) = args
11220 .split_first()
11221 .ok_or_else(|| self.syntax_err("sprintf requires format", line))?;
11222 Ok(Expr {
11223 kind: ExprKind::Sprintf {
11224 format: Box::new(first.clone()),
11225 args: rest.to_vec(),
11226 },
11227 line,
11228 })
11229 }
11230 "map" | "flat_map" | "maps" | "flat_maps" => {
11231 let flatten_array_refs = matches!(name.as_str(), "flat_map" | "flat_maps");
11232 let stream = matches!(name.as_str(), "maps" | "flat_maps");
11233 if matches!(self.peek(), Token::LBrace) {
11234 let (block, list) = self.parse_block_list()?;
11235 Ok(Expr {
11236 kind: ExprKind::MapExpr {
11237 block,
11238 list: Box::new(list),
11239 flatten_array_refs,
11240 stream,
11241 },
11242 line,
11243 })
11244 } else {
11245 let expr = self.parse_assign_expr_stop_at_pipe()?;
11246 let expr = Self::lift_bareword_to_topic_call(expr);
11249 let list_expr = if self.pipe_supplies_slurped_list_operand() {
11250 self.pipe_placeholder_list(line)
11251 } else {
11252 self.expect(&Token::Comma)?;
11253 let list_parts = self.parse_list_until_terminator()?;
11254 if list_parts.len() == 1 {
11255 list_parts.into_iter().next().unwrap()
11256 } else {
11257 Expr {
11258 kind: ExprKind::List(list_parts),
11259 line,
11260 }
11261 }
11262 };
11263 Ok(Expr {
11264 kind: ExprKind::MapExprComma {
11265 expr: Box::new(expr),
11266 list: Box::new(list_expr),
11267 flatten_array_refs,
11268 stream,
11269 },
11270 line,
11271 })
11272 }
11273 }
11274 "cond" => {
11275 if crate::compat_mode() {
11276 return Err(self
11277 .syntax_err("`cond` is a stryke extension (disabled by --compat)", line));
11278 }
11279 self.parse_cond_expr(line)
11280 }
11281 "match" => {
11282 if crate::compat_mode() {
11283 return Err(self.syntax_err(
11284 "algebraic `match` is a stryke extension (disabled by --compat)",
11285 line,
11286 ));
11287 }
11288 self.parse_algebraic_match_expr(line)
11289 }
11290 "grep" | "greps" | "filter" | "fi" | "find_all" => {
11291 let keyword = match name.as_str() {
11292 "grep" => crate::ast::GrepBuiltinKeyword::Grep,
11293 "greps" => crate::ast::GrepBuiltinKeyword::Greps,
11294 "filter" | "fi" => crate::ast::GrepBuiltinKeyword::Filter,
11295 "find_all" => crate::ast::GrepBuiltinKeyword::FindAll,
11296 _ => unreachable!(),
11297 };
11298 if matches!(self.peek(), Token::LBrace) {
11299 let (block, list) = self.parse_block_list()?;
11300 Ok(Expr {
11301 kind: ExprKind::GrepExpr {
11302 block,
11303 list: Box::new(list),
11304 keyword,
11305 },
11306 line,
11307 })
11308 } else {
11309 let expr = self.parse_assign_expr_stop_at_pipe()?;
11310 if self.pipe_supplies_slurped_list_operand() {
11311 let list = self.pipe_placeholder_list(line);
11316 let topic = Expr {
11317 kind: ExprKind::ScalarVar("_".into()),
11318 line,
11319 };
11320 let test = match &expr.kind {
11321 ExprKind::Integer(_) | ExprKind::Float(_) => Expr {
11322 kind: ExprKind::BinOp {
11323 op: BinOp::NumEq,
11324 left: Box::new(topic),
11325 right: Box::new(expr),
11326 },
11327 line,
11328 },
11329 ExprKind::String(_) | ExprKind::InterpolatedString(_) => Expr {
11330 kind: ExprKind::BinOp {
11331 op: BinOp::StrEq,
11332 left: Box::new(topic),
11333 right: Box::new(expr),
11334 },
11335 line,
11336 },
11337 ExprKind::Regex { .. } => Expr {
11338 kind: ExprKind::BinOp {
11339 op: BinOp::BindMatch,
11340 left: Box::new(topic),
11341 right: Box::new(expr),
11342 },
11343 line,
11344 },
11345 _ => {
11346 let expr = Self::lift_bareword_to_topic_call(expr);
11352 return Ok(Expr {
11353 kind: ExprKind::GrepExprComma {
11354 expr: Box::new(expr),
11355 list: Box::new(list),
11356 keyword,
11357 },
11358 line,
11359 });
11360 }
11361 };
11362 let block = vec![Statement {
11363 label: None,
11364 kind: StmtKind::Expression(test),
11365 line,
11366 }];
11367 Ok(Expr {
11368 kind: ExprKind::GrepExpr {
11369 block,
11370 list: Box::new(list),
11371 keyword,
11372 },
11373 line,
11374 })
11375 } else {
11376 let expr = Self::lift_bareword_to_topic_call(expr);
11377 self.expect(&Token::Comma)?;
11378 let list_parts = self.parse_list_until_terminator()?;
11379 let list_expr = if list_parts.len() == 1 {
11380 list_parts.into_iter().next().unwrap()
11381 } else {
11382 Expr {
11383 kind: ExprKind::List(list_parts),
11384 line,
11385 }
11386 };
11387 Ok(Expr {
11388 kind: ExprKind::GrepExprComma {
11389 expr: Box::new(expr),
11390 list: Box::new(list_expr),
11391 keyword,
11392 },
11393 line,
11394 })
11395 }
11396 }
11397 }
11398 "sort" => {
11399 use crate::ast::SortComparator;
11400 if matches!(self.peek(), Token::LBrace) {
11401 let block = self.parse_block()?;
11402 let block_end_line = self.prev_line();
11403 let _ = self.eat(&Token::Comma);
11404 let list = if self.in_pipe_rhs()
11405 && (matches!(
11406 self.peek(),
11407 Token::Semicolon
11408 | Token::RBrace
11409 | Token::RParen
11410 | Token::Eof
11411 | Token::PipeForward
11412 ) || self.peek_line() > block_end_line)
11413 {
11414 self.pipe_placeholder_list(line)
11415 } else {
11416 self.parse_expression()?
11417 };
11418 Ok(Expr {
11419 kind: ExprKind::SortExpr {
11420 cmp: Some(SortComparator::Block(block)),
11421 list: Box::new(list),
11422 },
11423 line,
11424 })
11425 } else if matches!(self.peek(), Token::ScalarVar(ref v) if v == "a" || v == "b") {
11426 let block = self.parse_block_or_bareword_cmp_block()?;
11428 let _ = self.eat(&Token::Comma);
11429 let list = if self.in_pipe_rhs()
11430 && matches!(
11431 self.peek(),
11432 Token::Semicolon
11433 | Token::RBrace
11434 | Token::RParen
11435 | Token::Eof
11436 | Token::PipeForward
11437 ) {
11438 self.pipe_placeholder_list(line)
11439 } else {
11440 self.parse_expression()?
11441 };
11442 Ok(Expr {
11443 kind: ExprKind::SortExpr {
11444 cmp: Some(SortComparator::Block(block)),
11445 list: Box::new(list),
11446 },
11447 line,
11448 })
11449 } else if matches!(self.peek(), Token::ScalarVar(_)) {
11450 self.suppress_indirect_paren_call =
11453 self.suppress_indirect_paren_call.saturating_add(1);
11454 let code = self.parse_assign_expr()?;
11455 self.suppress_indirect_paren_call =
11456 self.suppress_indirect_paren_call.saturating_sub(1);
11457 let _ = self.eat(&Token::Comma);
11458 let list = if self.in_pipe_rhs()
11459 && matches!(
11460 self.peek(),
11461 Token::Semicolon
11462 | Token::RBrace
11463 | Token::RParen
11464 | Token::Eof
11465 | Token::PipeForward
11466 ) {
11467 self.pipe_placeholder_list(line)
11468 } else if matches!(self.peek(), Token::LParen) {
11469 self.advance();
11470 let e = self.parse_expression()?;
11471 self.expect(&Token::RParen)?;
11472 e
11473 } else {
11474 self.parse_expression()?
11475 };
11476 Ok(Expr {
11477 kind: ExprKind::SortExpr {
11478 cmp: Some(SortComparator::Code(Box::new(code))),
11479 list: Box::new(list),
11480 },
11481 line,
11482 })
11483 } else if matches!(self.peek(), Token::Ident(ref name) if !Self::is_known_bareword(name) && !is_post_style_decl(name) && name != "varsync")
11484 {
11485 let block = self.parse_block_or_bareword_cmp_block()?;
11497 let _ = self.eat(&Token::Comma);
11498 let list = if self.in_pipe_rhs()
11499 && matches!(
11500 self.peek(),
11501 Token::Semicolon
11502 | Token::RBrace
11503 | Token::RParen
11504 | Token::Eof
11505 | Token::PipeForward
11506 ) {
11507 self.pipe_placeholder_list(line)
11508 } else {
11509 self.parse_expression()?
11510 };
11511 Ok(Expr {
11512 kind: ExprKind::SortExpr {
11513 cmp: Some(SortComparator::Block(block)),
11514 list: Box::new(list),
11515 },
11516 line,
11517 })
11518 } else {
11519 let list = if self.in_pipe_rhs()
11525 && (matches!(
11526 self.peek(),
11527 Token::Semicolon
11528 | Token::RBrace
11529 | Token::RParen
11530 | Token::Eof
11531 | Token::PipeForward
11532 ) || self.peek_line() > line)
11533 {
11534 self.pipe_placeholder_list(line)
11535 } else {
11536 self.parse_expression()?
11537 };
11538 Ok(Expr {
11539 kind: ExprKind::SortExpr {
11540 cmp: None,
11541 list: Box::new(list),
11542 },
11543 line,
11544 })
11545 }
11546 }
11547 "reduce" | "fold" | "inject" => {
11548 let (block, list) = self.parse_block_list()?;
11549 Ok(Expr {
11550 kind: ExprKind::ReduceExpr {
11551 block,
11552 list: Box::new(list),
11553 },
11554 line,
11555 })
11556 }
11557 "pmap" => {
11559 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
11560 Ok(Expr {
11561 kind: ExprKind::PMapExpr {
11562 block,
11563 list: Box::new(list),
11564 progress: progress.map(Box::new),
11565 flat_outputs: false,
11566 on_cluster: None,
11567 stream: false,
11568 },
11569 line,
11570 })
11571 }
11572 "pmap_on" => {
11573 let (cluster, block, list, progress) =
11574 self.parse_cluster_block_then_list_optional_progress()?;
11575 Ok(Expr {
11576 kind: ExprKind::PMapExpr {
11577 block,
11578 list: Box::new(list),
11579 progress: progress.map(Box::new),
11580 flat_outputs: false,
11581 on_cluster: Some(Box::new(cluster)),
11582 stream: false,
11583 },
11584 line,
11585 })
11586 }
11587 "pflat_map" => {
11588 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
11589 Ok(Expr {
11590 kind: ExprKind::PMapExpr {
11591 block,
11592 list: Box::new(list),
11593 progress: progress.map(Box::new),
11594 flat_outputs: true,
11595 on_cluster: None,
11596 stream: false,
11597 },
11598 line,
11599 })
11600 }
11601 "pflat_map_on" => {
11602 let (cluster, block, list, progress) =
11603 self.parse_cluster_block_then_list_optional_progress()?;
11604 Ok(Expr {
11605 kind: ExprKind::PMapExpr {
11606 block,
11607 list: Box::new(list),
11608 progress: progress.map(Box::new),
11609 flat_outputs: true,
11610 on_cluster: Some(Box::new(cluster)),
11611 stream: false,
11612 },
11613 line,
11614 })
11615 }
11616 "pmaps" => {
11617 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
11618 Ok(Expr {
11619 kind: ExprKind::PMapExpr {
11620 block,
11621 list: Box::new(list),
11622 progress: progress.map(Box::new),
11623 flat_outputs: false,
11624 on_cluster: None,
11625 stream: true,
11626 },
11627 line,
11628 })
11629 }
11630 "pflat_maps" => {
11631 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
11632 Ok(Expr {
11633 kind: ExprKind::PMapExpr {
11634 block,
11635 list: Box::new(list),
11636 progress: progress.map(Box::new),
11637 flat_outputs: true,
11638 on_cluster: None,
11639 stream: true,
11640 },
11641 line,
11642 })
11643 }
11644 "pgreps" => {
11645 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
11646 Ok(Expr {
11647 kind: ExprKind::PGrepExpr {
11648 block,
11649 list: Box::new(list),
11650 progress: progress.map(Box::new),
11651 stream: true,
11652 },
11653 line,
11654 })
11655 }
11656 "pmap_chunked" => {
11657 let chunk_size = self.parse_assign_expr()?;
11658 let block = self.parse_block_or_bareword_block()?;
11659 self.eat(&Token::Comma);
11660 let (list, progress) = self.parse_assign_expr_list_optional_progress()?;
11661 Ok(Expr {
11662 kind: ExprKind::PMapChunkedExpr {
11663 chunk_size: Box::new(chunk_size),
11664 block,
11665 list: Box::new(list),
11666 progress: progress.map(Box::new),
11667 },
11668 line,
11669 })
11670 }
11671 "pgrep" => {
11672 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
11673 Ok(Expr {
11674 kind: ExprKind::PGrepExpr {
11675 block,
11676 list: Box::new(list),
11677 progress: progress.map(Box::new),
11678 stream: false,
11679 },
11680 line,
11681 })
11682 }
11683 "pfor" | "pforeach" => {
11684 if matches!(self.peek(), Token::LParen) {
11685 self.expect(&Token::LParen)?;
11686 let list = self.parse_expression()?;
11687 self.expect(&Token::RParen)?;
11688 let block = self.parse_block()?;
11689 Ok(Expr {
11690 kind: ExprKind::PForExpr {
11691 block,
11692 list: Box::new(list),
11693 progress: None,
11694 },
11695 line,
11696 })
11697 } else {
11698 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
11699 Ok(Expr {
11700 kind: ExprKind::PForExpr {
11701 block,
11702 list: Box::new(list),
11703 progress: progress.map(Box::new),
11704 },
11705 line,
11706 })
11707 }
11708 }
11709 "par" => {
11715 let (block, list, _progress) = self.parse_block_then_list_optional_progress()?;
11716 Ok(Expr {
11717 kind: ExprKind::ParExpr {
11718 block,
11719 list: Box::new(list),
11720 },
11721 line,
11722 })
11723 }
11724 "par_lines" | "par_walk" => {
11725 let args = self.parse_builtin_args()?;
11726 if args.len() < 2 {
11727 return Err(
11728 self.syntax_err(format!("{} requires at least two arguments", name), line)
11729 );
11730 }
11731
11732 if name == "par_lines" {
11733 Ok(Expr {
11734 kind: ExprKind::ParLinesExpr {
11735 path: Box::new(args[0].clone()),
11736 callback: Box::new(args[1].clone()),
11737 progress: None,
11738 },
11739 line,
11740 })
11741 } else {
11742 Ok(Expr {
11743 kind: ExprKind::ParWalkExpr {
11744 path: Box::new(args[0].clone()),
11745 callback: Box::new(args[1].clone()),
11746 progress: None,
11747 },
11748 line,
11749 })
11750 }
11751 }
11752 "pwatch" | "watch" => {
11753 let args = self.parse_builtin_args()?;
11754 if args.len() < 2 {
11755 return Err(
11756 self.syntax_err(format!("{} requires at least two arguments", name), line)
11757 );
11758 }
11759 Ok(Expr {
11760 kind: ExprKind::PwatchExpr {
11761 path: Box::new(args[0].clone()),
11762 callback: Box::new(args[1].clone()),
11763 },
11764 line,
11765 })
11766 }
11767 "fan" => {
11768 let (count, block) = self.parse_fan_count_and_block(line)?;
11774 let progress = self.parse_fan_optional_progress("fan")?;
11775 Ok(Expr {
11776 kind: ExprKind::FanExpr {
11777 count,
11778 block,
11779 progress,
11780 capture: false,
11781 },
11782 line,
11783 })
11784 }
11785 "fan_cap" => {
11786 let (count, block) = self.parse_fan_count_and_block(line)?;
11787 let progress = self.parse_fan_optional_progress("fan_cap")?;
11788 Ok(Expr {
11789 kind: ExprKind::FanExpr {
11790 count,
11791 block,
11792 progress,
11793 capture: true,
11794 },
11795 line,
11796 })
11797 }
11798 "async" => {
11799 if !matches!(self.peek(), Token::LBrace) {
11800 return Err(self.syntax_err("async must be followed by { BLOCK }", line));
11801 }
11802 let block = self.parse_block()?;
11803 Ok(Expr {
11804 kind: ExprKind::AsyncBlock { body: block },
11805 line,
11806 })
11807 }
11808 "spawn" => {
11809 if !matches!(self.peek(), Token::LBrace) {
11810 return Err(self.syntax_err("spawn must be followed by { BLOCK }", line));
11811 }
11812 let block = self.parse_block()?;
11813 Ok(Expr {
11814 kind: ExprKind::SpawnBlock { body: block },
11815 line,
11816 })
11817 }
11818 "trace" => {
11819 if !matches!(self.peek(), Token::LBrace) {
11820 return Err(self.syntax_err("trace must be followed by { BLOCK }", line));
11821 }
11822 let block = self.parse_block()?;
11823 Ok(Expr {
11824 kind: ExprKind::Trace { body: block },
11825 line,
11826 })
11827 }
11828 "timer" => {
11829 let block = self.parse_block_or_bareword_block_no_args()?;
11830 Ok(Expr {
11831 kind: ExprKind::Timer { body: block },
11832 line,
11833 })
11834 }
11835 "bench" => {
11836 let block = self.parse_block_or_bareword_block_no_args()?;
11837 let times = Box::new(self.parse_expression()?);
11838 Ok(Expr {
11839 kind: ExprKind::Bench { body: block, times },
11840 line,
11841 })
11842 }
11843 "spinner" => {
11844 let (message, body) = if matches!(self.peek(), Token::LBrace) {
11846 let body = self.parse_block()?;
11847 (
11848 Box::new(Expr {
11849 kind: ExprKind::String("working".to_string()),
11850 line,
11851 }),
11852 body,
11853 )
11854 } else {
11855 let msg = self.parse_assign_expr()?;
11856 let body = self.parse_block()?;
11857 (Box::new(msg), body)
11858 };
11859 Ok(Expr {
11860 kind: ExprKind::Spinner { message, body },
11861 line,
11862 })
11863 }
11864 "thread" | "t" => {
11865 self.parse_thread_macro(line, false)
11875 }
11876 "retry" => {
11877 let body = if matches!(self.peek(), Token::LBrace) {
11880 self.parse_block()?
11881 } else {
11882 let bw_line = self.peek_line();
11883 let Token::Ident(ref name) = self.peek().clone() else {
11884 return Err(self
11885 .syntax_err("retry: expected block or bareword function name", line));
11886 };
11887 let name = name.clone();
11888 self.advance();
11889 vec![Statement::new(
11890 StmtKind::Expression(Expr {
11891 kind: ExprKind::FuncCall { name, args: vec![] },
11892 line: bw_line,
11893 }),
11894 bw_line,
11895 )]
11896 };
11897 self.eat(&Token::Comma);
11898 match self.peek() {
11899 Token::Ident(ref s) if s == "times" => {
11900 self.advance();
11901 }
11902 _ => {
11903 return Err(self.syntax_err("retry: expected `times =>` after block", line));
11904 }
11905 }
11906 self.expect(&Token::FatArrow)?;
11907 let times = Box::new(self.parse_assign_expr()?);
11908 let mut backoff = RetryBackoff::None;
11909 if self.eat(&Token::Comma) {
11910 match self.peek() {
11911 Token::Ident(ref s) if s == "backoff" => {
11912 self.advance();
11913 }
11914 _ => {
11915 return Err(
11916 self.syntax_err("retry: expected `backoff =>` after comma", line)
11917 );
11918 }
11919 }
11920 self.expect(&Token::FatArrow)?;
11921 let Token::Ident(mode) = self.peek().clone() else {
11922 return Err(self.syntax_err(
11923 "retry: expected backoff mode (none, linear, exponential)",
11924 line,
11925 ));
11926 };
11927 backoff = match mode.as_str() {
11928 "none" => RetryBackoff::None,
11929 "linear" => RetryBackoff::Linear,
11930 "exponential" => RetryBackoff::Exponential,
11931 _ => {
11932 return Err(
11933 self.syntax_err(format!("retry: invalid backoff `{mode}`"), line)
11934 );
11935 }
11936 };
11937 self.advance();
11938 }
11939 Ok(Expr {
11940 kind: ExprKind::RetryBlock {
11941 body,
11942 times,
11943 backoff,
11944 },
11945 line,
11946 })
11947 }
11948 "rate_limit" => {
11949 self.expect(&Token::LParen)?;
11950 let max = Box::new(self.parse_assign_expr()?);
11951 self.expect(&Token::Comma)?;
11952 let window = Box::new(self.parse_assign_expr()?);
11953 self.expect(&Token::RParen)?;
11954 let body = self.parse_block_or_bareword_block_no_args()?;
11955 let slot = self.alloc_rate_limit_slot();
11956 Ok(Expr {
11957 kind: ExprKind::RateLimitBlock {
11958 slot,
11959 max,
11960 window,
11961 body,
11962 },
11963 line,
11964 })
11965 }
11966 "every" => {
11967 let has_paren = self.eat(&Token::LParen);
11970 let interval = Box::new(self.parse_assign_expr()?);
11971 if has_paren {
11972 self.expect(&Token::RParen)?;
11973 }
11974 let body = if matches!(self.peek(), Token::LBrace) {
11975 self.parse_block()?
11976 } else {
11977 let bline = self.peek_line();
11978 let expr = self.parse_assign_expr()?;
11979 vec![Statement::new(StmtKind::Expression(expr), bline)]
11980 };
11981 Ok(Expr {
11982 kind: ExprKind::EveryBlock { interval, body },
11983 line,
11984 })
11985 }
11986 "gen" => {
11987 if !matches!(self.peek(), Token::LBrace) {
11988 return Err(self.syntax_err("gen must be followed by { BLOCK }", line));
11989 }
11990 let body = self.parse_block()?;
11991 Ok(Expr {
11992 kind: ExprKind::GenBlock { body },
11993 line,
11994 })
11995 }
11996 "yield" => {
11997 let e = self.parse_assign_expr()?;
11998 Ok(Expr {
11999 kind: ExprKind::Yield(Box::new(e)),
12000 line,
12001 })
12002 }
12003 "await" => {
12004 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12005 return Ok(e);
12006 }
12007 let a = self.parse_one_arg_or_default()?;
12010 Ok(Expr {
12011 kind: ExprKind::Await(Box::new(a)),
12012 line,
12013 })
12014 }
12015 "slurp" | "cat" | "c" => {
12016 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12017 return Ok(e);
12018 }
12019 let a = self.parse_one_arg_or_default()?;
12020 Ok(Expr {
12021 kind: ExprKind::Slurp(Box::new(a)),
12022 line,
12023 })
12024 }
12025 "swallow" | "swa" => {
12026 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12027 return Ok(e);
12028 }
12029 let a = self.parse_one_arg_or_default()?;
12030 Ok(Expr {
12031 kind: ExprKind::Swallow(Box::new(a)),
12032 line,
12033 })
12034 }
12035 "ingest" | "ing" => {
12036 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12037 return Ok(e);
12038 }
12039 let a = self.parse_one_arg_or_default()?;
12040 Ok(Expr {
12041 kind: ExprKind::Ingest(Box::new(a)),
12042 line,
12043 })
12044 }
12045 "burp" => {
12046 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12047 return Ok(e);
12048 }
12049 let a = self.parse_one_arg_or_default()?;
12050 Ok(Expr {
12051 kind: ExprKind::Burp(Box::new(a)),
12052 line,
12053 })
12054 }
12055 "god" => {
12056 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12057 return Ok(e);
12058 }
12059 let a = self.parse_one_arg_or_default()?;
12060 Ok(Expr {
12061 kind: ExprKind::God(Box::new(a)),
12062 line,
12063 })
12064 }
12065 "capture" => {
12066 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12067 return Ok(e);
12068 }
12069 let a = self.parse_one_arg()?;
12070 Ok(Expr {
12071 kind: ExprKind::Capture(Box::new(a)),
12072 line,
12073 })
12074 }
12075 "fetch_url" => {
12076 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12077 return Ok(e);
12078 }
12079 let a = self.parse_one_arg()?;
12080 Ok(Expr {
12081 kind: ExprKind::FetchUrl(Box::new(a)),
12082 line,
12083 })
12084 }
12085 "pchannel" => {
12086 let capacity = if self.eat(&Token::LParen) {
12087 if matches!(self.peek(), Token::RParen) {
12088 self.advance();
12089 None
12090 } else {
12091 let e = self.parse_expression()?;
12092 self.expect(&Token::RParen)?;
12093 Some(Box::new(e))
12094 }
12095 } else {
12096 None
12097 };
12098 Ok(Expr {
12099 kind: ExprKind::Pchannel { capacity },
12100 line,
12101 })
12102 }
12103 "psort" => {
12104 if matches!(self.peek(), Token::LBrace)
12105 || matches!(self.peek(), Token::ScalarVar(ref v) if v == "a" || v == "b")
12106 || matches!(self.peek(), Token::Ident(ref name) if !Self::is_known_bareword(name))
12107 {
12108 let block = self.parse_block_or_bareword_cmp_block()?;
12109 let block_end_line = self.prev_line();
12117 self.eat(&Token::Comma);
12118 let use_placeholder = self.in_pipe_rhs()
12119 && (matches!(
12120 self.peek(),
12121 Token::Semicolon
12122 | Token::RBrace
12123 | Token::RParen
12124 | Token::Eof
12125 | Token::PipeForward
12126 ) || self.peek_line() > block_end_line);
12127 let (list, progress) = if use_placeholder {
12128 (self.pipe_placeholder_list(line), None)
12129 } else {
12130 self.parse_assign_expr_list_optional_progress()?
12131 };
12132 Ok(Expr {
12133 kind: ExprKind::PSortExpr {
12134 cmp: Some(block),
12135 list: Box::new(list),
12136 progress: progress.map(Box::new),
12137 },
12138 line,
12139 })
12140 } else {
12141 let (list, progress) = self.parse_assign_expr_list_optional_progress()?;
12142 Ok(Expr {
12143 kind: ExprKind::PSortExpr {
12144 cmp: None,
12145 list: Box::new(list),
12146 progress: progress.map(Box::new),
12147 },
12148 line,
12149 })
12150 }
12151 }
12152 "preduce" => {
12153 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
12154 Ok(Expr {
12155 kind: ExprKind::PReduceExpr {
12156 block,
12157 list: Box::new(list),
12158 progress: progress.map(Box::new),
12159 },
12160 line,
12161 })
12162 }
12163 "preduce_init" => {
12164 let (init, block, list, progress) =
12165 self.parse_init_block_then_list_optional_progress()?;
12166 Ok(Expr {
12167 kind: ExprKind::PReduceInitExpr {
12168 init: Box::new(init),
12169 block,
12170 list: Box::new(list),
12171 progress: progress.map(Box::new),
12172 },
12173 line,
12174 })
12175 }
12176 "pmap_reduce" => {
12177 let map_block = self.parse_block_or_bareword_block()?;
12178 let reduce_block = if matches!(self.peek(), Token::LBrace) {
12181 self.parse_block()?
12182 } else {
12183 self.expect(&Token::Comma)?;
12185 self.parse_block_or_bareword_cmp_block()?
12186 };
12187 self.eat(&Token::Comma);
12188 let line = self.peek_line();
12189 if let Token::Ident(ref kw) = self.peek().clone() {
12190 if kw == "progress" && matches!(self.peek_at(1), Token::FatArrow) {
12191 self.advance();
12192 self.expect(&Token::FatArrow)?;
12193 let prog = self.parse_assign_expr()?;
12194 return Ok(Expr {
12195 kind: ExprKind::PMapReduceExpr {
12196 map_block,
12197 reduce_block,
12198 list: Box::new(Expr {
12199 kind: ExprKind::List(vec![]),
12200 line,
12201 }),
12202 progress: Some(Box::new(prog)),
12203 },
12204 line,
12205 });
12206 }
12207 }
12208 if self.pipe_supplies_slurped_list_operand()
12216 || matches!(
12217 self.peek(),
12218 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof
12219 )
12220 {
12221 return Ok(Expr {
12222 kind: ExprKind::PMapReduceExpr {
12223 map_block,
12224 reduce_block,
12225 list: Box::new(Expr {
12226 kind: ExprKind::List(vec![]),
12227 line,
12228 }),
12229 progress: None,
12230 },
12231 line,
12232 });
12233 }
12234 let mut parts = vec![self.parse_assign_expr()?];
12235 loop {
12236 if !self.eat(&Token::Comma) && !self.eat(&Token::FatArrow) {
12237 break;
12238 }
12239 if matches!(
12240 self.peek(),
12241 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof
12242 ) {
12243 break;
12244 }
12245 if let Token::Ident(ref kw) = self.peek().clone() {
12246 if kw == "progress" && matches!(self.peek_at(1), Token::FatArrow) {
12247 self.advance();
12248 self.expect(&Token::FatArrow)?;
12249 let prog = self.parse_assign_expr()?;
12250 return Ok(Expr {
12251 kind: ExprKind::PMapReduceExpr {
12252 map_block,
12253 reduce_block,
12254 list: Box::new(merge_expr_list(parts)),
12255 progress: Some(Box::new(prog)),
12256 },
12257 line,
12258 });
12259 }
12260 }
12261 parts.push(self.parse_assign_expr()?);
12262 }
12263 Ok(Expr {
12264 kind: ExprKind::PMapReduceExpr {
12265 map_block,
12266 reduce_block,
12267 list: Box::new(merge_expr_list(parts)),
12268 progress: None,
12269 },
12270 line,
12271 })
12272 }
12273 "puniq" => {
12274 if self.pipe_supplies_slurped_list_operand() {
12275 return Ok(Expr {
12276 kind: ExprKind::FuncCall {
12277 name: "puniq".to_string(),
12278 args: vec![],
12279 },
12280 line,
12281 });
12282 }
12283 let (list, progress) = self.parse_assign_expr_list_optional_progress()?;
12284 let mut args = vec![list];
12285 if let Some(p) = progress {
12286 args.push(p);
12287 }
12288 Ok(Expr {
12289 kind: ExprKind::FuncCall {
12290 name: "puniq".to_string(),
12291 args,
12292 },
12293 line,
12294 })
12295 }
12296 "pfirst" => {
12297 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
12298 let cr = Expr {
12299 kind: ExprKind::CodeRef {
12300 params: vec![],
12301 body: block,
12302 },
12303 line,
12304 };
12305 let mut args = vec![cr, list];
12306 if let Some(p) = progress {
12307 args.push(p);
12308 }
12309 Ok(Expr {
12310 kind: ExprKind::FuncCall {
12311 name: "pfirst".to_string(),
12312 args,
12313 },
12314 line,
12315 })
12316 }
12317 "pany" => {
12318 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
12319 let cr = Expr {
12320 kind: ExprKind::CodeRef {
12321 params: vec![],
12322 body: block,
12323 },
12324 line,
12325 };
12326 let mut args = vec![cr, list];
12327 if let Some(p) = progress {
12328 args.push(p);
12329 }
12330 Ok(Expr {
12331 kind: ExprKind::FuncCall {
12332 name: "pany".to_string(),
12333 args,
12334 },
12335 line,
12336 })
12337 }
12338 "uniq" | "distinct" => {
12339 if self.pipe_supplies_slurped_list_operand() {
12340 return Ok(Expr {
12341 kind: ExprKind::FuncCall {
12342 name: name.clone(),
12343 args: vec![],
12344 },
12345 line,
12346 });
12347 }
12348 let (list, progress) = self.parse_assign_expr_list_optional_progress()?;
12349 if progress.is_some() {
12350 return Err(self.syntax_err(
12351 "`progress =>` is not supported for uniq (use puniq for parallel + progress)",
12352 line,
12353 ));
12354 }
12355 Ok(Expr {
12356 kind: ExprKind::FuncCall {
12357 name: name.clone(),
12358 args: vec![list],
12359 },
12360 line,
12361 })
12362 }
12363 "flatten" => {
12364 if self.pipe_supplies_slurped_list_operand() {
12365 return Ok(Expr {
12366 kind: ExprKind::FuncCall {
12367 name: "flatten".to_string(),
12368 args: vec![],
12369 },
12370 line,
12371 });
12372 }
12373 let (list, progress) = self.parse_assign_expr_list_optional_progress()?;
12374 if progress.is_some() {
12375 return Err(self.syntax_err("`progress =>` is not supported for flatten", line));
12376 }
12377 Ok(Expr {
12378 kind: ExprKind::FuncCall {
12379 name: "flatten".to_string(),
12380 args: vec![list],
12381 },
12382 line,
12383 })
12384 }
12385 "set" => {
12386 if self.pipe_supplies_slurped_list_operand() {
12387 return Ok(Expr {
12388 kind: ExprKind::FuncCall {
12389 name: "set".to_string(),
12390 args: vec![],
12391 },
12392 line,
12393 });
12394 }
12395 let (list, progress) = self.parse_assign_expr_list_optional_progress()?;
12396 if progress.is_some() {
12397 return Err(self.syntax_err("`progress =>` is not supported for set", line));
12398 }
12399 Ok(Expr {
12400 kind: ExprKind::FuncCall {
12401 name: "set".to_string(),
12402 args: vec![list],
12403 },
12404 line,
12405 })
12406 }
12407 "size" => {
12411 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12412 return Ok(e);
12413 }
12414 if self.pipe_supplies_slurped_list_operand() {
12415 return Ok(Expr {
12416 kind: ExprKind::FuncCall {
12417 name: "size".to_string(),
12418 args: vec![],
12419 },
12420 line,
12421 });
12422 }
12423 let a = self.parse_one_arg_or_default()?;
12424 Ok(Expr {
12425 kind: ExprKind::FuncCall {
12426 name: "size".to_string(),
12427 args: vec![a],
12428 },
12429 line,
12430 })
12431 }
12432 "list_count" | "list_size" | "count" | "len" | "cnt" => {
12433 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12434 return Ok(e);
12435 }
12436 if self.pipe_supplies_slurped_list_operand() {
12437 return Ok(Expr {
12438 kind: ExprKind::FuncCall {
12439 name: name.clone(),
12440 args: vec![],
12441 },
12442 line,
12443 });
12444 }
12445 let args = if matches!(self.peek(), Token::LParen) {
12459 self.advance();
12460 if matches!(self.peek(), Token::RParen) {
12461 self.advance();
12462 Vec::new()
12463 } else {
12464 let inner = self.parse_expression()?;
12465 self.expect(&Token::RParen)?;
12466 vec![inner]
12467 }
12468 } else if self.peek_is_named_unary_terminator() {
12469 Vec::new()
12470 } else {
12471 let (list, progress) = self.parse_assign_expr_list_optional_progress()?;
12472 if progress.is_some() {
12473 return Err(self.syntax_err(
12474 "`progress =>` is not supported for list_count / list_size / count / cnt",
12475 line,
12476 ));
12477 }
12478 vec![list]
12479 };
12480 Ok(Expr {
12481 kind: ExprKind::FuncCall {
12482 name: name.clone(),
12483 args,
12484 },
12485 line,
12486 })
12487 }
12488 "shuffle" | "shuffled" => {
12489 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12490 return Ok(e);
12491 }
12492 if self.pipe_supplies_slurped_list_operand() {
12493 return Ok(Expr {
12494 kind: ExprKind::FuncCall {
12495 name: "shuffle".to_string(),
12496 args: vec![],
12497 },
12498 line,
12499 });
12500 }
12501 let (list, progress) = self.parse_assign_expr_list_optional_progress()?;
12502 if progress.is_some() {
12503 return Err(self.syntax_err("`progress =>` is not supported for shuffle", line));
12504 }
12505 Ok(Expr {
12506 kind: ExprKind::FuncCall {
12507 name: "shuffle".to_string(),
12508 args: vec![list],
12509 },
12510 line,
12511 })
12512 }
12513 "chunked" => {
12514 let mut parts = Vec::new();
12515 if self.eat(&Token::LParen) {
12516 if !matches!(self.peek(), Token::RParen) {
12517 parts.push(self.parse_assign_expr()?);
12518 while self.eat(&Token::Comma) {
12519 if matches!(self.peek(), Token::RParen) {
12520 break;
12521 }
12522 parts.push(self.parse_assign_expr()?);
12523 }
12524 }
12525 self.expect(&Token::RParen)?;
12526 } else {
12527 parts.push(self.parse_assign_expr_stop_at_pipe()?);
12531 loop {
12532 if !self.eat(&Token::Comma) && !self.eat(&Token::FatArrow) {
12533 break;
12534 }
12535 if matches!(
12536 self.peek(),
12537 Token::Semicolon
12538 | Token::RBrace
12539 | Token::RParen
12540 | Token::Eof
12541 | Token::PipeForward
12542 ) {
12543 break;
12544 }
12545 if self.peek_is_postfix_stmt_modifier_keyword() {
12546 break;
12547 }
12548 parts.push(self.parse_assign_expr_stop_at_pipe()?);
12549 }
12550 }
12551 if parts.len() == 1 {
12552 let n = parts.pop().unwrap();
12553 return Ok(Expr {
12554 kind: ExprKind::FuncCall {
12555 name: "chunked".to_string(),
12556 args: vec![n],
12557 },
12558 line,
12559 });
12560 }
12561 if parts.is_empty() {
12562 return Ok(Expr {
12563 kind: ExprKind::FuncCall {
12564 name: "chunked".to_string(),
12565 args: parts,
12566 },
12567 line,
12568 });
12569 }
12570 if parts.len() == 2 {
12571 let n = parts.pop().unwrap();
12572 let list = parts.pop().unwrap();
12573 return Ok(Expr {
12574 kind: ExprKind::FuncCall {
12575 name: "chunked".to_string(),
12576 args: vec![list, n],
12577 },
12578 line,
12579 });
12580 }
12581 Err(self.syntax_err(
12582 "chunked: use LIST |> chunked(N) or chunked((1,2,3), 2)",
12583 line,
12584 ))
12585 }
12586 "windowed" => {
12587 let mut parts = Vec::new();
12588 if self.eat(&Token::LParen) {
12589 if !matches!(self.peek(), Token::RParen) {
12590 parts.push(self.parse_assign_expr()?);
12591 while self.eat(&Token::Comma) {
12592 if matches!(self.peek(), Token::RParen) {
12593 break;
12594 }
12595 parts.push(self.parse_assign_expr()?);
12596 }
12597 }
12598 self.expect(&Token::RParen)?;
12599 } else {
12600 parts.push(self.parse_assign_expr_stop_at_pipe()?);
12603 loop {
12604 if !self.eat(&Token::Comma) && !self.eat(&Token::FatArrow) {
12605 break;
12606 }
12607 if matches!(
12608 self.peek(),
12609 Token::Semicolon
12610 | Token::RBrace
12611 | Token::RParen
12612 | Token::Eof
12613 | Token::PipeForward
12614 ) {
12615 break;
12616 }
12617 if self.peek_is_postfix_stmt_modifier_keyword() {
12618 break;
12619 }
12620 parts.push(self.parse_assign_expr_stop_at_pipe()?);
12621 }
12622 }
12623 if parts.len() == 1 {
12624 let n = parts.pop().unwrap();
12625 return Ok(Expr {
12626 kind: ExprKind::FuncCall {
12627 name: "windowed".to_string(),
12628 args: vec![n],
12629 },
12630 line,
12631 });
12632 }
12633 if parts.is_empty() {
12634 return Ok(Expr {
12635 kind: ExprKind::FuncCall {
12636 name: "windowed".to_string(),
12637 args: parts,
12638 },
12639 line,
12640 });
12641 }
12642 if parts.len() == 2 {
12643 let n = parts.pop().unwrap();
12644 let list = parts.pop().unwrap();
12645 return Ok(Expr {
12646 kind: ExprKind::FuncCall {
12647 name: "windowed".to_string(),
12648 args: vec![list, n],
12649 },
12650 line,
12651 });
12652 }
12653 Err(self.syntax_err(
12654 "windowed: use LIST |> windowed(N) or windowed((1,2,3), 2)",
12655 line,
12656 ))
12657 }
12658 "any" | "all" | "none" => {
12659 if matches!(self.peek(), Token::LParen) {
12661 self.advance();
12662 let args = self.parse_arg_list()?;
12663 self.expect(&Token::RParen)?;
12664 return Ok(Expr {
12665 kind: ExprKind::FuncCall {
12666 name: name.clone(),
12667 args,
12668 },
12669 line,
12670 });
12671 }
12672 if let Some(args) = self.try_parse_coderef_listop_args(line)? {
12676 return Ok(Expr {
12677 kind: ExprKind::FuncCall {
12678 name: name.clone(),
12679 args,
12680 },
12681 line,
12682 });
12683 }
12684 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
12686 if progress.is_some() {
12687 return Err(self.syntax_err(
12688 "`progress =>` is not supported for any/all/none (use pany for parallel + progress)",
12689 line,
12690 ));
12691 }
12692 let cr = Expr {
12693 kind: ExprKind::CodeRef {
12694 params: vec![],
12695 body: block,
12696 },
12697 line,
12698 };
12699 Ok(Expr {
12700 kind: ExprKind::FuncCall {
12701 name: name.clone(),
12702 args: vec![cr, list],
12703 },
12704 line,
12705 })
12706 }
12707 "first" | "detect" | "find" | "find_index" | "firstidx" | "first_index" => {
12709 let canonical =
12710 if matches!(name.as_str(), "find_index" | "firstidx" | "first_index") {
12711 "find_index"
12712 } else {
12713 "first"
12714 };
12715 if matches!(self.peek(), Token::LParen) {
12717 self.advance();
12718 let args = self.parse_arg_list()?;
12719 self.expect(&Token::RParen)?;
12720 return Ok(Expr {
12721 kind: ExprKind::FuncCall {
12722 name: canonical.to_string(),
12723 args,
12724 },
12725 line,
12726 });
12727 }
12728 if let Some(args) = self.try_parse_coderef_listop_args(line)? {
12730 return Ok(Expr {
12731 kind: ExprKind::FuncCall {
12732 name: canonical.to_string(),
12733 args,
12734 },
12735 line,
12736 });
12737 }
12738 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
12740 if progress.is_some() {
12741 return Err(self.syntax_err(
12742 "`progress =>` is not supported for first/detect/find/find_index (use pfirst for parallel + progress)",
12743 line,
12744 ));
12745 }
12746 let cr = Expr {
12747 kind: ExprKind::CodeRef {
12748 params: vec![],
12749 body: block,
12750 },
12751 line,
12752 };
12753 Ok(Expr {
12754 kind: ExprKind::FuncCall {
12755 name: canonical.to_string(),
12756 args: vec![cr, list],
12757 },
12758 line,
12759 })
12760 }
12761 "take_while" | "drop_while" | "skip_while" | "reject" | "grepv" | "tap" | "peek"
12762 | "partition" | "min_by" | "max_by" | "zip_with" | "count_by" => {
12763 if let Some(args) = self.try_parse_coderef_listop_args(line)? {
12765 return Ok(Expr {
12766 kind: ExprKind::FuncCall {
12767 name: name.to_string(),
12768 args,
12769 },
12770 line,
12771 });
12772 }
12773 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
12774 if progress.is_some() {
12775 return Err(
12776 self.syntax_err(format!("`progress =>` is not supported for {name}"), line)
12777 );
12778 }
12779 let cr = Expr {
12780 kind: ExprKind::CodeRef {
12781 params: vec![],
12782 body: block,
12783 },
12784 line,
12785 };
12786 Ok(Expr {
12787 kind: ExprKind::FuncCall {
12788 name: name.to_string(),
12789 args: vec![cr, list],
12790 },
12791 line,
12792 })
12793 }
12794 "group_by" | "chunk_by" => {
12795 if matches!(self.peek(), Token::LBrace) {
12796 let (block, list) = self.parse_block_list()?;
12797 let cr = Expr {
12798 kind: ExprKind::CodeRef {
12799 params: vec![],
12800 body: block,
12801 },
12802 line,
12803 };
12804 Ok(Expr {
12805 kind: ExprKind::FuncCall {
12806 name: name.to_string(),
12807 args: vec![cr, list],
12808 },
12809 line,
12810 })
12811 } else {
12812 let key_expr = self.parse_assign_expr()?;
12813 self.expect(&Token::Comma)?;
12814 let list_parts = self.parse_list_until_terminator()?;
12815 let list_expr = if list_parts.len() == 1 {
12816 list_parts.into_iter().next().unwrap()
12817 } else {
12818 Expr {
12819 kind: ExprKind::List(list_parts),
12820 line,
12821 }
12822 };
12823 Ok(Expr {
12824 kind: ExprKind::FuncCall {
12825 name: name.to_string(),
12826 args: vec![key_expr, list_expr],
12827 },
12828 line,
12829 })
12830 }
12831 }
12832 "with_index" => {
12833 if self.pipe_supplies_slurped_list_operand() {
12834 return Ok(Expr {
12835 kind: ExprKind::FuncCall {
12836 name: "with_index".to_string(),
12837 args: vec![],
12838 },
12839 line,
12840 });
12841 }
12842 let (list, progress) = self.parse_assign_expr_list_optional_progress()?;
12843 if progress.is_some() {
12844 return Err(
12845 self.syntax_err("`progress =>` is not supported for with_index", line)
12846 );
12847 }
12848 Ok(Expr {
12849 kind: ExprKind::FuncCall {
12850 name: "with_index".to_string(),
12851 args: vec![list],
12852 },
12853 line,
12854 })
12855 }
12856 "pcache" => {
12857 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
12858 Ok(Expr {
12859 kind: ExprKind::PcacheExpr {
12860 block,
12861 list: Box::new(list),
12862 progress: progress.map(Box::new),
12863 },
12864 line,
12865 })
12866 }
12867 "pselect" => {
12868 let paren = self.eat(&Token::LParen);
12869 let (receivers, timeout) = self.parse_comma_expr_list_with_timeout_tail(paren)?;
12870 if paren {
12871 self.expect(&Token::RParen)?;
12872 }
12873 if receivers.is_empty() {
12874 return Err(self.syntax_err("pselect needs at least one receiver", line));
12875 }
12876 Ok(Expr {
12877 kind: ExprKind::PselectExpr {
12878 receivers,
12879 timeout: timeout.map(Box::new),
12880 },
12881 line,
12882 })
12883 }
12884 "open" => {
12885 let paren = matches!(self.peek(), Token::LParen);
12886 if paren {
12887 self.advance();
12888 }
12889 if matches!(self.peek(), Token::Ident(ref s) if is_lexical_decl(s)) {
12890 self.advance();
12891 let name = self.parse_scalar_var_name()?;
12892 self.expect(&Token::Comma)?;
12893 let mode = self.parse_assign_expr()?;
12894 let file = if self.eat(&Token::Comma) {
12895 Some(self.parse_assign_expr()?)
12896 } else {
12897 None
12898 };
12899 if paren {
12900 self.expect(&Token::RParen)?;
12901 }
12902 Ok(Expr {
12903 kind: ExprKind::Open {
12904 handle: Box::new(Expr {
12905 kind: ExprKind::OpenMyHandle { name },
12906 line,
12907 }),
12908 mode: Box::new(mode),
12909 file: file.map(Box::new),
12910 },
12911 line,
12912 })
12913 } else {
12914 let handle_lit = self.take_bareword_filehandle();
12922 if handle_lit.is_some() {
12923 self.expect(&Token::Comma)?;
12926 }
12927 let args = if paren {
12928 self.parse_arg_list()?
12929 } else {
12930 self.parse_list_until_terminator()?
12931 };
12932 if paren {
12933 self.expect(&Token::RParen)?;
12934 }
12935 let total = handle_lit.is_some() as usize + args.len();
12936 if total < 2 {
12937 return Err(self.syntax_err("open requires at least 2 arguments", line));
12938 }
12939 let (handle_expr, mode_expr, file_expr) = match handle_lit {
12940 Some(name) => {
12941 let h = Expr {
12942 kind: ExprKind::String(name),
12943 line,
12944 };
12945 (h, args[0].clone(), args.get(1).cloned())
12946 }
12947 None => (args[0].clone(), args[1].clone(), args.get(2).cloned()),
12948 };
12949 Ok(Expr {
12950 kind: ExprKind::Open {
12951 handle: Box::new(handle_expr),
12952 mode: Box::new(mode_expr),
12953 file: file_expr.map(Box::new),
12954 },
12955 line,
12956 })
12957 }
12958 }
12959 "close" => {
12960 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12961 return Ok(e);
12962 }
12963 let a = self
12965 .take_bareword_filehandle_arg(line)
12966 .map(Ok)
12967 .unwrap_or_else(|| self.parse_one_arg_or_default())?;
12968 Ok(Expr {
12969 kind: ExprKind::Close(Box::new(a)),
12970 line,
12971 })
12972 }
12973 "opendir" => {
12974 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12975 return Ok(e);
12976 }
12977 let paren = matches!(self.peek(), Token::LParen);
12978 if paren {
12979 self.advance();
12980 }
12981 if matches!(self.peek(), Token::Ident(ref s) if is_lexical_decl(s)) {
12982 self.advance();
12983 let hname = self.parse_scalar_var_name()?;
12984 self.expect(&Token::Comma)?;
12985 let path = self.parse_assign_expr()?;
12986 if paren {
12987 self.expect(&Token::RParen)?;
12988 }
12989 return Ok(Expr {
12990 kind: ExprKind::Opendir {
12991 handle: Box::new(Expr {
12992 kind: ExprKind::OpendirMyHandle { name: hname },
12993 line,
12994 }),
12995 path: Box::new(path),
12996 },
12997 line,
12998 });
12999 }
13000 let args = if paren {
13001 self.parse_arg_list()?
13002 } else {
13003 self.parse_list_until_terminator()?
13004 };
13005 if paren {
13006 self.expect(&Token::RParen)?;
13007 }
13008 if args.len() != 2 {
13009 return Err(self.syntax_err("opendir requires two arguments", line));
13010 }
13011 Ok(Expr {
13012 kind: ExprKind::Opendir {
13013 handle: Box::new(args[0].clone()),
13014 path: Box::new(args[1].clone()),
13015 },
13016 line,
13017 })
13018 }
13019 "readdir" => {
13020 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
13021 return Ok(e);
13022 }
13023 let a = self.parse_one_arg()?;
13024 Ok(Expr {
13025 kind: ExprKind::Readdir(Box::new(a)),
13026 line,
13027 })
13028 }
13029 "closedir" => {
13030 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
13031 return Ok(e);
13032 }
13033 let a = self.parse_one_arg()?;
13034 Ok(Expr {
13035 kind: ExprKind::Closedir(Box::new(a)),
13036 line,
13037 })
13038 }
13039 "rewinddir" => {
13040 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
13041 return Ok(e);
13042 }
13043 let a = self.parse_one_arg()?;
13044 Ok(Expr {
13045 kind: ExprKind::Rewinddir(Box::new(a)),
13046 line,
13047 })
13048 }
13049 "telldir" => {
13050 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
13051 return Ok(e);
13052 }
13053 let a = self.parse_one_arg()?;
13054 Ok(Expr {
13055 kind: ExprKind::Telldir(Box::new(a)),
13056 line,
13057 })
13058 }
13059 "seekdir" => {
13060 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
13061 return Ok(e);
13062 }
13063 let args = self.parse_builtin_args()?;
13064 if args.len() != 2 {
13065 return Err(self.syntax_err("seekdir requires two arguments", line));
13066 }
13067 Ok(Expr {
13068 kind: ExprKind::Seekdir {
13069 handle: Box::new(args[0].clone()),
13070 position: Box::new(args[1].clone()),
13071 },
13072 line,
13073 })
13074 }
13075 "eof" => {
13076 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
13077 return Ok(e);
13078 }
13079 if let Some(a) = self.take_bareword_filehandle_arg(line) {
13083 return Ok(Expr {
13084 kind: ExprKind::Eof(Some(Box::new(a))),
13085 line,
13086 });
13087 }
13088 if matches!(self.peek(), Token::LParen) {
13089 self.advance();
13090 if matches!(self.peek(), Token::RParen) {
13091 self.advance();
13092 Ok(Expr {
13093 kind: ExprKind::Eof(None),
13094 line,
13095 })
13096 } else {
13097 let a = self
13099 .take_bareword_filehandle_arg(line)
13100 .map(Ok)
13101 .unwrap_or_else(|| self.parse_expression())?;
13102 self.expect(&Token::RParen)?;
13103 Ok(Expr {
13104 kind: ExprKind::Eof(Some(Box::new(a))),
13105 line,
13106 })
13107 }
13108 } else {
13109 Ok(Expr {
13110 kind: ExprKind::Eof(None),
13111 line,
13112 })
13113 }
13114 }
13115 "system" => {
13116 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
13117 return Ok(e);
13118 }
13119 let args = self.parse_builtin_args()?;
13120 Ok(Expr {
13121 kind: ExprKind::System(args),
13122 line,
13123 })
13124 }
13125 "exec" => {
13126 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
13127 return Ok(e);
13128 }
13129 let args = self.parse_builtin_args()?;
13130 Ok(Expr {
13131 kind: ExprKind::Exec(args),
13132 line,
13133 })
13134 }
13135 "eval" => {
13136 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
13137 return Ok(e);
13138 }
13139 let a = if matches!(self.peek(), Token::LBrace) {
13140 let block = self.parse_block()?;
13141 Expr {
13142 kind: ExprKind::CodeRef {
13143 params: vec![],
13144 body: block,
13145 },
13146 line,
13147 }
13148 } else {
13149 self.parse_one_arg_or_default()?
13150 };
13151 Ok(Expr {
13152 kind: ExprKind::Eval(Box::new(a)),
13153 line,
13154 })
13155 }
13156 "do" => {
13157 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
13158 return Ok(e);
13159 }
13160 let a = self.parse_one_arg()?;
13161 Ok(Expr {
13162 kind: ExprKind::Do(Box::new(a)),
13163 line,
13164 })
13165 }
13166 "require" => {
13167 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
13168 return Ok(e);
13169 }
13170 let a = self.parse_one_arg()?;
13171 Ok(Expr {
13172 kind: ExprKind::Require(Box::new(a)),
13173 line,
13174 })
13175 }
13176 "exit" => {
13177 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
13178 return Ok(e);
13179 }
13180 if matches!(
13181 self.peek(),
13182 Token::Semicolon | Token::RBrace | Token::Eof | Token::PipeForward
13183 ) {
13184 Ok(Expr {
13185 kind: ExprKind::Exit(None),
13186 line,
13187 })
13188 } else {
13189 let a = self.parse_one_arg()?;
13190 Ok(Expr {
13191 kind: ExprKind::Exit(Some(Box::new(a))),
13192 line,
13193 })
13194 }
13195 }
13196 "chdir" => {
13197 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
13198 return Ok(e);
13199 }
13200 let a = self.parse_one_arg_or_default()?;
13201 Ok(Expr {
13202 kind: ExprKind::Chdir(Box::new(a)),
13203 line,
13204 })
13205 }
13206 "mkdir" => {
13207 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
13208 return Ok(e);
13209 }
13210 let args = self.parse_builtin_args()?;
13211 Ok(Expr {
13212 kind: ExprKind::Mkdir {
13213 path: Box::new(args[0].clone()),
13214 mode: args.get(1).cloned().map(Box::new),
13215 },
13216 line,
13217 })
13218 }
13219 "unlink" | "rm" => {
13220 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
13221 return Ok(e);
13222 }
13223 let args = self.parse_builtin_args()?;
13224 Ok(Expr {
13225 kind: ExprKind::Unlink(args),
13226 line,
13227 })
13228 }
13229 "rename" => {
13230 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
13231 return Ok(e);
13232 }
13233 let args = self.parse_builtin_args()?;
13234 if args.len() != 2 {
13235 return Err(self.syntax_err("rename requires two arguments", line));
13236 }
13237 Ok(Expr {
13238 kind: ExprKind::Rename {
13239 old: Box::new(args[0].clone()),
13240 new: Box::new(args[1].clone()),
13241 },
13242 line,
13243 })
13244 }
13245 "chmod" => {
13246 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
13247 return Ok(e);
13248 }
13249 let args = self.parse_builtin_args()?;
13250 if args.len() < 2 {
13251 return Err(self.syntax_err("chmod requires mode and at least one file", line));
13252 }
13253 Ok(Expr {
13254 kind: ExprKind::Chmod(args),
13255 line,
13256 })
13257 }
13258 "chown" => {
13259 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
13260 return Ok(e);
13261 }
13262 let args = self.parse_builtin_args()?;
13263 if args.len() < 3 {
13264 return Err(
13265 self.syntax_err("chown requires uid, gid, and at least one file", line)
13266 );
13267 }
13268 Ok(Expr {
13269 kind: ExprKind::Chown(args),
13270 line,
13271 })
13272 }
13273 "stat" => {
13274 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
13275 return Ok(e);
13276 }
13277 let args = self.parse_builtin_args()?;
13278 let arg = if args.len() == 1 {
13279 args[0].clone()
13280 } else if args.is_empty() {
13281 Expr {
13282 kind: ExprKind::ScalarVar("_".into()),
13283 line,
13284 }
13285 } else {
13286 return Err(self.syntax_err("stat requires zero or one argument", line));
13287 };
13288 Ok(Expr {
13289 kind: ExprKind::Stat(Box::new(arg)),
13290 line,
13291 })
13292 }
13293 "lstat" => {
13294 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
13295 return Ok(e);
13296 }
13297 let args = self.parse_builtin_args()?;
13298 let arg = if args.len() == 1 {
13299 args[0].clone()
13300 } else if args.is_empty() {
13301 Expr {
13302 kind: ExprKind::ScalarVar("_".into()),
13303 line,
13304 }
13305 } else {
13306 return Err(self.syntax_err("lstat requires zero or one argument", line));
13307 };
13308 Ok(Expr {
13309 kind: ExprKind::Lstat(Box::new(arg)),
13310 line,
13311 })
13312 }
13313 "link" => {
13314 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
13315 return Ok(e);
13316 }
13317 let args = self.parse_builtin_args()?;
13318 if args.len() != 2 {
13319 return Err(self.syntax_err("link requires two arguments", line));
13320 }
13321 Ok(Expr {
13322 kind: ExprKind::Link {
13323 old: Box::new(args[0].clone()),
13324 new: Box::new(args[1].clone()),
13325 },
13326 line,
13327 })
13328 }
13329 "symlink" => {
13330 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
13331 return Ok(e);
13332 }
13333 let args = self.parse_builtin_args()?;
13334 if args.len() != 2 {
13335 return Err(self.syntax_err("symlink requires two arguments", line));
13336 }
13337 Ok(Expr {
13338 kind: ExprKind::Symlink {
13339 old: Box::new(args[0].clone()),
13340 new: Box::new(args[1].clone()),
13341 },
13342 line,
13343 })
13344 }
13345 "readlink" => {
13346 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
13347 return Ok(e);
13348 }
13349 let args = self.parse_builtin_args()?;
13350 let arg = if args.len() == 1 {
13351 args[0].clone()
13352 } else if args.is_empty() {
13353 Expr {
13354 kind: ExprKind::ScalarVar("_".into()),
13355 line,
13356 }
13357 } else {
13358 return Err(self.syntax_err("readlink requires zero or one argument", line));
13359 };
13360 Ok(Expr {
13361 kind: ExprKind::Readlink(Box::new(arg)),
13362 line,
13363 })
13364 }
13365 "files" => {
13366 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
13367 return Ok(e);
13368 }
13369 let args = self.parse_builtin_args()?;
13370 Ok(Expr {
13371 kind: ExprKind::Files(args),
13372 line,
13373 })
13374 }
13375 "filesf" | "f" => {
13376 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
13377 return Ok(e);
13378 }
13379 let args = self.parse_builtin_args()?;
13380 Ok(Expr {
13381 kind: ExprKind::Filesf(args),
13382 line,
13383 })
13384 }
13385 "fr" => {
13386 let args = self.parse_builtin_args()?;
13387 Ok(Expr {
13388 kind: ExprKind::FilesfRecursive(args),
13389 line,
13390 })
13391 }
13392 "dirs" | "d" => {
13393 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
13394 return Ok(e);
13395 }
13396 let args = self.parse_builtin_args()?;
13397 Ok(Expr {
13398 kind: ExprKind::Dirs(args),
13399 line,
13400 })
13401 }
13402 "dr" => {
13403 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
13404 return Ok(e);
13405 }
13406 let args = self.parse_builtin_args()?;
13407 Ok(Expr {
13408 kind: ExprKind::DirsRecursive(args),
13409 line,
13410 })
13411 }
13412 "sym_links" => {
13413 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
13414 return Ok(e);
13415 }
13416 let args = self.parse_builtin_args()?;
13417 Ok(Expr {
13418 kind: ExprKind::SymLinks(args),
13419 line,
13420 })
13421 }
13422 "sockets" => {
13423 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
13424 return Ok(e);
13425 }
13426 let args = self.parse_builtin_args()?;
13427 Ok(Expr {
13428 kind: ExprKind::Sockets(args),
13429 line,
13430 })
13431 }
13432 "pipes" => {
13433 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
13434 return Ok(e);
13435 }
13436 let args = self.parse_builtin_args()?;
13437 Ok(Expr {
13438 kind: ExprKind::Pipes(args),
13439 line,
13440 })
13441 }
13442 "block_devices" => {
13443 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
13444 return Ok(e);
13445 }
13446 let args = self.parse_builtin_args()?;
13447 Ok(Expr {
13448 kind: ExprKind::BlockDevices(args),
13449 line,
13450 })
13451 }
13452 "char_devices" => {
13453 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
13454 return Ok(e);
13455 }
13456 let args = self.parse_builtin_args()?;
13457 Ok(Expr {
13458 kind: ExprKind::CharDevices(args),
13459 line,
13460 })
13461 }
13462 "exe" | "executables" => {
13463 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
13464 return Ok(e);
13465 }
13466 let args = self.parse_builtin_args()?;
13467 Ok(Expr {
13468 kind: ExprKind::Executables(args),
13469 line,
13470 })
13471 }
13472 "glob" => {
13473 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
13474 return Ok(e);
13475 }
13476 let args = self.parse_builtin_args()?;
13477 Ok(Expr {
13478 kind: ExprKind::Glob(args),
13479 line,
13480 })
13481 }
13482 "glob_par" => {
13483 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
13484 return Ok(e);
13485 }
13486 let (args, progress) = self.parse_glob_par_or_par_sed_args()?;
13487 Ok(Expr {
13488 kind: ExprKind::GlobPar { args, progress },
13489 line,
13490 })
13491 }
13492 "par_sed" => {
13493 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
13494 return Ok(e);
13495 }
13496 let (args, progress) = self.parse_glob_par_or_par_sed_args()?;
13497 Ok(Expr {
13498 kind: ExprKind::ParSed { args, progress },
13499 line,
13500 })
13501 }
13502 "bless" => {
13503 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
13504 return Ok(e);
13505 }
13506 let args = self.parse_builtin_args()?;
13507 Ok(Expr {
13508 kind: ExprKind::Bless {
13509 ref_expr: Box::new(args[0].clone()),
13510 class: args.get(1).cloned().map(Box::new),
13511 },
13512 line,
13513 })
13514 }
13515 "caller" => {
13516 if matches!(self.peek(), Token::LParen) {
13517 self.advance();
13518 if matches!(self.peek(), Token::RParen) {
13519 self.advance();
13520 Ok(Expr {
13521 kind: ExprKind::Caller(None),
13522 line,
13523 })
13524 } else {
13525 let a = self.parse_expression()?;
13526 self.expect(&Token::RParen)?;
13527 Ok(Expr {
13528 kind: ExprKind::Caller(Some(Box::new(a))),
13529 line,
13530 })
13531 }
13532 } else {
13533 Ok(Expr {
13534 kind: ExprKind::Caller(None),
13535 line,
13536 })
13537 }
13538 }
13539 "wantarray" => {
13540 if crate::no_interop_mode() {
13541 return Err(self.syntax_err(
13542 "stryke `wantarray` is rejected under --no-interop — \
13543 use explicit return-shape (`@result` vs `$scalar`) \
13544 or pass a flag arg instead of context-sniffing",
13545 line,
13546 ));
13547 }
13548 if matches!(self.peek(), Token::LParen) {
13549 self.advance();
13550 self.expect(&Token::RParen)?;
13551 }
13552 Ok(Expr {
13553 kind: ExprKind::Wantarray,
13554 line,
13555 })
13556 }
13557 "sub" => {
13558 if crate::no_interop_mode() {
13560 return Err(self.syntax_err(
13561 "stryke uses `fn {}` instead of `sub {}` (--no-interop)",
13562 line,
13563 ));
13564 }
13565 let (params, _prototype) = self.parse_sub_sig_or_prototype_opt()?;
13567 let body = self.parse_block()?;
13568 Ok(Expr {
13569 kind: ExprKind::CodeRef { params, body },
13570 line,
13571 })
13572 }
13573 "fn" => {
13574 let (params, _prototype) = self.parse_sub_sig_or_prototype_opt()?;
13576 self.parse_sub_attributes()?;
13577 let body = self.parse_fn_eq_body_or_block(false)?;
13578 Ok(Expr {
13579 kind: ExprKind::CodeRef { params, body },
13580 line,
13581 })
13582 }
13583 _ => {
13584 if matches!(self.peek(), Token::FatArrow) && !Self::is_underscore_topic_slot(&name)
13589 {
13590 return Ok(Expr {
13591 kind: ExprKind::String(name),
13592 line,
13593 });
13594 }
13595 if Self::is_underscore_topic_slot(&name) {
13611 if matches!(self.peek(), Token::LBracket) && self.peek_line() == line {
13612 self.advance(); let index = self.parse_expression()?;
13614 self.expect(&Token::RBracket)?;
13615 return Ok(Expr {
13616 kind: ExprKind::ArrayElement {
13617 array: format!("__topicstr__{}", name),
13618 index: Box::new(index),
13619 },
13620 line,
13621 });
13622 }
13623 return Ok(Expr {
13624 kind: ExprKind::ScalarVar(name.clone()),
13625 line,
13626 });
13627 }
13628 if matches!(self.peek(), Token::LParen) {
13630 self.advance();
13631 let args = self.parse_arg_list()?;
13632 self.expect(&Token::RParen)?;
13633 Ok(Expr {
13634 kind: ExprKind::FuncCall { name, args },
13635 line,
13636 })
13637 } else if self.peek().is_term_start()
13638 && !(matches!(self.peek(), Token::Ident(ref kw) if kw == "sub")
13639 && matches!(self.peek_at(1), Token::Ident(_)))
13640 && !(self.suppress_parenless_call > 0 && matches!(self.peek(), Token::Ident(_)))
13641 && !(matches!(self.peek(), Token::LBrace)
13642 && self.peek_line() > self.prev_line())
13643 && !(matches!(self.peek(), Token::BitNot)
13644 && self.suppress_tilde_range == 0
13645 && matches!(
13646 self.peek_at(1),
13647 Token::Ident(_) | Token::Integer(_) | Token::Float(_)
13648 ))
13649 {
13650 let args = self.parse_list_until_terminator()?;
13664 Ok(Expr {
13665 kind: ExprKind::FuncCall { name, args },
13666 line,
13667 })
13668 } else {
13669 Ok(Expr {
13675 kind: ExprKind::Bareword(name),
13676 line,
13677 })
13678 }
13679 }
13680 }
13681 }
13682
13683 fn take_bareword_filehandle_if(&mut self, accept: &[Token]) -> Option<String> {
13695 let Token::Ident(h) = self.peek().clone() else {
13696 return None;
13697 };
13698 let saved = self.pos;
13704 let qualified = h == "main"
13705 && matches!(self.peek_at(1), Token::PackageSep)
13706 && matches!(self.peek_at(2), Token::Ident(s) if !s.is_empty());
13707 if qualified {
13708 self.advance(); self.advance(); }
13711 let leaf = match self.peek().clone() {
13712 Token::Ident(s) => s,
13713 _ => {
13714 self.pos = saved;
13715 return None;
13716 }
13717 };
13718 let mut chars = leaf.chars();
13719 let first = match chars.next() {
13720 Some(c) => c,
13721 None => {
13722 self.pos = saved;
13723 return None;
13724 }
13725 };
13726 if !(first.is_ascii_uppercase() || first == '_') {
13727 self.pos = saved;
13728 return None;
13729 }
13730 if !leaf
13731 .chars()
13732 .all(|c| c.is_ascii_uppercase() || c.is_ascii_digit() || c == '_')
13733 {
13734 self.pos = saved;
13735 return None;
13736 }
13737 let next = self.peek_at(1);
13738 let ok = if accept.is_empty() {
13739 matches!(
13740 next,
13741 Token::Comma
13742 | Token::Semicolon
13743 | Token::RParen
13744 | Token::RBrace
13745 | Token::Eof
13746 | Token::PipeForward
13747 )
13748 } else {
13749 accept
13750 .iter()
13751 .any(|t| std::mem::discriminant(t) == std::mem::discriminant(next))
13752 };
13753 if !ok {
13754 self.pos = saved;
13755 return None;
13756 }
13757 self.advance();
13758 Some(leaf)
13759 }
13760
13761 fn take_bareword_filehandle(&mut self) -> Option<String> {
13763 self.take_bareword_filehandle_if(&[Token::Comma])
13764 }
13765
13766 fn take_bareword_filehandle_arg(&mut self, line: usize) -> Option<Expr> {
13770 self.take_bareword_filehandle_if(&[]).map(|name| Expr {
13771 kind: ExprKind::String(name),
13772 line,
13773 })
13774 }
13775
13776 fn parse_print_like(
13777 &mut self,
13778 make: impl FnOnce(Option<String>, Vec<Expr>) -> ExprKind,
13779 ) -> StrykeResult<Expr> {
13780 let line = self.peek_line();
13781 let handle = if let Token::Ident(ref h) = self.peek().clone() {
13783 if h == "main"
13790 && matches!(self.peek_at(1), Token::PackageSep)
13791 && matches!(
13792 self.peek_at(2),
13793 Token::Ident(s) if !s.is_empty()
13794 && s.chars().all(|c| c.is_ascii_uppercase() || c.is_ascii_digit() || c == '_')
13795 && s.chars().next().is_some_and(|c| c.is_ascii_uppercase() || c == '_'))
13796 {
13797 self.advance(); self.advance(); }
13800 let h = match self.peek().clone() {
13801 Token::Ident(s) => s,
13802 _ => h.clone(),
13803 };
13804 if h.chars().all(|c| c.is_uppercase() || c == '_')
13805 && !matches!(self.peek(), Token::LParen)
13806 {
13807 let saved = self.pos;
13808 self.advance();
13809 let is_tilde_range_after = matches!(self.peek(), Token::BitNot)
13816 && self.suppress_tilde_range == 0
13817 && matches!(
13818 self.peek_at(1),
13819 Token::Ident(_) | Token::Integer(_) | Token::Float(_)
13820 );
13821 if !is_tilde_range_after
13822 && (self.peek().is_term_start()
13823 || matches!(
13824 self.peek(),
13825 Token::DoubleString(_)
13826 | Token::BacktickString(_)
13827 | Token::SingleString(_)
13828 ))
13829 {
13830 Some(h)
13831 } else {
13832 self.pos = saved;
13833 None
13834 }
13835 } else {
13836 None
13837 }
13838 } else if let Token::ScalarVar(ref v) = self.peek().clone() {
13839 let v = v.clone();
13855 if v == "_" || matches!(self.peek_at(1), Token::LParen) {
13856 None
13857 } else {
13858 let saved = self.pos;
13859 let var_line = self.peek_line();
13860 self.advance();
13861 let next = self.peek().clone();
13862 let next_line = self.peek_line();
13863 let is_stmt_modifier = matches!(&next, Token::Ident(kw)
13864 if matches!(kw.as_str(), "if" | "unless" | "while" | "until" | "for" | "foreach"));
13865 if !is_stmt_modifier
13866 && next_line == var_line
13867 && !matches!(next, Token::LBracket | Token::LBrace)
13868 && (next.is_term_start()
13869 || matches!(
13870 next,
13871 Token::DoubleString(_)
13872 | Token::BacktickString(_)
13873 | Token::SingleString(_)
13874 ))
13875 {
13876 Some(format!("${v}"))
13878 } else {
13879 self.pos = saved;
13880 None
13881 }
13882 }
13883 } else {
13884 None
13885 };
13886 let args =
13893 if matches!(self.peek(), Token::LParen) && matches!(self.peek_at(1), Token::RParen) {
13894 let line_topic = self.peek_line();
13895 self.advance(); self.advance(); vec![Expr {
13898 kind: ExprKind::ScalarVar("_".into()),
13899 line: line_topic,
13900 }]
13901 } else {
13902 self.parse_list_until_terminator_allow_pipe()?
13903 };
13904 Ok(Expr {
13905 kind: make(handle, args),
13906 line,
13907 })
13908 }
13909
13910 fn parse_block_list(&mut self) -> StrykeResult<(Block, Expr)> {
13911 let block = self.parse_block()?;
13912 let block_end_line = self.prev_line();
13913 self.eat(&Token::Comma);
13914 if self.in_pipe_rhs()
13918 && (matches!(
13919 self.peek(),
13920 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof | Token::PipeForward
13921 ) || self.peek_line() > block_end_line)
13922 {
13923 let line = self.peek_line();
13924 return Ok((block, self.pipe_placeholder_list(line)));
13925 }
13926 let list = self.parse_expression()?;
13927 Ok((block, list))
13928 }
13929
13930 fn parse_comma_expr_list_with_timeout_tail(
13933 &mut self,
13934 paren: bool,
13935 ) -> StrykeResult<(Vec<Expr>, Option<Expr>)> {
13936 let mut parts = vec![self.parse_assign_expr()?];
13937 loop {
13938 if !self.eat(&Token::Comma) && !self.eat(&Token::FatArrow) {
13939 break;
13940 }
13941 if paren && matches!(self.peek(), Token::RParen) {
13942 break;
13943 }
13944 if matches!(
13945 self.peek(),
13946 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof
13947 ) {
13948 break;
13949 }
13950 if self.peek_is_postfix_stmt_modifier_keyword() {
13951 break;
13952 }
13953 if let Token::Ident(ref kw) = self.peek().clone() {
13954 if kw == "timeout" && matches!(self.peek_at(1), Token::FatArrow) {
13955 self.advance();
13956 self.expect(&Token::FatArrow)?;
13957 let t = self.parse_assign_expr()?;
13958 return Ok((parts, Some(t)));
13959 }
13960 }
13961 parts.push(self.parse_assign_expr()?);
13962 }
13963 Ok((parts, None))
13964 }
13965
13966 fn parse_init_block_then_list_optional_progress(
13968 &mut self,
13969 ) -> StrykeResult<(Expr, Block, Expr, Option<Expr>)> {
13970 let init = self.parse_assign_expr()?;
13971 self.expect(&Token::Comma)?;
13972 let block = self.parse_block_or_bareword_block()?;
13973 self.eat(&Token::Comma);
13974 let line = self.peek_line();
13975 if let Token::Ident(ref kw) = self.peek().clone() {
13976 if kw == "progress" && matches!(self.peek_at(1), Token::FatArrow) {
13977 self.advance();
13978 self.expect(&Token::FatArrow)?;
13979 let prog = self.parse_assign_expr()?;
13980 return Ok((
13981 init,
13982 block,
13983 Expr {
13984 kind: ExprKind::List(vec![]),
13985 line,
13986 },
13987 Some(prog),
13988 ));
13989 }
13990 }
13991 if matches!(
13992 self.peek(),
13993 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof
13994 ) {
13995 return Ok((
13996 init,
13997 block,
13998 Expr {
13999 kind: ExprKind::List(vec![]),
14000 line,
14001 },
14002 None,
14003 ));
14004 }
14005 let mut parts = vec![self.parse_assign_expr()?];
14006 loop {
14007 if !self.eat(&Token::Comma) && !self.eat(&Token::FatArrow) {
14008 break;
14009 }
14010 if matches!(
14011 self.peek(),
14012 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof
14013 ) {
14014 break;
14015 }
14016 if self.peek_is_postfix_stmt_modifier_keyword() {
14017 break;
14018 }
14019 if let Token::Ident(ref kw) = self.peek().clone() {
14020 if kw == "progress" && matches!(self.peek_at(1), Token::FatArrow) {
14021 self.advance();
14022 self.expect(&Token::FatArrow)?;
14023 let prog = self.parse_assign_expr()?;
14024 return Ok((init, block, merge_expr_list(parts), Some(prog)));
14025 }
14026 }
14027 parts.push(self.parse_assign_expr()?);
14028 }
14029 Ok((init, block, merge_expr_list(parts), None))
14030 }
14031
14032 fn parse_cluster_block_then_list_optional_progress(
14034 &mut self,
14035 ) -> StrykeResult<(Expr, Block, Expr, Option<Expr>)> {
14036 self.suppress_scalar_hash_brace = self.suppress_scalar_hash_brace.saturating_add(1);
14039 let cluster = self.parse_assign_expr();
14040 self.suppress_scalar_hash_brace = self.suppress_scalar_hash_brace.saturating_sub(1);
14041 let cluster = cluster?;
14042 self.eat(&Token::Comma);
14044 let block = self.parse_block_or_bareword_block()?;
14045 let block_end_line = self.prev_line();
14046 self.eat(&Token::Comma);
14047 let line = self.peek_line();
14048 if let Token::Ident(ref kw) = self.peek().clone() {
14049 if kw == "progress" && matches!(self.peek_at(1), Token::FatArrow) {
14050 self.advance();
14051 self.expect(&Token::FatArrow)?;
14052 let prog = self.parse_assign_expr_stop_at_pipe()?;
14053 return Ok((
14054 cluster,
14055 block,
14056 Expr {
14057 kind: ExprKind::List(vec![]),
14058 line,
14059 },
14060 Some(prog),
14061 ));
14062 }
14063 }
14064 let empty_list_ok = matches!(
14065 self.peek(),
14066 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof | Token::PipeForward
14067 ) || (self.in_pipe_rhs()
14068 && (matches!(self.peek(), Token::Comma) || self.peek_line() > block_end_line));
14069 if empty_list_ok {
14070 return Ok((
14071 cluster,
14072 block,
14073 Expr {
14074 kind: ExprKind::List(vec![]),
14075 line,
14076 },
14077 None,
14078 ));
14079 }
14080 let mut parts = vec![self.parse_assign_expr_stop_at_pipe()?];
14081 loop {
14082 if !self.eat(&Token::Comma) && !self.eat(&Token::FatArrow) {
14083 break;
14084 }
14085 if matches!(
14086 self.peek(),
14087 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof | Token::PipeForward
14088 ) {
14089 break;
14090 }
14091 if self.peek_is_postfix_stmt_modifier_keyword() {
14092 break;
14093 }
14094 if let Token::Ident(ref kw) = self.peek().clone() {
14095 if kw == "progress" && matches!(self.peek_at(1), Token::FatArrow) {
14096 self.advance();
14097 self.expect(&Token::FatArrow)?;
14098 let prog = self.parse_assign_expr_stop_at_pipe()?;
14099 return Ok((cluster, block, merge_expr_list(parts), Some(prog)));
14100 }
14101 }
14102 parts.push(self.parse_assign_expr_stop_at_pipe()?);
14103 }
14104 Ok((cluster, block, merge_expr_list(parts), None))
14105 }
14106
14107 fn parse_block_then_list_optional_progress(
14116 &mut self,
14117 ) -> StrykeResult<(Block, Expr, Option<Expr>)> {
14118 let block = self.parse_block_or_bareword_block()?;
14119 let block_end_line = self.prev_line();
14120 self.eat(&Token::Comma);
14121 let line = self.peek_line();
14122 if let Token::Ident(ref kw) = self.peek().clone() {
14123 if kw == "progress" && matches!(self.peek_at(1), Token::FatArrow) {
14124 self.advance();
14125 self.expect(&Token::FatArrow)?;
14126 let prog = self.parse_assign_expr_stop_at_pipe()?;
14127 return Ok((
14128 block,
14129 Expr {
14130 kind: ExprKind::List(vec![]),
14131 line,
14132 },
14133 Some(prog),
14134 ));
14135 }
14136 }
14137 let empty_list_ok = matches!(
14145 self.peek(),
14146 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof | Token::PipeForward
14147 ) || (self.in_pipe_rhs()
14148 && (matches!(self.peek(), Token::Comma) || self.peek_line() > block_end_line));
14149 if empty_list_ok {
14150 return Ok((
14151 block,
14152 Expr {
14153 kind: ExprKind::List(vec![]),
14154 line,
14155 },
14156 None,
14157 ));
14158 }
14159 let mut parts = vec![self.parse_assign_expr_stop_at_pipe()?];
14160 loop {
14161 if !self.eat(&Token::Comma) && !self.eat(&Token::FatArrow) {
14162 break;
14163 }
14164 if matches!(
14165 self.peek(),
14166 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof | Token::PipeForward
14167 ) {
14168 break;
14169 }
14170 if self.peek_is_postfix_stmt_modifier_keyword() {
14171 break;
14172 }
14173 if let Token::Ident(ref kw) = self.peek().clone() {
14174 if kw == "progress" && matches!(self.peek_at(1), Token::FatArrow) {
14175 self.advance();
14176 self.expect(&Token::FatArrow)?;
14177 let prog = self.parse_assign_expr_stop_at_pipe()?;
14178 return Ok((block, merge_expr_list(parts), Some(prog)));
14179 }
14180 }
14181 parts.push(self.parse_assign_expr_stop_at_pipe()?);
14182 }
14183 Ok((block, merge_expr_list(parts), None))
14184 }
14185
14186 fn parse_fan_count_and_block(
14188 &mut self,
14189 line: usize,
14190 ) -> StrykeResult<(Option<Box<Expr>>, Block)> {
14191 if matches!(self.peek(), Token::LBrace) {
14193 let block = self.parse_block()?;
14194 return Ok((None, block));
14195 }
14196 let saved = self.pos;
14197 let first = self.parse_postfix()?;
14199 if matches!(self.peek(), Token::LBrace) {
14200 let block = self.parse_block()?;
14202 Ok((Some(Box::new(first)), block))
14203 } else if matches!(self.peek(), Token::Semicolon | Token::RBrace | Token::Eof)
14204 || (matches!(self.peek(), Token::Comma)
14205 && matches!(self.peek_at(1), Token::Ident(ref kw) if kw == "progress"))
14206 {
14207 let block = self.bareword_to_no_arg_block(first);
14209 Ok((None, block))
14210 } else if matches!(first.kind, ExprKind::Integer(_)) {
14211 self.eat(&Token::Comma);
14213 let body = self.parse_fan_blockless_body(line)?;
14214 Ok((Some(Box::new(first)), body))
14215 } else {
14216 self.pos = saved;
14219 let body = self.parse_fan_blockless_body(line)?;
14220 Ok((None, body))
14221 }
14222 }
14223
14224 fn parse_fan_blockless_body(&mut self, line: usize) -> StrykeResult<Block> {
14226 if matches!(self.peek(), Token::LBrace) {
14227 return self.parse_block();
14228 }
14229 if let Token::Ident(ref name) = self.peek().clone() {
14231 if matches!(
14232 self.peek_at(1),
14233 Token::Comma | Token::Semicolon | Token::RBrace | Token::Eof | Token::PipeForward
14234 ) {
14235 let name = name.clone();
14236 self.advance();
14237 let body = Expr {
14238 kind: ExprKind::FuncCall { name, args: vec![] },
14239 line,
14240 };
14241 return Ok(vec![Statement::new(StmtKind::Expression(body), line)]);
14242 }
14243 }
14244 let expr = self.parse_assign_expr_stop_at_pipe()?;
14246 Ok(vec![Statement::new(StmtKind::Expression(expr), line)])
14247 }
14248
14249 fn bareword_to_no_arg_block(&self, expr: Expr) -> Block {
14252 let line = expr.line;
14253 let body = match &expr.kind {
14254 ExprKind::Bareword(name) => Expr {
14255 kind: ExprKind::FuncCall {
14256 name: name.clone(),
14257 args: vec![],
14258 },
14259 line,
14260 },
14261 _ => expr,
14262 };
14263 vec![Statement::new(StmtKind::Expression(body), line)]
14264 }
14265
14266 fn parse_block_or_bareword_block(&mut self) -> StrykeResult<Block> {
14275 if matches!(self.peek(), Token::LBrace) {
14276 return self.parse_block();
14277 }
14278 let line = self.peek_line();
14279 if let Token::Ident(ref name) = self.peek().clone() {
14282 if matches!(
14283 self.peek_at(1),
14284 Token::Comma | Token::Semicolon | Token::RBrace | Token::Eof | Token::PipeForward
14285 ) {
14286 let name = name.clone();
14287 self.advance();
14288 let body = Expr {
14289 kind: ExprKind::FuncCall {
14290 name,
14291 args: vec![Expr {
14292 kind: ExprKind::ScalarVar("_".to_string()),
14293 line,
14294 }],
14295 },
14296 line,
14297 };
14298 return Ok(vec![Statement::new(StmtKind::Expression(body), line)]);
14299 }
14300 }
14301 let expr = self.parse_assign_expr_stop_at_pipe()?;
14303 Ok(vec![Statement::new(StmtKind::Expression(expr), line)])
14304 }
14305
14306 fn parse_block_or_bareword_block_no_args(&mut self) -> StrykeResult<Block> {
14311 if matches!(self.peek(), Token::LBrace) {
14312 return self.parse_block();
14313 }
14314 let line = self.peek_line();
14315 if let Token::Ident(ref name) = self.peek().clone() {
14316 if matches!(
14317 self.peek_at(1),
14318 Token::Comma
14319 | Token::Semicolon
14320 | Token::RBrace
14321 | Token::Eof
14322 | Token::PipeForward
14323 | Token::Integer(_)
14324 ) {
14325 let name = name.clone();
14326 self.advance();
14327 let body = Expr {
14328 kind: ExprKind::FuncCall { name, args: vec![] },
14329 line,
14330 };
14331 return Ok(vec![Statement::new(StmtKind::Expression(body), line)]);
14332 }
14333 }
14334 let expr = self.parse_postfix()?;
14335 Ok(vec![Statement::new(StmtKind::Expression(expr), line)])
14336 }
14337
14338 fn is_known_bareword(name: &str) -> bool {
14346 Self::is_perl5_core(name) || Self::stryke_extension_name(name).is_some()
14347 }
14348
14349 fn is_try_builtin_name(name: &str) -> bool {
14355 crate::builtins::BUILTIN_ARMS
14356 .iter()
14357 .any(|arm| arm.contains(&name))
14358 }
14359
14360 fn is_perl5_core(name: &str) -> bool {
14365 matches!(
14366 name,
14367 "map" | "grep" | "sort" | "reverse" | "join" | "split"
14369 | "push" | "pop" | "shift" | "unshift" | "splice"
14370 | "splice_last" | "splice1" | "spl_last"
14371 | "pack" | "unpack"
14372 | "unpack_first" | "unpack1" | "up1"
14373 | "keys" | "values" | "each"
14375 | "chomp" | "chop" | "chr" | "ord" | "hex" | "oct"
14377 | "lc" | "uc" | "lcfirst" | "ucfirst"
14378 | "length" | "substr" | "index" | "rindex"
14379 | "sprintf" | "printf" | "print" | "say"
14380 | "pos" | "quotemeta" | "study"
14381 | "abs" | "int" | "sqrt" | "sin" | "cos" | "atan2"
14383 | "exp" | "log" | "rand" | "srand"
14384 | "time" | "localtime" | "gmtime"
14386 | "defined" | "undef" | "ref" | "scalar" | "wantarray"
14388 | "caller" | "delete" | "exists" | "bless" | "prototype"
14389 | "tie" | "untie" | "tied"
14390 | "open" | "close" | "read" | "readline" | "write" | "seek" | "tell"
14392 | "eof" | "binmode" | "getc" | "fileno" | "truncate"
14393 | "format" | "formline" | "select" | "vec"
14394 | "sysopen" | "sysread" | "sysseek" | "syswrite"
14395 | "stat" | "lstat" | "rename" | "unlink" | "utime"
14397 | "mkdir" | "rmdir" | "chdir" | "chmod" | "chown"
14398 | "glob" | "opendir" | "readdir" | "closedir"
14399 | "link" | "readlink" | "symlink"
14400 | "fcntl" | "flock" | "ioctl" | "pipe" | "dbmopen" | "dbmclose"
14402 | "msgctl" | "msgget" | "msgrcv" | "msgsnd"
14404 | "semctl" | "semget" | "semop"
14405 | "shmctl" | "shmget" | "shmread" | "shmwrite"
14406 | "system" | "exec" | "exit" | "die" | "warn" | "dump"
14408 | "fork" | "wait" | "waitpid" | "kill" | "syscall" | "alarm" | "sleep"
14409 | "chroot" | "times" | "umask" | "reset"
14410 | "getpgrp" | "setpgrp" | "getppid"
14411 | "getpriority" | "setpriority"
14412 | "socket" | "socketpair" | "connect" | "listen" | "accept" | "shutdown"
14414 | "send" | "recv" | "bind" | "setsockopt" | "getsockopt"
14415 | "getpeername" | "getsockname"
14416 | "getpwnam" | "getpwuid" | "getpwent" | "setpwent"
14418 | "getgrnam" | "getgrgid" | "getgrent" | "setgrent"
14419 | "getlogin"
14420 | "gethostbyname" | "gethostbyaddr" | "gethostent"
14421 | "getnetbyname" | "getnetent"
14422 | "getprotobyname" | "getprotoent"
14423 | "getservbyname" | "getservent"
14424 | "sethostent" | "setnetent" | "setprotoent" | "setservent"
14425 | "endpwent" | "endgrent"
14426 | "endhostent" | "endnetent" | "endprotoent" | "endservent"
14427 | "return" | "do" | "eval" | "require"
14429 | "my" | "our" | "local" | "use" | "import" | "no"
14438 | "sub" | "if" | "unless" | "while" | "until"
14439 | "for" | "foreach" | "last" | "next" | "redo" | "goto"
14440 | "not" | "and" | "or"
14441 | "qw" | "qq" | "q"
14443 | "BEGIN" | "END"
14445 )
14446 }
14447
14448 fn stryke_extension_name(name: &str) -> Option<&str> {
14451 match name {
14452 | "burp" | "god" | "swallow" | "ingest"
14464 | "null"
14469 | "congregation" | "ordain" | "muster" | "pray" | "annex"
14476 | "harvest" | "excommunicate" | "smite" | "bestow"
14477 | "enshrine" | "exhume" | "smother" | "amen"
14478 | "anoint" | "welcome" | "pilgrimage" | "bow"
14479 | "lick" | "peruse"
14480 | "chant" | "profess" | "apostatize" | "cathedral" | "cloister"
14481 | "recant" | "martyr" | "resurrect" | "divine" | "interrogate"
14482 | "ulid" | "is_ulid" | "ulid_timestamp"
14484 | "kahan_sum" | "welford_mean" | "welford_variance"
14485 | "welford_stddev" | "welford_pop_variance"
14486 | "clear" | "cls" | "whoami" | "groups"
14488 | "pushd" | "popd" | "dir_stack"
14489 | "history" | "repl_alias" | "repl_unalias" | "set_alias" | "unset_alias"
14490 | "term_size" | "term_width" | "term_height"
14491 | "set_title" | "beep" | "ring_bell" | "man" | "manpage"
14492 | "run" | "exec_script" | "source" | "src"
14493 | "rm" | "mktemp" | "mktempdir" | "whereis"
14495 | "nice" | "renice"
14496 | "tree" | "comm" | "column" | "xargs"
14497 | "openurl" | "xdg_open"
14498 | "curl_get" | "curl_post"
14499 | "iconv" | "strftime"
14500 | "tac" | "rev_lines"
14501 | "tty_raw" | "tty_cooked"
14502 | "bloom_filter" | "bloom_add" | "bloom_contains" | "bloom_len"
14504 | "bloom_clear" | "bloom_merge" | "bloom_fpr" | "bloom_bits"
14505 | "bloom_serialize" | "bloom_deserialize"
14506 | "hll" | "hyperloglog" | "hll_add" | "hll_count" | "hll_merge"
14507 | "hll_clear" | "hll_precision" | "hll_serialize" | "hll_deserialize"
14508 | "cms" | "count_min_sketch" | "cms_add" | "cms_count" | "cms_query"
14509 | "cms_merge" | "cms_clear" | "cms_serialize" | "cms_deserialize"
14510 | "topk" | "top_k_sketch" | "topk_add" | "topk_heavies" | "topk_count"
14511 | "topk_size" | "topk_merge" | "topk_clear"
14512 | "topk_serialize" | "topk_deserialize"
14513 | "t_digest" | "tdg" | "tdigest" | "td_add" | "td_quantile" | "td_count"
14514 | "td_min" | "td_max" | "td_sum" | "td_mean" | "td_merge" | "td_clear"
14515 | "td_serialize" | "td_deserialize"
14516 | "roaring" | "roaring_bitmap" | "rbm" | "rb_add" | "rb_remove" | "rb_contains"
14517 | "rb_len" | "rb_min" | "rb_max" | "rb_to_array" | "rb_rank"
14518 | "rb_or" | "rb_and" | "rb_xor" | "rb_andnot" | "rb_clear"
14519 | "rb_serialize" | "rb_deserialize"
14520 | "token_bucket" | "leaky_bucket" | "rl_try_take" | "rl_available"
14522 | "hash_ring" | "consistent_hash" | "hr_add" | "hr_remove" | "hr_get" | "hr_nodes"
14523 | "simhash" | "sh_add" | "sh_digest" | "sh_similarity"
14524 | "minhash" | "mh_add" | "mh_jaccard" | "mh_merge"
14525 | "interval_tree" | "it_insert" | "it_query_point" | "it_query_range"
14526 | "it_remove" | "it_len"
14527 | "bk_tree" | "bk_insert" | "bk_query" | "bk_len"
14528 | "rope" | "rope_insert" | "rope_delete" | "rope_substring"
14529 | "rope_to_string" | "rope_len"
14530 | "myers_diff" | "patience_diff"
14531 | "kv_open" | "kv_new" | "kv_put" | "kv_set" | "kv_get"
14533 | "kv_del" | "kv_delete" | "kv_remove" | "kv_exists" | "kv_has"
14534 | "kv_keys" | "kv_scan" | "kv_len" | "kv_count" | "kv_size"
14535 | "kv_commit" | "kv_flush" | "kv_batch" | "kv_close"
14536 | "kv_stats" | "kv_info"
14537 | "proceed" | "intercept_list" | "intercept_remove" | "intercept_clear"
14539 | "pmap" | "pmap_on" | "pflat_map" | "pflat_map_on" | "pmap_chunked"
14541 | "pgrep" | "pfor" | "pforeach" | "ploop" | "pwhile" | "psort" | "preduce" | "preduce_init" | "pmap_reduce"
14542 | "pcache" | "pchannel" | "pselect" | "puniq" | "pfirst" | "pany"
14543 | "fan" | "fan_cap" | "par_lines" | "par_walk" | "par_sed"
14544 | "par_find_files" | "par_line_count" | "pwatch" | "par_pipeline_stream"
14545 | "glob_par" | "ppool" | "barrier" | "pipeline" | "cluster"
14546 | "pmaps" | "pflat_maps" | "pgreps"
14547 | "controller" | "agent"
14549 | "mark" | "provenance" | "unmark"
14551 | "kick" | "udp_send"
14553 | "tcp_probe" | "tcp_banner" | "whois_query"
14554 | "udp_open" | "udp_send_to" | "udp_recv" | "udp_recv_from"
14556 | "udp_close" | "stun" | "stun_classify" | "punch"
14557 | "turn_allocate" | "turn_permission" | "turn_send"
14559 | "turn_recv" | "turn_refresh"
14560 | "teleport" | "arrive"
14562 | "turnbuckle" | "tb_alive" | "tb_ping" | "tb_close"
14564 | "weep"
14566 | "fore" | "e" | "ep" | "flat_map" | "flat_maps" | "maps" | "filter" | "fi" | "find_all" | "reduce" | "fold"
14568 | "inject" | "collect" | "uniq" | "distinct" | "any" | "all" | "none"
14569 | "first" | "detect" | "find" | "find_index" | "firstidx" | "first_index"
14570 | "compact" | "concat" | "chain" | "reject" | "grepv" | "flatten" | "set"
14571 | "min_by" | "max_by" | "sort_by" | "tally"
14572 | "each_with_index" | "count" | "cnt" |"len" | "group_by" | "chunk_by"
14573 | "zip" | "chunk" | "chunked" | "sliding_window" | "windowed"
14574 | "enumerate" | "with_index" | "shuffle" | "shuffled"| "heap"
14575 | "take_while" | "drop_while" | "skip_while" | "tap" | "peek" | "partition"
14576 | "zip_with" | "count_by" | "skip" | "first_or"
14577 | "getopts"
14579 | "input" | "lines" | "words" | "chars" | "cindex" | "crindex"
14581 | "digits" | "letters" | "letters_uc" | "letters_lc"
14582 | "punctuation" | "punct"
14583 | "sentences" | "sents"
14584 | "paragraphs" | "paras" | "sections" | "sects"
14585 | "numbers" | "nums" | "graphemes" | "grs" | "columns" | "cols"
14586 | "trim" | "avg" | "stddev"
14587 | "squared" | "sq" | "square" | "cubed" | "cb" | "cube" | "expt" | "pow" | "pw"
14588 | "normalize" | "snake_case" | "camel_case" | "kebab_case"
14589 | "frequencies" | "freq" | "pfrequencies" | "pfreq"
14590 | "template" | "deburr"
14591 | "shell_quote" | "shell_split" | "shellwords"
14592 | "from_jsonl" | "to_jsonl" | "from_ini" | "to_ini"
14593 | "base64url_encode" | "base64url_decode"
14594 | "interleave" | "ddump" | "stringify" | "str" | "top"
14595 | "to_json" | "to_csv" | "to_toml" | "to_yaml" | "to_xml"
14596 | "to_html" | "to_markdown" | "to_table" | "xopen"
14597 | "from_json" | "from_csv" | "from_toml" | "from_yaml" | "from_xml"
14598 | "clip" | "clipboard" | "paste" | "pbcopy" | "pbpaste" | "preview"
14599 | "sparkline" | "spark" | "bar_chart" | "bars" | "flame" | "flamechart"
14600 | "histo" | "gauge" | "spinner" | "spinner_start" | "spinner_stop"
14601 | "to_hash" | "to_set"
14602 | "to_file" | "read_lines" | "append_file" | "write_json" | "read_json"
14603 | "tempfile" | "tempdir" | "list_count" | "list_size" | "size"
14604 | "clamp" | "grep_v" | "select_keys" | "pluck" | "glob_match" | "which_all"
14605 | "dedup" | "nth" | "tail" | "take" | "drop" | "tee" | "range"
14606 | "inc" | "dec" | "elapsed"
14607 | "files" | "filesf" | "f" | "fr" | "dirs" | "d" | "dr" | "sym_links"
14609 | "sockets" | "pipes" | "block_devices" | "char_devices" | "exe" | "executables"
14610 | "basename" | "dirname" | "fileparse" | "realpath" | "canonpath"
14611 | "copy" | "cp" | "move" | "spurt" | "spit" | "read_bytes" | "which"
14612 | "getcwd" | "cd" | "ls" | "touch" | "gethostname" | "uname"
14613 | "file" | "xxd"
14614 | "csv_read" | "csv_write" | "dataframe" | "sqlite"
14616 | "fetch" | "fetch_json" | "fetch_async" | "fetch_async_json"
14617 | "par_fetch" | "par_csv_read" | "par_pipeline"
14618 | "json_encode" | "json_decode" | "json_jq"
14619 | "http_request" | "serve" | "ssh"
14620 | "html_parse" | "css_select" | "xml_parse" | "xpath"
14621 | "smtp_send"
14622 | "net_interfaces" | "net_ipv4" | "net_ipv6" | "net_mac"
14623 | "net_public_ip" | "net_dns" | "net_reverse_dns"
14624 | "net_ping" | "net_port_open" | "net_ports_scan"
14625 | "net_latency" | "net_download" | "net_headers"
14626 | "net_dns_servers" | "net_gateway" | "net_whois" | "net_hostname"
14627 | "git_log" | "git_status" | "git_diff" | "git_branches"
14629 | "git_tags" | "git_blame" | "git_authors" | "git_files"
14630 | "git_show" | "git_root"
14631 | "gh_get" | "gh_user" | "gh_org" | "gh_followers" | "gh_following"
14633 | "gh_repo" | "gh_repos" | "gh_org_repos" | "gh_starred"
14634 | "gh_gists" | "gh_gist"
14635 | "gh_issues" | "gh_prs" | "gh_commits" | "gh_branches"
14636 | "gh_tags" | "gh_releases" | "gh_contributors" | "gh_forks"
14637 | "gh_stargazers" | "gh_topics" | "gh_languages"
14638 | "gh_readme" | "gh_workflows" | "gh_runs"
14639 | "gh_search_repos" | "gh_search_users" | "gh_search_code" | "gh_search_issues"
14640 | "gh_rate_limit" | "gh_meta" | "gh_emojis" | "gh_zen"
14641 | "audio_convert" | "audio_info" | "id3_read" | "id3_write"
14643 | "to_pdf" | "pdf_text" | "pdf_pages"
14645 | "toml_encode" | "toml_decode"
14647 | "yaml_encode" | "yaml_decode"
14648 | "xml_encode" | "xml_decode"
14649 | "md5" | "sha1" | "sha224" | "sha256" | "sha384" | "sha512"
14651 | "sha3_256" | "s3_256" | "sha3_512" | "s3_512"
14652 | "shake128" | "shake256"
14653 | "hmac_sha256" | "hmac_sha1" | "hmac_sha384" | "hmac_sha512" | "hmac_md5"
14654 | "uuid" | "crc32"
14655 | "blake2b" | "b2b" | "blake2s" | "b2s" | "blake3" | "b3"
14656 | "ripemd160" | "rmd160" | "md4"
14657 | "xxh32" | "xxhash32" | "xxh64" | "xxhash64" | "xxh3" | "xxhash3" | "xxh3_128" | "xxhash3_128"
14658 | "murmur3" | "murmur3_32" | "murmur3_128"
14659 | "siphash" | "siphash_keyed"
14660 | "hkdf_sha256" | "hkdf" | "hkdf_sha512"
14661 | "poly1305" | "poly1305_mac"
14662 | "base32_encode" | "b32e" | "base32_decode" | "b32d"
14663 | "base58_encode" | "b58e" | "base58_decode" | "b58d"
14664 | "totp" | "totp_generate" | "totp_verify" | "hotp" | "hotp_generate"
14665 | "aes_cbc_encrypt" | "aes_cbc_enc" | "aes_cbc_decrypt" | "aes_cbc_dec"
14666 | "blowfish_encrypt" | "bf_enc" | "blowfish_decrypt" | "bf_dec"
14667 | "des3_encrypt" | "3des_enc" | "tdes_enc" | "des3_decrypt" | "3des_dec" | "tdes_dec"
14668 | "twofish_encrypt" | "tf_enc" | "twofish_decrypt" | "tf_dec"
14669 | "camellia_encrypt" | "cam_enc" | "camellia_decrypt" | "cam_dec"
14670 | "cast5_encrypt" | "cast5_enc" | "cast5_decrypt" | "cast5_dec"
14671 | "salsa20" | "salsa20_encrypt" | "salsa20_decrypt"
14672 | "xsalsa20" | "xsalsa20_encrypt" | "xsalsa20_decrypt"
14673 | "secretbox" | "secretbox_seal" | "secretbox_open"
14674 | "nacl_box_keygen" | "box_keygen" | "nacl_box" | "nacl_box_seal" | "box_seal"
14675 | "nacl_box_open" | "box_open"
14676 | "qr_ascii" | "qr" | "qr_png" | "qr_svg"
14677 | "barcode_code128" | "code128" | "barcode_code39" | "code39"
14678 | "barcode_ean13" | "ean13" | "barcode_svg"
14679 | "argon2_hash" | "argon2" | "argon2_verify"
14680 | "bcrypt_hash" | "bcrypt" | "bcrypt_verify"
14681 | "scrypt_hash" | "scrypt" | "scrypt_verify"
14682 | "pbkdf2" | "pbkdf2_derive"
14683 | "random_bytes" | "randbytes" | "random_bytes_hex" | "randhex"
14684 | "aes_encrypt" | "aes_enc" | "aes_decrypt" | "aes_dec"
14685 | "chacha_encrypt" | "chacha_enc" | "chacha_decrypt" | "chacha_dec"
14686 | "rsa_keygen" | "rsa_encrypt" | "rsa_enc" | "rsa_decrypt" | "rsa_dec"
14687 | "rsa_encrypt_pkcs1" | "rsa_decrypt_pkcs1" | "rsa_sign" | "rsa_verify"
14688 | "ecdsa_p256_keygen" | "p256_keygen" | "ecdsa_p256_sign" | "p256_sign"
14689 | "ecdsa_p256_verify" | "p256_verify"
14690 | "ecdsa_p384_keygen" | "p384_keygen" | "ecdsa_p384_sign" | "p384_sign"
14691 | "ecdsa_p384_verify" | "p384_verify"
14692 | "ecdsa_secp256k1_keygen" | "secp256k1_keygen"
14693 | "ecdsa_secp256k1_sign" | "secp256k1_sign"
14694 | "ecdsa_secp256k1_verify" | "secp256k1_verify"
14695 | "ecdh_p256" | "p256_dh" | "ecdh_p384" | "p384_dh"
14696 | "ed25519_keygen" | "ed_keygen" | "ed25519_sign" | "ed_sign"
14697 | "ed25519_verify" | "ed_verify"
14698 | "x25519_keygen" | "x_keygen" | "x25519_dh" | "x_dh"
14699 | "base64_encode" | "base64_decode"
14700 | "hex_encode" | "hex_decode"
14701 | "hide" | "reveal" | "hide_capacity"
14703 | "url_encode" | "url_decode"
14704 | "gzip" | "gunzip" | "gz" | "ugz" | "zstd" | "zstd_decode" | "zst" | "uzst"
14705 | "brotli" | "br" | "brotli_decode" | "ubr"
14706 | "xz" | "lzma" | "xz_decode" | "unxz" | "unlzma"
14707 | "bzip2" | "bz2" | "bzip2_decode" | "bunzip2" | "ubz2"
14708 | "lz4" | "lz4_decode" | "unlz4"
14709 | "snappy" | "snp" | "snappy_decode" | "unsnappy"
14710 | "lzw" | "lzw_decode" | "unlzw"
14711 | "tar_create" | "tar" | "tar_extract" | "untar" | "tar_list"
14712 | "tar_gz_create" | "tgz" | "tar_gz_extract" | "untgz"
14713 | "zip_create" | "zip_archive" | "zip_extract" | "unzip_archive" | "zip_list"
14714 | "erf" | "erfc" | "gamma" | "tgamma" | "lgamma" | "ln_gamma"
14716 | "digamma" | "psi" | "beta_fn" | "lbeta" | "ln_beta"
14717 | "betainc" | "beta_reg" | "gammainc" | "gamma_li"
14718 | "gammaincc" | "gamma_ui" | "gammainc_reg" | "gamma_lr"
14719 | "gammaincc_reg" | "gamma_ur"
14720 | "datetime_utc" | "datetime_now_tz" | "now"
14722 | "datetime_format_tz" | "datetime_add_seconds"
14723 | "datetime_from_epoch"
14724 | "datetime_parse_rfc3339" | "datetime_parse_local"
14725 | "datetime_strftime" | "strptime" | "time_ago" | "from_now"
14726 | "dateseq" | "dategrep" | "dateround" | "datesort"
14727 | "jwt_encode" | "jwt_decode" | "jwt_decode_unsafe"
14729 | "log_info" | "log_warn" | "log_error"
14731 | "log_debug" | "log_trace" | "log_json" | "log_level"
14732 | "async" | "spawn" | "trace" | "timer" | "bench"
14734 | "eval_timeout" | "retry" | "rate_limit" | "every"
14735 | "gen" | "watch"
14736 | "cache_clear" | "cache_exists" | "cache_stats" | "cacheview"
14738 | "assert_eq" | "assert_ne" | "assert_ok" | "assert_err"
14740 | "assert_true" | "assert_false"
14741 | "assert_gt" | "assert_lt" | "assert_ge" | "assert_le"
14742 | "assert_match" | "assert_contains" | "assert_near" | "assert_dies"
14743 | "test_run" | "run_tests" | "test_skip" | "skip_test" | "skip_assert"
14744 | "mounts" | "du" | "du_tree" | "process_list"
14746 | "thread_count" | "pool_info" | "par_bench"
14747 | "perfview" | "pfv"
14748 | "docs" | "help" | "h"
14749 | "banner"
14750 | "ip_parse" | "ip_is_valid" | "ip_version" | "ip_family"
14752 | "ip_to_int" | "int_to_ip" | "ip_to_bytes" | "bytes_to_ip"
14753 | "ip_to_bits" | "bits_to_ip"
14754 | "ip_is_private" | "ip_is_loopback" | "ip_is_multicast"
14755 | "ip_is_link_local" | "ip_is_unspecified" | "ip_is_global"
14756 | "ip_is_documentation" | "ip_is_benchmarking" | "ip_is_shared"
14757 | "ip_is_reserved" | "ip_is_broadcast"
14758 | "ip_canonical" | "ip_reverse" | "ip_arpa"
14759 | "ip_compare" | "ip_sort" | "ip_random"
14760 | "ipv4_parse" | "ipv4_is_valid" | "ipv4_classful_class"
14761 | "ipv6_parse" | "ipv6_is_valid" | "ipv6_canonical"
14762 | "ipv6_expand" | "ipv6_compress" | "ipv6_strip_zone" | "ipv6_zone_id"
14763 | "ipv6_link_local" | "ipv6_unique_local" | "ipv6_solicited_node"
14764 | "ipv6_eui64_addr" | "ipv6_link_local_from_mac"
14765 | "ipv4_to_ipv6_mapped" | "ipv4_to_ipv6_6to4" | "ipv6_to_ipv4_compat"
14766 | "ipv6_is_6to4" | "ipv6_6to4_extract"
14767 | "ipv6_is_teredo" | "ipv6_teredo_extract"
14768 | "ipv6_is_isatap" | "ipv6_isatap_extract"
14769 | "cidr_parse" | "cidr_valid_subnet" | "cidr_format"
14770 | "cidr_prefix_len" | "cidr_class"
14771 | "cidr_network" | "cidr_broadcast" | "cidr_netmask"
14772 | "cidr_hostmask" | "cidr_wildcard"
14773 | "cidr_to_netmask" | "netmask_to_prefix"
14774 | "cidr_first_host" | "cidr_last_host" | "cidr_num_hosts"
14775 | "cidr_size" | "cidr_hosts" | "cidr_iterate"
14776 | "cidr_contains" | "ip_in_cidr" | "ip_in_subnet"
14777 | "cidr_subnet" | "cidr_supernet" | "cidr_subnets" | "cidr_split"
14778 | "cidr_overlaps" | "cidr_aggregate" | "cidr_summarize"
14779 | "cidr_intersection" | "cidr_difference" | "cidr_union"
14780 | "cidr_minimum_covering" | "cidr_is_aggregable"
14781 | "cidr_next" | "cidr_prev" | "cidr_distance"
14782 | "cidr_random_ip" | "ip_random_in_cidr"
14783 | "cidr_compare" | "cidr_sort"
14784 | "mac_parse" | "mac_is_valid" | "mac_normalize" | "mac_format"
14785 | "mac_to_int" | "int_to_mac" | "mac_to_bytes" | "bytes_to_mac"
14786 | "mac_oui" | "mac_vendor_lookup" | "mac_lookup_vendor"
14787 | "mac_is_unicast" | "mac_is_multicast" | "mac_is_broadcast"
14788 | "mac_is_locally_administered" | "mac_is_universally_administered"
14789 | "mac_random" | "mac_random_local" | "mac_compare"
14790 | "eui48_to_eui64" | "eui64_to_eui48" | "eui64_from_mac"
14791 | "port_name" | "port_is_well_known" | "port_is_assigned"
14792 | "port_is_registered" | "port_is_ephemeral" | "port_is_dynamic"
14793 | "port_to_service" | "port_service_lookup"
14794 | "port_parse_range" | "port_random_ephemeral"
14795 | "ws_handshake_key" | "ws_handshake_accept"
14796 | "ws_mask" | "ws_unmask" | "ws_frame_encode" | "ws_frame_decode"
14797 | "ws_close_frame"
14798 | "cookie_parse" | "cookie_format"
14799 | "cookie_jar_new" | "cookie_jar_add" | "cookie_jar_get"
14800 | "cookie_is_session" | "cookie_is_expired"
14801 | "cookie_domain_matches" | "cookie_path_matches"
14802 | "cookie_set_max_age"
14803 | "http_method_is_idempotent" | "http_method_is_safe"
14804 | "http_method_has_body"
14805 | "http_status_class"
14806 | "http_status_is_informational" | "http_status_is_success"
14807 | "http_status_is_redirect" | "http_status_is_client_error"
14808 | "http_status_is_server_error"
14809 | "http_status_text" | "http_date_parse" | "http_date_format"
14810 | "mime_type_for_extension" | "mime_extension_for_type"
14811 | "mime_is_text" | "mime_is_image" | "mime_is_audio"
14812 | "mime_is_video" | "mime_is_application"
14813 | "bandwidth_format" | "bandwidth_parse"
14814 | "latency_ms" | "packet_loss" | "jitter_ms"
14815 | "rtt_min" | "rtt_max" | "rtt_avg"
14816 | "is_alpha_only" | "is_alphanumeric_only" | "is_numeric_only"
14818 | "is_ascii_only" | "is_printable_ascii" | "is_utf8"
14819 | "is_lowercase" | "is_uppercase" | "is_titlecase"
14820 | "is_palindrome_str"
14821 | "is_hex" | "is_octal" | "is_binary" | "is_base32"
14822 | "is_md5_hash" | "is_sha1_hash" | "is_sha256_hash"
14823 | "is_ipv6" | "is_cidr" | "is_mac"
14824 | "is_url_http" | "is_url_https"
14825 | "is_uuid_v4" | "is_uuid_v7"
14826 | "is_jwt" | "is_email_strict"
14827 | "luhn_digit" | "is_imei" | "is_imsi"
14828 | "is_vin" | "vin_decode"
14829 | "is_ean13" | "is_upc"
14830 | "is_isbn" | "isbn10_to_isbn13" | "isbn13_to_isbn10"
14831 | "iban_format" | "iban_country" | "is_bic" | "is_swift"
14832 | "is_phone" | "is_phone_e164"
14833 | "is_zip_us" | "is_zip_plus4" | "is_postal_code" | "is_ssn_us"
14834 | "semver_compare" | "semver_satisfies"
14835 | "semver_increment_major" | "semver_increment_minor" | "semver_increment_patch"
14836 | "extended_gcd" | "modinverse" | "modpow" | "modular_sqrt"
14838 | "stirling_1" | "stirling_2" | "catalan_number" | "lucas_n"
14839 | "prime_count_below" | "divisor_count" | "divisor_sum" | "sigma_divisors"
14840 | "sum_digits" | "product_digits" | "collatz_steps"
14841 | "hyperoperation" | "busy_beaver"
14842 | "quadratic_residue" | "is_quadratic_residue"
14843 | "discrete_log" | "order_modulo" | "square_free"
14844 | "perfect_number" | "abundant" | "deficient"
14845 | "random_bernoulli" | "random_normal" | "random_lognormal"
14847 | "random_exponential" | "random_poisson" | "random_gamma" | "random_beta"
14848 | "random_alphanumeric" | "random_alphabetic" | "random_password"
14849 | "random_choices_weighted"
14850 | "sample_weighted_unique" | "reservoir_sample_weighted"
14851 | "seeded_rng" | "save_random_state" | "restore_random_state"
14852 | "complex_new" | "complex_real" | "complex_imag"
14854 | "complex_polar" | "complex_from_polar"
14855 | "complex_magnitude" | "complex_abs" | "complex_phase" | "complex_angle"
14856 | "complex_conjugate"
14857 | "complex_add" | "complex_sub" | "complex_mul" | "complex_div"
14858 | "complex_pow" | "complex_sqrt" | "complex_exp" | "complex_log"
14859 | "complex_sin" | "complex_cos" | "complex_tan"
14860 | "complex_sinh" | "complex_cosh" | "complex_tanh"
14861 | "complex_equal"
14862 | "point_angle"
14863 | "line_intersect" | "line_segment_intersect" | "line_distance_point"
14864 | "polygon_signed_area" | "polygon_orientation" | "polygon_reverse"
14865 | "polygon_contains_point" | "polygon_convex"
14866 | "polygon_simplify_dp" | "polygon_convex_hull_2d"
14867 | "triangle_area" | "triangle_centroid"
14868 | "triangle_circumcircle" | "triangle_incircle"
14869 | "triangle_contains_point"
14870 | "circle_circumference" | "circle_area"
14871 | "circle_intersects_line" | "circle_intersects_circle"
14872 | "rect_area" | "rect_perimeter" | "rect_intersect"
14873 | "rect_contains_point" | "rect_union"
14874 | "ellipse_area"
14875 | "sphere_surface_area" | "cylinder_surface_area"
14876 | "cone_surface_area" | "torus_surface_area"
14877 | "srgb_to_rgb" | "rgb_to_srgb"
14878 | "rgb_to_p3" | "p3_to_rgb"
14879 | "rgb_to_adobe_rgb" | "adobe_rgb_to_rgb"
14880 | "xyz_d65_to_d50" | "xyz_d50_to_d65"
14881 | "gamma_apply" | "gamma_remove"
14882 | "white_point_d65" | "white_point_d50"
14883 | "color_temperature_to_rgb" | "rgb_to_color_temperature"
14884 | "chromatic_adaptation"
14885 | "color_interpolate_rgb" | "color_interpolate_hsl"
14886 | "color_interpolate_lab" | "color_interpolate_oklab"
14887 | "color_blend_screen"
14888 | "atan2_deg" | "atan2_quadrant"
14889 | "polar_to_cartesian" | "cartesian_to_polar"
14890 | "spherical_to_cartesian" | "cartesian_to_spherical"
14891 | "cylindrical_to_cartesian" | "cartesian_to_cylindrical"
14892 | "versine_fn"
14893 | "triples" | "n_tuples" | "peekable" | "runs" | "unique_by"
14895 | "multipeek" | "lookahead_n"
14896 | "sliding_average" | "sliding_sum" | "sliding_max" | "sliding_min"
14897 | "top_n_by" | "bottom_n_by" | "all_equal" | "take_n_random"
14898 | "unzip3" | "roundrobin" | "mode_iter" | "distinct_sample"
14899 | "ranked_choice" | "boyer_moore_majority"
14900 | "quickselect_nth" | "quickselect_median"
14901 | "top_k_min_heap" | "bottom_k_max_heap"
14902 | "unique_consecutive" | "exclude" | "exclude_first" | "exclude_last"
14903 | "weave_n" | "pad_left_n" | "pad_right_n"
14904 | "collect_into_string" | "collect_into_hashset" | "collect_into_btreeset"
14905 | "collect_into_hashmap" | "collect_into_btreemap"
14906 | "foldl1_iter" | "foldr1_iter"
14907 | "sort_by_cached_key"
14908 | "position_max" | "position_min" | "position_max_by" | "position_min_by"
14909 | "group_map"
14910 | "levenshtein_normalized" | "ratcliff_obershelp" | "match_rating"
14911 | "str_lcs" | "str_lcs_length" | "str_longest_common_substring"
14912 | "str_kmp" | "str_boyer_moore" | "str_rabin_karp"
14913 | "str_aho_corasick" | "str_z_array" | "str_suffix_array"
14914 | "str_rotations" | "str_compress_rle" | "str_decompress_rle"
14915 | "str_huffman_encode" | "str_huffman_decode"
14916 | "str_compress_lzss" | "str_decompress_lzss"
14917 | "str_isogram" | "fold_case"
14918 | "bignum_new" | "bignum_from_str" | "bignum_to_str" | "bignum_to_int"
14920 | "bignum_add" | "bignum_sub" | "bignum_mul" | "bignum_div" | "bignum_mod"
14921 | "bignum_pow" | "bignum_modpow" | "bignum_gcd" | "bignum_lcm"
14922 | "bignum_factorial" | "bignum_sqrt" | "bignum_bit_length"
14923 | "bignum_set_bit" | "bignum_clear_bit" | "bignum_test_bit"
14924 | "bignum_and" | "bignum_or" | "bignum_xor" | "bignum_not"
14925 | "bignum_shl" | "bignum_shr" | "bignum_compare"
14926 | "bignum_negate" | "bignum_abs" | "bignum_sign"
14927 | "bignum_is_zero" | "bignum_is_negative" | "bignum_is_prime"
14928 | "bignum_random"
14929 | "gravity_constant" | "physics_apply_force" | "physics_apply_impulse"
14930 | "physics_collide_aabb" | "physics_collide_sphere"
14931 | "physics_raycast" | "physics_step"
14932 | "particle_emit" | "particle_update"
14933 | "vector2_new" | "vector2_add" | "vector2_sub" | "vector2_scale"
14934 | "vector2_dot" | "vector2_cross" | "vector2_length"
14935 | "vector2_normalize" | "vector2_distance" | "vector2_rotate"
14936 | "quaternion_new" | "quaternion_from_axis_angle"
14937 | "quaternion_multiply" | "quaternion_normalize" | "quaternion_to_matrix"
14938 | "freq_to_note" | "note_to_freq" | "midi_note_to_name"
14939 | "chord_notes" | "scale_notes" | "transpose_note"
14940 | "window_tukey" | "zero_crossing_rate" | "peak_db"
14941 | "audio_normalize" | "audio_fade_in" | "audio_fade_out"
14942 | "audio_to_mono" | "audio_to_stereo"
14943 | "biquad_lowpass" | "biquad_highpass" | "biquad_bandpass" | "biquad_notch"
14944 | "oscillator_sine" | "oscillator_square"
14945 | "oscillator_sawtooth" | "oscillator_triangle"
14946 | "adsr_envelope" | "ar_envelope" | "crossfade"
14947 | "fade_curve_linear" | "fade_curve_logarithmic" | "fade_curve_exponential"
14948 | "bbox_contains" | "bbox_union" | "bbox_intersect"
14949 | "bbox_center" | "bbox_area"
14950 | "mercator_unproject" | "geohash_precision"
14951 | "jq_get" | "jq_set" | "jq_delete" | "jq_select"
14953 | "jq_keys_at" | "jq_values_at" | "jq_length_at"
14954 | "jq_type" | "jq_has" | "jq_paths" | "jq_leaf_paths"
14955 | "jq_walk" | "jq_map_values" | "jq_filter"
14956 | "jq_to_entries" | "jq_from_entries" | "jq_with_entries"
14957 | "jq_recurse" | "jq_min_by" | "jq_max_by"
14958 | "jq_sort_by" | "jq_group_by" | "jq_unique_by"
14959 | "jq_any" | "jq_all" | "jq_flatten"
14960 | "jq_index" | "jq_indices" | "jq_first" | "jq_last"
14961 | "jq_split_at" | "jq_chunks" | "jq_zip" | "jq_combinations"
14962 | "json_diff" | "json_patch" | "json_merge_patch"
14963 | "json_pointer_resolve" | "json_pointer_set"
14964 | "html_to_text" | "html_pretty" | "html_minify"
14965 | "html_sanitize" | "html_strip_tags" | "html_strip_scripts" | "html_strip_styles"
14966 | "html_extract_links" | "html_extract_images" | "html_extract_text"
14967 | "html_extract_meta" | "html_extract_title"
14968 | "html_extract_headings" | "html_extract_tables"
14969 | "html_inner_text" | "html_canonical_url"
14970 | "html_meta_charset" | "html_meta_keywords" | "html_meta_description"
14971 | "html_meta_og" | "html_meta_twitter"
14972 | "html_to_markdown" | "markdown_to_html" | "markdown_render"
14973 | "xml_pretty" | "xml_minify"
14974 | "xml_namespace" | "xml_text" | "xml_attrs"
14975 | "xml_children_by_tag" | "xml_root"
14976 | "xpath_select_one" | "xpath_attribute" | "xpath_text"
14977 | "xml_to_json" | "json_to_xml" | "xml_canonicalize"
14978 | "css_parse" | "css_minify" | "css_pretty"
14979 | "css_selector_parse" | "css_rule_extract" | "css_specificity"
14980 | "css_var_resolve" | "css_property_set" | "css_property_get"
14981 | "css_url_extract" | "css_import_extract" | "css_font_extract"
14982 | "selector_to_xpath" | "xpath_to_selector"
14983 | "http_status_continue" | "http_status_switching_protocols"
14985 | "http_status_ok" | "http_status_created" | "http_status_accepted"
14986 | "http_status_no_content" | "http_status_partial_content"
14987 | "http_status_multiple_choices" | "http_status_moved_permanently"
14988 | "http_status_found" | "http_status_see_other" | "http_status_not_modified"
14989 | "http_status_temporary_redirect" | "http_status_permanent_redirect"
14990 | "http_status_bad_request" | "http_status_unauthorized"
14991 | "http_status_payment_required" | "http_status_forbidden"
14992 | "http_status_not_found" | "http_status_method_not_allowed"
14993 | "http_status_not_acceptable" | "http_status_conflict" | "http_status_gone"
14994 | "http_status_length_required" | "http_status_precondition_failed"
14995 | "http_status_payload_too_large" | "http_status_uri_too_long"
14996 | "http_status_unsupported_media_type" | "http_status_range_not_satisfiable"
14997 | "http_status_expectation_failed" | "http_status_im_a_teapot"
14998 | "http_status_unprocessable_entity" | "http_status_too_many_requests"
14999 | "http_status_internal_server_error" | "http_status_not_implemented"
15000 | "http_status_bad_gateway" | "http_status_service_unavailable"
15001 | "http_status_gateway_timeout" | "http_status_http_version_not_supported"
15002 | "http_method_get" | "http_method_post" | "http_method_put"
15003 | "http_method_delete" | "http_method_patch" | "http_method_head"
15004 | "http_method_options" | "http_method_trace" | "http_method_connect"
15005 | "dbeta" | "qbeta" | "rbeta" | "dcauchy" | "qcauchy" | "rcauchy"
15006 | "dexp" | "qexp" | "rexp" | "dgamma" | "qgamma" | "rgamma"
15007 | "dlnorm" | "qlnorm" | "rlnorm" | "dlogis" | "qlogis" | "rlogis"
15008 | "dpois" | "qpois" | "rpois" | "dweibull" | "qweibull" | "rweibull"
15009 | "qnorm" | "rnorm" | "qunif" | "runif"
15010 | "qbinom" | "rbinom" | "qgeom" | "rgeom" | "qhyper" | "rhyper"
15011 | "qchisq" | "rchisq" | "qf" | "rf" | "qt" | "rt"
15012 | "currency_format" | "currency_parse" | "currency_round"
15014 | "currency_split_thousands" | "currency_code_to_symbol"
15015 | "currency_symbol_to_code" | "currency_convert" | "currency_rate"
15016 | "currency_iso_4217" | "currency_decimal_places"
15017 | "money_add" | "money_sub" | "money_mul" | "money_div" | "money_compare"
15018 | "tokenize_simple" | "tokenize_word" | "tokenize_subword"
15019 | "tokenize_bpe" | "tokenize_sentencepiece" | "embed_text"
15020 | "cosine_similarity" | "euclidean_distance" | "manhattan_distance"
15021 | "dot_product" | "normalize_vector"
15022 | "vector_add" | "vector_sub" | "vector_scale" | "vector_mean"
15023 | "top_k_indices" | "softmax" | "sigmoid" | "log_softmax" | "cross_entropy"
15024 | "path_canonical" | "path_relative_to" | "path_components"
15025 | "path_filename" | "path_stem" | "path_extension"
15026 | "path_join_many" | "path_with_extension" | "path_with_filename"
15027 | "path_is_subdirectory" | "path_common_ancestor" | "path_strip_prefix"
15028 | "path_glob_match_regex"
15029 | "file_mime" | "file_kind" | "file_attr_get" | "file_attr_set"
15030 | "xattr_get" | "xattr_set" | "xattr_list"
15031 | "file_chmod_string" | "file_chmod_octal" | "file_locked"
15032 | "file_acl_get" | "file_acl_set"
15033 | "locale_parse" | "locale_format" | "locale_language"
15034 | "locale_region" | "locale_script" | "locale_variant" | "locale_canonical"
15035 | "bcp47_parse" | "bcp47_format" | "bcp47_validate"
15036 | "language_tag_match" | "language_tag_subtags"
15037 | "locale_likely_subtags" | "locale_minimize" | "locale_collation"
15038 | "locale_calendar" | "locale_currency"
15039 | "locale_number_format" | "locale_date_format" | "locale_time_format"
15040 | "locale_decimal_separator" | "locale_group_separator"
15041 | "locale_first_day_of_week" | "locale_measurement_system"
15042 | "country_code_alpha2" | "country_code_alpha3" | "country_code_numeric"
15043 | "country_name" | "country_phone_prefix" | "country_currency"
15044 | "country_languages"
15045 | "language_iso_639_1" | "language_iso_639_2" | "language_iso_639_3"
15046 | "language_name"
15047 | "channel_unbounded" | "channel_bounded" | "channel_sync"
15048 | "channel_send_timeout" | "channel_recv_timeout"
15049 | "channel_try_recv" | "channel_try_send"
15050 | "channel_drain" | "channel_close" | "channel_is_closed"
15051 | "broadcast_channel_new" | "broadcast_channel_subscribe"
15052 | "broadcast_channel_publish"
15053 | "mpsc_new" | "mpmc_new" | "spmc_new" | "oneshot_new"
15054 | "mutex" | "mutex_lock" | "mutex_unlock" | "mutex_try_lock" | "mutex_is_locked"
15056 | "semaphore" | "sem"
15057 | "semaphore_acquire" | "sem_acquire"
15058 | "semaphore_release" | "sem_release"
15059 | "semaphore_try_acquire" | "sem_try_acquire"
15060 | "semaphore_permits" | "sem_permits"
15061 | "semaphore_limit" | "sem_limit"
15062 | "stress_cpu" | "scpu" | "stress_mem" | "smem"
15064 | "stress_io" | "sio" | "stress_test" | "st"
15065 | "heat" | "fire" | "fire_and_forget" | "pin"
15066 | "slurp" | "cat" | "c" | "capture" | "pager" | "pg" | "less"
15068 | "stdin"
15069 | "__stryke_rust_compile"
15071 | "vec_set_value"
15072 | "p" | "rev"
15074 | "even" | "odd" | "zero" | "nonzero"
15076 | "positive" | "pos_n" | "negative" | "neg_n"
15077 | "sign" | "negate" | "double" | "triple" | "half"
15078 | "identity" | "id"
15079 | "round" | "floor" | "ceil" | "ceiling" | "trunc" | "truncn"
15080 | "gcd" | "lcm" | "min2" | "max2"
15081 | "log2" | "log10" | "hypot"
15082 | "rad_to_deg" | "r2d" | "deg_to_rad" | "d2r"
15083 | "pow2" | "abs_diff"
15084 | "factorial" | "fact" | "fibonacci" | "fib"
15085 | "is_prime" | "is_square" | "is_power_of_two" | "is_pow2"
15086 | "cbrt" | "exp2" | "percent" | "pct" | "inverse"
15087 | "median" | "mode_val" | "variance"
15088 | "is_empty" | "is_blank" | "is_numeric"
15090 | "is_upper" | "is_lower" | "is_alpha" | "is_digit" | "is_alnum"
15091 | "is_space" | "is_whitespace"
15092 | "starts_with" | "sw" | "ends_with" | "ew" | "contains"
15093 | "capitalize" | "cap" | "swap_case" | "repeat"
15094 | "title_case" | "title" | "squish"
15095 | "pad_left" | "lpad" | "pad_right" | "rpad" | "center"
15096 | "truncate_at" | "shorten" | "reverse_str" | "rev_str"
15097 | "char_count" | "word_count" | "wc" | "line_count" | "lc_lines"
15098 | "is_array" | "is_arrayref" | "is_hash" | "is_hashref"
15100 | "is_code" | "is_coderef" | "is_ref"
15101 | "is_undef" | "is_defined" | "is_def"
15102 | "is_string" | "is_str" | "is_int" | "is_integer" | "is_float"
15103 | "invert" | "merge_hash"
15105 | "hash_map_values" | "hash_filter_keys" | "hash_filter_values"
15106 | "has_key" | "hk" | "has_any_key" | "has_all_keys"
15107 | "both" | "either" | "neither" | "xor_bool" | "bool_to_int" | "b2i"
15109 | "riffle" | "intersperse" | "every_nth"
15111 | "drop_n" | "take_n" | "rotate" | "swap_pairs"
15112 | "to_bin" | "bin_of" | "to_hex" | "hex_of" | "to_oct" | "oct_of"
15114 | "from_bin" | "from_hex" | "from_oct" | "to_base" | "from_base"
15115 | "bits_count" | "popcount" | "leading_zeros" | "lz"
15116 | "trailing_zeros" | "tz" | "bit_length" | "bitlen"
15117 | "bit_and" | "bit_or" | "bit_xor" | "bit_not"
15119 | "shift_left" | "shl" | "shift_right" | "shr"
15120 | "bit_set" | "bit_clear" | "bit_toggle" | "bit_test"
15121 | "c_to_f" | "f_to_c" | "c_to_k" | "k_to_c" | "f_to_k" | "k_to_f"
15123 | "miles_to_km" | "km_to_miles" | "miles_to_m" | "m_to_miles"
15125 | "feet_to_m" | "m_to_feet" | "inches_to_cm" | "cm_to_inches"
15126 | "yards_to_m" | "m_to_yards"
15127 | "kg_to_lbs" | "lbs_to_kg" | "g_to_oz" | "oz_to_g"
15129 | "stone_to_kg" | "kg_to_stone"
15130 | "bytes_to_kb" | "b_to_kb" | "kb_to_bytes" | "kb_to_b"
15132 | "bytes_to_mb" | "mb_to_bytes" | "bytes_to_gb" | "gb_to_bytes"
15133 | "kb_to_mb" | "mb_to_gb"
15134 | "bits_to_bytes" | "bytes_to_bits"
15135 | "seconds_to_minutes" | "s_to_m" | "minutes_to_seconds" | "m_to_s"
15137 | "seconds_to_hours" | "hours_to_seconds"
15138 | "seconds_to_days" | "days_to_seconds"
15139 | "minutes_to_hours" | "hours_to_minutes"
15140 | "hours_to_days" | "days_to_hours"
15141 | "is_leap_year" | "is_leap" | "days_in_month"
15143 | "month_name" | "month_short"
15144 | "weekday_name" | "weekday_short" | "quarter_of"
15145 | "now_ms" | "now_us" | "now_ns"
15147 | "unix_epoch" | "epoch" | "unix_epoch_ms" | "epoch_ms"
15148 | "rgb_to_hex" | "hex_to_rgb"
15150 | "ansi_red" | "ansi_green" | "ansi_yellow" | "ansi_blue"
15151 | "ansi_magenta" | "ansi_cyan" | "ansi_white" | "ansi_black"
15152 | "ansi_bold" | "ansi_dim" | "ansi_underline" | "ansi_reverse"
15153 | "strip_ansi"
15154 | "red" | "green" | "yellow" | "blue" | "magenta" | "purple" | "cyan"
15155 | "white" | "black" | "bold" | "dim" | "italic" | "underline"
15156 | "strikethrough" | "ansi_off" | "off" | "gray" | "grey"
15157 | "bright_red" | "bright_green" | "bright_yellow" | "bright_blue"
15158 | "bright_magenta" | "bright_cyan" | "bright_white"
15159 | "bg_red" | "bg_green" | "bg_yellow" | "bg_blue"
15160 | "bg_magenta" | "bg_cyan" | "bg_white" | "bg_black"
15161 | "red_bold" | "bold_red" | "green_bold" | "bold_green"
15162 | "yellow_bold" | "bold_yellow" | "blue_bold" | "bold_blue"
15163 | "magenta_bold" | "bold_magenta" | "cyan_bold" | "bold_cyan"
15164 | "white_bold" | "bold_white"
15165 | "blink" | "rapid_blink" | "hidden" | "overline"
15166 | "bg_bright_red" | "bg_bright_green" | "bg_bright_yellow" | "bg_bright_blue"
15167 | "bg_bright_magenta" | "bg_bright_cyan" | "bg_bright_white"
15168 | "rgb" | "bg_rgb" | "color256" | "c256" | "bg_color256" | "bg_c256"
15169 | "ipv4_to_int" | "int_to_ipv4"
15171 | "is_valid_ipv4" | "is_valid_ipv6" | "is_valid_email" | "is_valid_url"
15172 | "path_ext" | "path_parent" | "path_join" | "path_split"
15174 | "strip_prefix" | "strip_suffix" | "ensure_prefix" | "ensure_suffix"
15175 | "const_fn" | "always_true" | "always_false"
15177 | "flip_args" | "first_arg" | "second_arg" | "last_arg"
15178 | "count_eq" | "count_ne" | "all_eq"
15180 | "all_distinct" | "all_unique" | "has_duplicates"
15181 | "sum_of" | "product_of" | "max_of" | "min_of" | "range_of"
15182 | "quote" | "single_quote" | "unquote"
15184 | "extract_between" | "ellipsis"
15185 | "coin_flip" | "dice_roll"
15187 | "random_int" | "random_float" | "random_bool"
15188 | "random_choice" | "random_between"
15189 | "random_string" | "random_alpha" | "random_digit"
15190 | "refresh_stashes"
15192 | "os_name" | "os_arch" | "num_cpus"
15194 | "pid" | "ppid" | "uid" | "gid"
15195 | "username" | "home_dir" | "temp_dir"
15196 | "mem_total" | "mem_free" | "mem_used"
15197 | "swap_total" | "swap_free" | "swap_used"
15198 | "disk_total" | "disk_free" | "disk_avail" | "disk_used"
15199 | "load_avg" | "sys_uptime" | "page_size"
15200 | "os_version" | "os_family" | "endianness" | "pointer_width"
15201 | "proc_mem" | "rss"
15202 | "transpose" | "unzip"
15204 | "run_length_encode" | "rle" | "run_length_decode" | "rld"
15205 | "sliding_pairs" | "consecutive_eq" | "flatten_deep"
15206 | "tan" | "asin" | "acos" | "atan"
15208 | "sinh" | "cosh" | "tanh" | "asinh" | "acosh" | "atanh"
15209 | "sqr" | "cube_fn"
15210 | "mod_op" | "ceil_div" | "floor_div"
15211 | "is_finite" | "is_infinite" | "is_inf" | "is_nan"
15212 | "degrees" | "radians"
15213 | "min_abs" | "max_abs"
15214 | "saturate" | "sat01" | "wrap_around"
15215 | "rot13" | "rot47" | "caesar_shift" | "reverse_words"
15217 | "count_vowels" | "count_consonants" | "is_vowel" | "is_consonant"
15218 | "first_word" | "last_word"
15219 | "left_str" | "head_str" | "right_str" | "tail_str" | "mid_str"
15220 | "lowercase" | "uppercase"
15221 | "pascal_case" | "pc_case"
15222 | "constant_case" | "upper_snake" | "dot_case" | "path_case"
15223 | "is_palindrome" | "hamming_distance"
15224 | "longest_common_prefix" | "lcp"
15225 | "ascii_ord" | "ascii_chr" | "count_char" | "indexes_of"
15226 | "replace_first" | "replace_all_str"
15227 | "contains_any" | "contains_all"
15228 | "starts_with_any" | "ends_with_any"
15229 | "is_pair" | "is_triple"
15231 | "is_sorted" | "is_asc" | "is_sorted_desc" | "is_desc"
15232 | "is_empty_arr" | "is_empty_hash"
15233 | "is_subset" | "is_superset" | "is_permutation"
15234 | "first_eq" | "last_eq"
15236 | "index_of" | "last_index_of" | "positions_of"
15237 | "batch" | "binary_search" | "bsearch" | "linear_search" | "lsearch"
15238 | "distinct_count" | "longest" | "shortest"
15239 | "array_union" | "list_union"
15240 | "array_intersection" | "list_intersection"
15241 | "array_difference" | "list_difference"
15242 | "symmetric_diff" | "group_of_n" | "chunk_n"
15243 | "repeat_list" | "cycle_n" | "random_sample" | "sample_n"
15244 | "pick_keys" | "pick" | "omit_keys" | "omit"
15246 | "map_keys_fn" | "map_values_fn"
15247 | "hash_size" | "hash_from_pairs" | "pairs_from_hash"
15248 | "hash_eq" | "keys_sorted" | "values_sorted" | "remove_keys"
15249 | "today" | "yesterday" | "tomorrow" | "is_weekend" | "is_weekday"
15251 | "json_pretty" | "json_minify" | "escape_json" | "json_escape"
15253 | "cmd_exists" | "env_get" | "env_has" | "env_keys"
15255 | "argc" | "script_name"
15256 | "has_stdin_tty" | "has_stdout_tty" | "has_stderr_tty"
15257 | "uuid_v4" | "nanoid" | "short_id" | "is_uuid" | "token"
15259 | "email_domain" | "email_local"
15261 | "url_host" | "url_path" | "url_query" | "url_scheme"
15262 | "file_size" | "fsize" | "file_mtime" | "mtime"
15264 | "file_atime" | "atime" | "file_ctime" | "ctime"
15265 | "is_symlink" | "is_readable" | "is_writable" | "is_executable"
15266 | "path_is_abs" | "path_is_rel"
15267 | "min_max" | "percentile" | "harmonic_mean" | "geometric_mean" | "zscore"
15269 | "sorted" | "sorted_desc" | "sorted_nums" | "sorted_by_length"
15270 | "reverse_list" | "list_reverse"
15271 | "without" | "without_nth" | "take_last" | "drop_last"
15272 | "pairwise" | "zipmap"
15273 | "format_bytes" | "human_bytes"
15274 | "format_duration" | "human_duration"
15275 | "format_number" | "group_number"
15276 | "format_percent" | "pad_number"
15277 | "spaceship" | "cmp_num" | "cmp_str"
15278 | "compare_versions" | "version_cmp"
15279 | "hash_insert" | "hash_update" | "hash_delete"
15280 | "matches_regex" | "re_match"
15281 | "count_regex_matches" | "regex_extract"
15282 | "regex_split_str" | "regex_replace_str"
15283 | "shuffle_chars" | "random_char" | "nth_word"
15284 | "head_lines" | "tail_lines" | "count_substring"
15285 | "is_valid_hex" | "hex_upper" | "hex_lower"
15286 | "ms_to_s" | "s_to_ms" | "ms_to_ns" | "ns_to_ms"
15287 | "us_to_ns" | "ns_to_us"
15288 | "liters_to_gallons" | "gallons_to_liters"
15289 | "liters_to_ml" | "ml_to_liters"
15290 | "cups_to_ml" | "ml_to_cups"
15291 | "newtons_to_lbf" | "lbf_to_newtons"
15292 | "joules_to_cal" | "cal_to_joules"
15293 | "watts_to_hp" | "hp_to_watts"
15294 | "pascals_to_psi" | "psi_to_pascals"
15295 | "bar_to_pascals" | "pascals_to_bar"
15296 | "match"
15298 | "fst" | "rest" | "rst" | "second" | "snd"
15300 | "last_clj" | "lastc" | "butlast" | "bl"
15301 | "ffirst" | "ffs" | "fnext" | "fne" | "nfirst" | "nfs" | "nnext" | "nne"
15302 | "cons" | "conj"
15303 | "peek_clj" | "pkc" | "pop_clj" | "popc"
15304 | "some" | "not_any" | "not_every"
15305 | "comp" | "compose" | "partial" | "constantly" | "complement" | "compl"
15306 | "fnil" | "juxt"
15307 | "memoize" | "memo" | "curry" | "once"
15308 | "deep_clone" | "dclone" | "deep_merge" | "dmerge" | "deep_equal" | "deq"
15309 | "iterate" | "iter" | "repeatedly" | "rptd" | "cycle" | "cyc"
15310 | "mapcat" | "mcat" | "keep" | "kp" | "remove_clj" | "remc"
15311 | "reductions" | "rdcs"
15312 | "partition_by" | "pby" | "partition_all" | "pall"
15313 | "split_at" | "spat" | "split_with" | "spw"
15314 | "assoc" | "dissoc" | "get_in" | "gin" | "assoc_in" | "ain" | "update_in" | "uin"
15315 | "into" | "empty_clj" | "empc" | "seq" | "vec_clj" | "vecc"
15316 | "apply" | "appl"
15317 | "divmod" | "dm" | "accumulate" | "accum" | "starmap" | "smap"
15319 | "zip_longest" | "zipl" | "zip_fill" | "zipf" | "combinations" | "comb" | "permutations" | "perm"
15320 | "cartesian_product" | "cprod" | "compress" | "cmpr" | "filterfalse" | "falf"
15321 | "islice" | "isl" | "chain_from" | "chfr" | "pairwise_iter" | "pwi"
15322 | "tee_iter" | "teei" | "groupby_iter" | "gbi"
15323 | "each_slice" | "eslice" | "each_cons" | "econs"
15324 | "one" | "none_match" | "nonem"
15325 | "find_index_fn" | "fidx" | "rindex_fn" | "ridx"
15326 | "minmax" | "mmx" | "minmax_by" | "mmxb"
15327 | "dig" | "values_at" | "vat" | "fetch_val" | "fv" | "slice_arr" | "sla"
15328 | "transform_keys" | "tkeys" | "transform_values" | "tvals"
15329 | "sum_by" | "sumb" | "uniq_by" | "uqb"
15330 | "flat_map_fn" | "fmf" | "then_fn" | "thfn" | "times_fn" | "timf"
15331 | "step" | "upto" | "downto"
15332 | "find_last" | "fndl" | "find_last_index" | "fndli"
15334 | "at_index" | "ati" | "replace_at" | "repa"
15335 | "to_sorted" | "tsrt" | "to_reversed" | "trev" | "to_spliced" | "tspl"
15336 | "flat_depth" | "fltd" | "fill_arr" | "filla" | "includes_val" | "incv"
15337 | "object_keys" | "okeys" | "object_values" | "ovals"
15338 | "object_entries" | "oents" | "object_from_entries" | "ofents"
15339 | "span_fn" | "spanf" | "break_fn" | "brkf" | "group_runs" | "gruns"
15341 | "nub" | "sort_on" | "srton"
15342 | "intersperse_val" | "isp" | "intercalate" | "ical"
15343 | "replicate_val" | "repv" | "elem_of" | "elof" | "not_elem" | "ntelm"
15344 | "lookup_assoc" | "lkpa" | "scanl" | "scanr" | "unfoldr" | "unfr"
15345 | "find_map" | "fndm" | "filter_map" | "fltm" | "fold_right" | "fldr"
15347 | "partition_either" | "peith" | "try_fold" | "tfld"
15348 | "map_while" | "mapw" | "inspect" | "insp"
15349 | "tally_by" | "talb" | "sole" | "chunk_while" | "chkw" | "count_while" | "cntw"
15351 | "insert_at" | "insa" | "delete_at" | "dela" | "update_at" | "upda"
15353 | "split_on" | "spon" | "words_from" | "wfrm" | "unwords" | "unwds"
15354 | "lines_from" | "lfrm" | "unlines" | "unlns"
15355 | "window_n" | "winn" | "adjacent_pairs" | "adjp"
15356 | "zip_all" | "zall" | "unzip_pairs" | "uzp"
15357 | "interpose" | "ipos" | "partition_n" | "partn"
15358 | "map_indexed" | "mapi" | "reduce_indexed" | "redi" | "filter_indexed" | "flti"
15359 | "group_by_fn" | "gbf" | "index_by" | "idxb" | "associate" | "assoc_fn"
15360 | "combinations_rep" | "combrep" | "inits" | "tails" | "subsequences" | "subseqs"
15362 | "nub_by" | "nubb" | "slice_when" | "slcw" | "slice_before" | "slcb" | "slice_after" | "slca"
15363 | "each_with_object" | "ewo" | "reduce_right" | "redr"
15364 | "is_sorted_by" | "issrtb" | "intersperse_with" | "ispw"
15365 | "running_reduce" | "runred" | "windowed_circular" | "wincirc"
15366 | "distinct_by" | "distb" | "average" | "mean" | "copy_within" | "cpyw"
15367 | "and_list" | "andl" | "or_list" | "orl" | "concat_map" | "cmap"
15368 | "elem_index" | "elidx" | "elem_indices" | "elidxs" | "find_indices" | "fndidxs"
15369 | "delete_first" | "delfst" | "delete_by" | "delby" | "insert_sorted" | "inssrt"
15370 | "union_list" | "unionl" | "intersect_list" | "intl"
15371 | "maximum_by" | "maxby" | "minimum_by" | "minby" | "batched" | "btch"
15372 | "match_all" | "mall" | "capture_groups" | "capg" | "is_match" | "ism"
15374 | "split_regex" | "splre" | "replace_regex" | "replre"
15375 | "is_ascii" | "isasc" | "to_ascii" | "toasc"
15376 | "char_at" | "chat" | "code_point_at" | "cpat" | "from_code_point" | "fcp"
15377 | "normalize_spaces" | "nrmsp" | "remove_whitespace" | "rmws"
15378 | "pluralize" | "plur" | "ordinalize" | "ordn"
15379 | "parse_int" | "pint" | "parse_float" | "pflt" | "parse_bool" | "pbool"
15380 | "levenshtein" | "lev" | "soundex" | "sdx" | "similarity" | "sim"
15381 | "common_prefix" | "cpfx" | "common_suffix" | "csfx"
15382 | "wrap_text" | "wrpt" | "dedent" | "ddt" | "indent" | "idt"
15383 | "lerp" | "inv_lerp" | "ilerp" | "smoothstep" | "smst" | "remap"
15385 | "dotp" | "cross_product" | "crossp"
15386 | "matrix_mul" | "matmul" | "mm"
15387 | "magnitude" | "mag" | "normalize_vec" | "nrmv"
15388 | "distance" | "dist" | "mdist"
15389 | "covariance" | "cov" | "correlation" | "corr"
15390 | "iqr" | "quantile" | "qntl" | "quantiles" | "qntls"
15391 | "lsp_completion_words" | "lsp_words"
15392 | "doctor" | "health"
15393 | "clamp_int" | "clpi"
15394 | "in_range" | "inrng" | "wrap_range" | "wrprng"
15395 | "sum_squares" | "sumsq" | "rms" | "cumsum" | "csum" | "cumprod" | "cprod_acc" | "diff"
15396 | "add_days" | "addd" | "add_hours" | "addh" | "add_minutes" | "addm"
15398 | "diff_days" | "diffd" | "diff_hours" | "diffh"
15399 | "start_of_day" | "sod" | "end_of_day" | "eod"
15400 | "start_of_hour" | "soh" | "start_of_minute" | "som"
15401 | "urle" | "urld"
15403 | "html_encode" | "htmle" | "html_decode" | "htmld"
15404 | "adler32" | "adl32" | "fnv1a" | "djb2"
15405 | "is_credit_card" | "iscc" | "is_isbn10" | "isbn10" | "is_isbn13" | "isbn13"
15407 | "is_iban" | "isiban" | "is_hex_str" | "ishex" | "is_binary_str" | "isbin"
15408 | "is_octal_str" | "isoct" | "is_json" | "isjson" | "is_base64" | "isb64"
15409 | "is_semver" | "issv" | "is_slug" | "isslug" | "slugify" | "slug"
15410 | "mode_stat" | "mstat" | "sampn" | "weighted_sample" | "wsamp"
15412 | "shuffle_arr" | "shuf" | "argmax" | "amax" | "argmin" | "amin"
15413 | "argsort" | "asrt" | "rank" | "rnk" | "dense_rank" | "drnk"
15414 | "partition_point" | "ppt" | "lower_bound" | "lbound"
15415 | "upper_bound" | "ubound" | "equal_range" | "eqrng"
15416 | "matrix_add" | "madd" | "matrix_sub" | "msub" | "matrix_mult" | "mmult"
15418 | "matrix_scalar" | "mscal" | "matrix_identity" | "mident"
15419 | "matrix_zeros" | "mzeros" | "matrix_ones" | "mones"
15420 | "matrix_diag" | "mdiag" | "matrix_trace" | "mtrace"
15421 | "matrix_row" | "mrow" | "matrix_col" | "mcol"
15422 | "matrix_shape" | "mshape" | "matrix_det" | "mdet"
15423 | "matrix_scale" | "mat_scale" | "diagonal" | "diag"
15424 | "topological_sort" | "toposort" | "bfs_traverse" | "bfs"
15426 | "dfs_traverse" | "dfs" | "shortest_path_bfs" | "spbfs"
15427 | "connected_components_graph" | "ccgraph"
15428 | "has_cycle_graph" | "hascyc" | "is_bipartite_graph" | "isbip"
15429 | "is_ipv4_addr" | "isip4" | "is_ipv6_addr" | "isip6"
15431 | "is_mac_addr" | "ismac" | "is_port_num" | "isport"
15432 | "is_hostname_valid" | "ishost"
15433 | "is_iso_date" | "isisodt" | "is_iso_time" | "isisotm"
15434 | "is_iso_datetime" | "isisodtm"
15435 | "is_phone_num" | "isphone" | "is_us_zip" | "iszip"
15436 | "word_wrap_text" | "wwrap" | "center_text" | "ctxt"
15438 | "ljust_text" | "ljt" | "rjust_text" | "rjt" | "zfill_num" | "zfill"
15439 | "remove_all_str" | "rmall" | "replace_n_times" | "repln"
15440 | "find_all_indices" | "fndalli"
15441 | "text_between" | "txbtwn" | "text_before" | "txbef" | "text_after" | "txaft"
15442 | "text_before_last" | "txbefl" | "text_after_last" | "txaftl"
15443 | "is_even_num" | "iseven" | "is_odd_num" | "isodd"
15445 | "is_positive_num" | "ispos" | "is_negative_num" | "isneg"
15446 | "is_zero_num" | "iszero" | "is_whole_num" | "iswhole"
15447 | "log_with_base" | "logb" | "nth_root_of" | "nroot"
15448 | "frac_part" | "fracp" | "reciprocal_of" | "recip"
15449 | "copy_sign" | "cpsgn" | "fused_mul_add" | "fmadd"
15450 | "floor_mod" | "fmod" | "floor_div_op" | "fdivop"
15451 | "signum_of" | "sgnum" | "midpoint_of" | "midpt"
15452 | "longest_run" | "lrun" | "longest_increasing" | "linc"
15454 | "longest_decreasing" | "ldec" | "max_sum_subarray" | "maxsub"
15455 | "majority_element" | "majority" | "kth_largest" | "kthl"
15456 | "kth_smallest" | "kths" | "count_inversions" | "cinv"
15457 | "is_monotonic" | "ismono" | "equilibrium_index" | "eqidx"
15458 | "jaccard_index" | "jaccard" | "dice_coefficient" | "dicecoef"
15460 | "overlap_coefficient" | "overlapcoef"
15461 | "power_set" | "powerset" | "cartesian_power" | "cartpow"
15462 | "is_isogram" | "isiso" | "is_heterogram" | "ishet"
15464 | "hamdist" | "jaro_similarity" | "jarosim"
15465 | "longest_common_substring" | "lcsub"
15466 | "longest_common_subsequence" | "lcseq"
15467 | "count_words" | "wcount" | "count_lines" | "lcount"
15468 | "count_chars" | "ccount" | "count_bytes" | "bcount"
15469 | "binomial" | "binom" | "catalan" | "catn" | "pascal_row" | "pascrow"
15471 | "is_coprime" | "iscopr" | "euler_totient" | "etot"
15472 | "mobius" | "mob" | "is_squarefree" | "issqfr"
15473 | "digital_root" | "digroot" | "is_narcissistic" | "isnarc"
15474 | "is_harshad" | "isharsh" | "is_kaprekar" | "iskap"
15475 | "day_of_year" | "doy" | "week_of_year" | "woy"
15477 | "days_in_month_fn" | "daysinmo" | "is_valid_date" | "isvdate"
15478 | "age_in_years" | "ageyrs"
15479 | "when_true" | "when_false" | "if_else" | "clamp_fn"
15482 | "attempt" | "try_fn" | "safe_div" | "safe_mod" | "safe_sqrt" | "safe_log"
15483 | "juxt2" | "juxt3" | "tap_val" | "debug_val" | "converge"
15484 | "iterate_n" | "unfold" | "arity_of" | "is_callable"
15485 | "coalesce" | "default_to" | "fallback"
15486 | "apply_list" | "zip_apply" | "scan"
15487 | "keep_if" | "reject_if" | "group_consecutive"
15488 | "after_n" | "before_n" | "clamp_list" | "normalize_list"
15489
15490 | "matrix_multiply" | "mat_mul"
15494 | "identity_matrix" | "eye" | "zeros_matrix" | "zeros" | "ones_matrix" | "ones"
15495
15496
15497
15498 | "vec_normalize" | "unit_vec" | "vec_add" | "vec_sub" | "vec_scale"
15499 | "linspace" | "arange"
15500 | "re_test" | "re_find_all" | "re_groups" | "re_escape"
15502 | "re_split_limit" | "glob_to_regex" | "is_regex_valid"
15503 | "cwd" | "pwd_str" | "cpu_count" | "is_root" | "uptime_secs"
15505 | "env_pairs" | "env_set" | "env_remove" | "hostname_str" | "is_tty" | "signal_name"
15506 | "stack_new" | "queue_new" | "lru_new"
15508 | "counter" | "counter_most_common" | "defaultdict" | "ordered_set"
15509 | "bitset_new" | "bitset_set" | "bitset_test" | "bitset_clear"
15510 | "abs_ceil" | "abs_each" | "abs_floor" | "ceil_each" | "dec_each"
15512 | "double_each" | "floor_each" | "half_each" | "inc_each" | "length_each"
15513 | "negate_each" | "not_each" | "offset_each" | "reverse_each" | "round_each"
15514 | "scale_each" | "sqrt_each" | "square_each" | "to_float_each" | "to_int_each"
15515 | "trim_each" | "type_each" | "upcase_each" | "downcase_each" | "bool_each"
15516 | "avogadro" | "boltzmann" | "golden_ratio" | "gravity" | "ln10" | "ln2"
15518 | "planck" | "speed_of_light" | "sqrt2"
15519 | "bmi_calc" | "compound_interest" | "dew_point" | "discount_amount"
15521 | "force_mass_acc" | "freq_wavelength" | "future_value" | "haversine"
15522 | "heat_index" | "kinetic_energy" | "margin_price" | "markup_price"
15523 | "mortgage_payment" | "ohms_law_i" | "ohms_law_r" | "ohms_law_v"
15524 | "potential_energy" | "present_value" | "simple_interest" | "speed_distance_time"
15525 | "tax_amount" | "tip_amount" | "wavelength_freq" | "wind_chill"
15526 | "angle_between_deg" | "approx_eq" | "chebyshev_distance" | "copysign"
15528 | "cube_root" | "entropy" | "float_bits" | "fma"
15529 | "int_bits" | "jaccard_similarity" | "log_base" | "mae" | "mse" | "nth_root"
15530 | "r_squared" | "reciprocal" | "relu" | "rmse" | "rotate_point" | "round_to"
15531 | "signum" | "square_root"
15532 | "cubes_seq" | "fibonacci_seq" | "powers_of_seq" | "primes_seq"
15534 | "squares_seq" | "triangular_seq"
15535 | "alternate_case" | "angle_bracket" | "bracket" | "byte_length"
15537 | "bytes_to_hex_str" | "camel_words" | "char_length" | "chars_to_string"
15538 | "chomp_str" | "chop_str" | "filter_chars" | "from_csv_line" | "hex_to_bytes"
15539 | "insert_str" | "intersperse_char" | "ljust" | "map_chars" | "mirror_string"
15540 | "normalize_whitespace" | "only_alnum" | "only_alpha" | "only_ascii"
15541 | "only_digits" | "parenthesize" | "remove_str" | "repeat_string" | "rjust"
15542 | "sentence_case" | "string_count" | "string_sort" | "string_to_chars"
15543 | "string_unique_chars" | "substring" | "to_csv_line" | "trim_left" | "trim_right"
15544 | "xor_strings"
15545 | "adjacent_difference" | "append_elem" | "consecutive_pairs" | "contains_elem"
15547 | "count_elem" | "drop_every" | "duplicate_count" | "elem_at" | "find_first"
15548 | "first_elem" | "flatten_once" | "fold_left" | "from_digits" | "from_pairs"
15549 | "group_by_size" | "hash_from_list"
15550 | "hash_merge_deep" | "hash_to_list" | "hash_zip" | "head_n" | "histogram_bins"
15551 | "index_of_elem" | "init_list" | "interleave_lists" | "last_elem" | "least_common"
15552 | "list_compact" | "list_eq" | "list_flatten_deep" | "max_list" | "mean_list"
15553 | "min_list" | "mode_list" | "most_common" | "partition_two" | "prefix_sums"
15554 | "prepend" | "product_list" | "remove_at" | "remove_elem" | "remove_first_elem"
15555 | "repeat_elem" | "running_max" | "running_min" | "sample_one" | "scan_left"
15556 | "second_elem" | "span" | "suffix_sums" | "sum_list" | "tail_n" | "take_every"
15557 | "third_elem" | "to_array" | "to_pairs" | "trimmed_mean" | "unique_count_of"
15558 | "wrap_index" | "digits_of"
15559 | "all_match" | "any_match" | "is_between" | "is_blank_or_nil" | "is_divisible_by"
15561 | "is_email" | "is_even" | "is_falsy" | "is_fibonacci" | "is_hex_color"
15562 | "is_in_range" | "is_ipv4" | "is_multiple_of" | "is_negative" | "is_nil"
15563 | "is_nonzero" | "is_odd" | "is_perfect_square" | "is_positive" | "is_power_of"
15564 | "is_prefix" | "is_present" | "is_strictly_decreasing" | "is_strictly_increasing"
15565 | "is_suffix" | "is_triangular" | "is_truthy" | "is_url" | "is_whole" | "is_zero"
15566 | "count_digits" | "count_letters" | "count_lower" | "count_match"
15568 | "count_punctuation" | "count_spaces" | "count_upper" | "defined_count"
15569 | "empty_count" | "falsy_count" | "nonempty_count" | "numeric_count"
15570 | "truthy_count" | "undef_count"
15571 | "assert_type" | "between" | "clamp_each" | "die_if" | "die_unless"
15573 | "join_colons" | "join_commas" | "join_dashes" | "join_dots" | "join_lines"
15574 | "join_pipes" | "join_slashes" | "join_spaces" | "join_tabs" | "measure"
15575 | "max_float" | "min_float" | "noop_val" | "nop" | "pass" | "pred" | "succ"
15576 | "tap_debug" | "to_bool" | "to_float" | "to_int" | "to_string" | "void"
15577 | "range_exclusive" | "range_inclusive"
15578 | "aliquot_sum" | "autocorrelation" | "bell_number" | "cagr" | "coeff_of_variation"
15580 | "collatz_length" | "collatz_sequence" | "convolution"
15581 | "depreciation_double" | "depreciation_linear" | "discount" | "divisors"
15582 | "epsilon" | "euler_number" | "exponential_moving_average"
15583 | "f64_max" | "f64_min" | "fft_magnitude" | "goldbach" | "i64_max" | "i64_min"
15584 | "kurtosis" | "linear_regression" | "look_and_say" | "lucas" | "luhn_check"
15585 | "mean_absolute_error" | "mean_squared_error" | "median_absolute_deviation"
15586 | "minkowski_distance" | "moving_average" | "multinomial" | "neg_inf" | "npv"
15587 | "num_divisors" | "partition_number" | "pascals_triangle" | "skewness"
15588 | "standard_error" | "subfactorial" | "sum_divisors" | "totient_sum"
15589 | "tribonacci" | "weighted_mean" | "winsorize"
15590 | "chi_square_stat" | "describe" | "five_number_summary"
15592 | "gini" | "gini_coefficient" | "lorenz_curve" | "outliers_iqr"
15593 | "percentile_rank" | "quartiles" | "sample_stddev" | "sample_variance"
15594 | "spearman_correlation" | "t_test_one_sample" | "t_test_two_sample"
15595 | "z_score" | "z_scores"
15596 | "abundant_numbers" | "deficient_numbers" | "is_abundant" | "is_deficient"
15598 | "is_pentagonal" | "is_perfect" | "is_smith" | "next_prime" | "nth_prime"
15599 | "pentagonal_number" | "perfect_numbers" | "prev_prime" | "prime_factors"
15600 | "prime_pi" | "primes_up_to" | "triangular_number" | "twin_primes"
15601 | "area_circle" | "area_ellipse" | "area_rectangle" | "area_trapezoid" | "area_triangle"
15603 | "bearing" | "circumference" | "cone_volume" | "cylinder_volume" | "heron_area"
15604 | "midpoint" | "perimeter_rectangle" | "perimeter_triangle" | "point_distance"
15605 | "polygon_area" | "slope" | "sphere_surface" | "sphere_volume" | "triangle_hypotenuse"
15606 | "angle_between" | "arc_length" | "bounding_box" | "centroid"
15608 | "circle_from_three_points" | "circ3" | "convex_hull" | "ellipse_perimeter" | "ellper"
15609 | "frustum_volume" | "haversine_distance" | "line_intersection"
15610 | "point_in_polygon" | "pip" | "polygon_perimeter" | "polyper" | "pyramid_volume"
15611 | "reflect_point" | "scale_point" | "sector_area"
15612 | "torus_surface" | "torus_volume" | "translate_point"
15613 | "vector_angle" | "vector_cross" | "vector_dot" | "vector_magnitude" | "vector_normalize"
15614 | "avogadro_number" | "boltzmann_constant" | "electron_mass" | "elementary_charge"
15616 | "gravitational_constant" | "phi" | "pi" | "PI" | "planck_constant"
15617 | "proton_mass" | "sol" | "tau" | "TAU" | "E"
15618 | "bac_estimate" | "bmi" | "break_even" | "margin" | "markup" | "roi" | "tax" | "tip"
15620 | "amortization_schedule" | "black_scholes_call" | "black_scholes_put"
15622 | "bond_price" | "bond_yield" | "capm" | "continuous_compound" | "ccomp"
15623 | "discounted_payback" | "duration" | "irr"
15624 | "max_drawdown" | "mdd" | "modified_duration" | "mod_dur" | "nper" | "num_periods" | "payback_period"
15625 | "pmt" | "pv" | "rule_of_72" | "sharpe_ratio" | "sortino_ratio"
15626 | "wacc" | "xirr"
15627 | "acronym" | "atbash" | "bigrams" | "camel_to_snake" | "char_frequencies"
15629 | "chunk_string" | "collapse_whitespace" | "dedent_text" | "indent_text"
15630 | "initials" | "leetspeak" | "mask_string" | "ngrams" | "pig_latin"
15631 | "remove_consonants" | "remove_vowels" | "reverse_each_word" | "snake_to_camel"
15632 | "sort_words" | "string_distance" | "string_multiply" | "strip_html"
15633 | "trigrams" | "unique_words" | "word_frequencies" | "zalgo"
15634 | "braille_encode" | "double_metaphone" | "metaphone" | "morse_decode"
15636 | "morse_encode" | "nato_phonetic" | "phonetic_digit" | "subscript" | "superscript"
15637 | "to_emoji_num"
15638 | "int_to_roman" | "roman_add" | "roman_numeral_list" | "roman_to_int"
15640 | "base_convert" | "binary_to_gray" | "gray_code_sequence" | "gray_to_binary"
15642 | "ansi_256" | "ansi_truecolor" | "color_blend" | "color_complement"
15644 | "color_darken" | "color_distance" | "color_grayscale" | "color_invert"
15645 | "color_lighten" | "hsl_to_rgb" | "hsv_to_rgb" | "random_color"
15646 | "rgb_to_hsl" | "rgb_to_hsv"
15647 | "matrix_flatten" | "matrix_from_rows" | "matrix_hadamard" | "matrix_inverse"
15649 | "matrix_map" | "matrix_max" | "matrix_min" | "matrix_power" | "matrix_sum"
15650 | "matrix_transpose"
15651 | "binary_insert" | "bucket" | "clamp_array" | "group_consecutive_by"
15653 | "histogram" | "merge_sorted" | "next_permutation" | "normalize_array"
15654 | "normalize_range" | "peak_detect" | "range_compress" | "range_expand"
15655 | "reservoir_sample" | "run_length_decode_str" | "run_length_encode_str"
15656 | "zero_crossings"
15657 | "apply_window" | "bandpass_filter" | "cross_correlation" | "dft"
15659 | "downsample" | "decimate" | "energy" | "envelope" | "hilbert_env" | "highpass_filter" | "idft"
15660 | "lowpass_filter" | "median_filter" | "normalize_signal" | "phase_spectrum"
15661 | "power_spectrum" | "psd" | "resample" | "spectral_centroid" | "spectrogram" | "stft" | "upsample" | "interpolate"
15662 | "window_blackman" | "window_hamming" | "window_hann" | "window_kaiser"
15663 | "is_anagram" | "is_balanced_parens" | "is_control" | "is_numeric_string"
15665 | "is_pangram" | "is_printable" | "is_valid_cidr" | "is_valid_cron"
15666 | "is_valid_hex_color" | "is_valid_latitude" | "is_valid_longitude" | "is_valid_mime"
15667 | "eval_rpn" | "fizzbuzz" | "game_of_life_step" | "mandelbrot_char"
15669 | "sierpinski" | "tower_of_hanoi" | "truth_table"
15670 | "byte_size" | "degrees_to_compass" | "to_string_val" | "type_of"
15672 | "quadratic_roots" | "quadratic_discriminant" | "arithmetic_series"
15674 | "geometric_series" | "stirling_approx"
15675 | "double_factorial" | "rising_factorial" | "falling_factorial"
15676 | "gamma_approx" | "erf_approx" | "normal_pdf" | "normal_cdf"
15677 | "poisson_pmf" | "exponential_pdf" | "inverse_lerp"
15678 | "map_range"
15679 | "momentum" | "impulse" | "work" | "power_phys" | "torque" | "angular_velocity"
15681 | "centripetal_force" | "escape_velocity" | "orbital_velocity" | "orbital_period"
15682 | "gravitational_force" | "coulomb_force" | "electric_field" | "capacitance"
15683 | "capacitor_energy" | "inductor_energy" | "resonant_frequency"
15684 | "rc_time_constant" | "rl_time_constant" | "impedance_rlc"
15685 | "relativistic_mass" | "lorentz_factor" | "time_dilation" | "length_contraction"
15686 | "relativistic_energy" | "rest_energy" | "de_broglie_wavelength"
15687 | "photon_energy" | "photon_energy_wavelength" | "schwarzschild_radius"
15688 | "stefan_boltzmann" | "wien_displacement" | "ideal_gas_pressure" | "ideal_gas_volume"
15689 | "projectile_range" | "projectile_max_height" | "projectile_time"
15690 | "spring_force" | "spring_energy" | "pendulum_period" | "doppler_frequency"
15691 | "decibel_ratio" | "snells_law" | "brewster_angle" | "critical_angle"
15692 | "lens_power" | "thin_lens" | "magnification_lens"
15693 | "euler_mascheroni" | "apery_constant" | "feigenbaum_delta" | "feigenbaum_alpha"
15695 | "catalan_constant" | "khinchin_constant" | "glaisher_constant"
15696 | "plastic_number" | "silver_ratio" | "supergolden_ratio"
15697 | "vacuum_permittivity" | "vacuum_permeability" | "coulomb_constant"
15699 | "fine_structure_constant" | "rydberg_constant" | "bohr_radius"
15700 | "bohr_magneton" | "nuclear_magneton" | "stefan_boltzmann_constant"
15701 | "wien_constant" | "gas_constant" | "faraday_constant" | "neutron_mass"
15702 | "atomic_mass_unit" | "earth_mass" | "earth_radius" | "sun_mass" | "sun_radius"
15703 | "astronomical_unit" | "light_year" | "parsec" | "hubble_constant"
15704 | "planck_length" | "planck_time" | "planck_mass" | "planck_temperature"
15705 | "matrix_solve" | "msolve" | "solve"
15707 | "matrix_lu" | "mlu" | "matrix_qr" | "mqr"
15708 | "matrix_eigenvalues" | "meig" | "eigenvalues" | "eig"
15709 | "matrix_norm" | "mnorm" | "matrix_cond" | "mcond" | "cond"
15710 | "matrix_pinv" | "mpinv" | "pinv"
15711 | "matrix_cholesky" | "mchol" | "cholesky"
15712 | "matrix_det_general" | "mdetg" | "det"
15713 | "welch_ttest" | "welcht" | "paired_ttest" | "pairedt"
15715 | "cohen_d" | "cohend" | "anova_oneway" | "anova" | "anova1"
15716 | "spearman_corr" | "rho" | "kendall_tau" | "kendall" | "ktau"
15717 | "confidence_interval" | "ci"
15718 | "beta_pdf" | "betapdf" | "gamma_pdf" | "gammapdf"
15720 | "chi2_pdf" | "chi2pdf" | "chi_squared_pdf"
15721 | "t_pdf" | "tpdf" | "student_pdf"
15722 | "f_pdf" | "fpdf" | "fisher_pdf"
15723 | "lognormal_pdf" | "lnormpdf" | "weibull_pdf" | "weibpdf"
15724 | "cauchy_pdf" | "cauchypdf" | "laplace_pdf" | "laplacepdf"
15725 | "pareto_pdf" | "paretopdf"
15726 | "lagrange_interp" | "lagrange" | "linterp"
15728 | "cubic_spline" | "cspline" | "spline"
15729 | "poly_eval" | "polyval" | "polynomial_fit" | "polyfit"
15730 | "trapz" | "trapezoid" | "simpson" | "simps"
15732 | "numerical_diff" | "numdiff" | "diff_array"
15733 | "cumtrapz" | "cumulative_trapz"
15734 | "bisection" | "bisect" | "newton_method" | "newton" | "newton_raphson"
15736 | "golden_section" | "golden" | "gss"
15737 | "rk4" | "runge_kutta" | "rk4_ode" | "euler_ode" | "euler_method"
15739 | "dijkstra" | "shortest_path" | "bellman_ford" | "bellmanford"
15741 | "floyd_warshall" | "floydwarshall" | "apsp"
15742 | "prim_mst" | "mst" | "prim"
15743 | "cot" | "sec" | "csc" | "acot" | "asec" | "acsc" | "sinc" | "versin" | "versine"
15745 | "leaky_relu" | "lrelu" | "elu" | "selu" | "gelu"
15747 | "silu" | "swish" | "mish" | "softplus"
15748 | "hard_sigmoid" | "hardsigmoid" | "hard_swish" | "hardswish"
15749 | "bessel_j0" | "j0" | "bessel_j1" | "j1"
15751 | "lambert_w" | "lambertw" | "productlog"
15752 | "bessel_j" | "bessel_y" | "bessel_i" | "bessel_k"
15754 | "hankel_h1" | "hankel_h2" | "bessel_j_zero"
15755 | "airy_ai" | "airy_bi" | "airy_ai_prime" | "airy_bi_prime"
15756 | "spherical_bessel_j" | "spherical_bessel_y"
15757 | "struve_h" | "struve_l" | "kelvin_ber" | "kelvin_bei"
15758 | "legendre_p" | "legendre_q" | "assoc_legendre_p"
15760 | "hermite_h" | "hermite_he" | "laguerre_l" | "assoc_laguerre_l"
15761 | "jacobi_p" | "gegenbauer_c" | "chebyshev_t" | "chebyshev_u"
15762 | "spherical_harmonic_y" | "zernike_r"
15763 | "elliptic_k" | "elliptic_e" | "elliptic_pi" | "elliptic_f"
15765 | "elliptic_e_inc" | "elliptic_pi_inc"
15766 | "carlson_rf" | "carlson_rd" | "carlson_rj"
15767 | "jacobi_sn" | "jacobi_cn" | "jacobi_dn" | "jacobi_am"
15768 | "elliptic_theta"
15769 | "weierstrass_p" | "weierstrass_zeta" | "weierstrass_sigma"
15770 | "zeta" | "riemann_zeta" | "hurwitz_zeta"
15772 | "polylog" | "dilog" | "lerch_phi"
15773 | "riemann_siegel_z" | "riemann_siegel_theta"
15774 | "dirichlet_eta" | "dirichlet_beta"
15775 | "hypergeometric_2f1" | "hyper_2f1"
15777 | "hypergeometric_1f1" | "hyper_1f1" | "kummer_m"
15778 | "hypergeometric_0f1" | "hyper_0f1"
15779 | "hypergeometric_pfq" | "hyper_pfq"
15780 | "hypergeometric_u" | "tricomi_u"
15781 | "dedekind_eta" | "klein_j" | "klein_invariant_j"
15783 | "modular_lambda" | "ramanujan_tau"
15784 | "sin_integral" | "si_int" | "cos_integral" | "ci_int"
15786 | "sinh_integral" | "shi_int" | "cosh_integral" | "chi_int"
15787 | "exp_integral_e" | "ei_n" | "exp_integral_ei" | "ei_int"
15788 | "log_integral" | "li_int" | "fresnel_s" | "fresnel_c"
15789 | "jacobi_symbol" | "kronecker_symbol"
15791 | "primitive_root" | "multiplicative_order"
15792 | "mangoldt_lambda" | "von_mangoldt" | "carmichael_lambda"
15793 | "squares_r" | "thue_morse" | "rudin_shapiro"
15794 | "farey_sequence" | "farey"
15795 | "frobenius_number" | "frobenius_solve" | "stern_brocot"
15796 | "stirling_s1" | "stirling_first" | "bell_polynomial_b" | "bell_y"
15798 | "clebsch_gordan" | "three_j_symbol" | "wigner_3j"
15799 | "six_j_symbol" | "wigner_6j" | "nine_j_symbol" | "wigner_9j"
15800 | "debruijn_sequence" | "debruijn" | "wigner_d"
15801 | "q_pochhammer" | "q_factorial" | "q_binomial"
15803 | "q_hypergeometric_pfq"
15804 | "mittag_leffler_e" | "mittag_leffler"
15805 | "coulomb_wave_f" | "coulomb_wave_g"
15806 | "inverse_erf" | "erfinv" | "inverse_erfc" | "erfcinv"
15808 | "inverse_gamma_regularized" | "gamma_lr_inv"
15809 | "inverse_beta_regularized" | "beta_reg_inv"
15810 | "inverse_jacobi_sn"
15811 | "dirac_delta" | "heaviside_theta" | "heaviside"
15813 | "unit_box" | "unit_triangle"
15814 | "square_wave" | "triangle_wave" | "sawtooth_wave" | "dirac_comb"
15815 | "liouville_lambda" | "jordan_totient" | "ramanujan_sum"
15817 | "cyclotomic_polynomial" | "cyclotomic" | "legendre_symbol"
15818 | "pythagorean_triple_q" | "gen_pythagorean_triple"
15819 | "sophie_germain_q" | "mersenne_q"
15820 | "lucas_lehmer_test" | "lucas_lehmer"
15821 | "continued_fraction" | "from_continued_fraction" | "convergents"
15822 | "best_rational_approximation" | "best_rational"
15823 | "motzkin_number" | "motzkin"
15825 | "narayana_number" | "narayana"
15826 | "delannoy_number" | "delannoy"
15827 | "schroder_number" | "schroder" | "large_schroder"
15828 | "small_schroder_number" | "small_schroder"
15829 | "eulerian_number"
15830 | "bernoulli_polynomial" | "euler_polynomial"
15831 | "pell_number" | "pell" | "pell_lucas_number" | "pell_lucas"
15832 | "perrin_number" | "perrin" | "padovan_number" | "padovan"
15833 | "kronecker_product" | "tensor_product" | "tensor_contract"
15835 | "matrix_rank" | "mrank"
15836 | "companion_matrix" | "companion"
15837 | "characteristic_polynomial" | "charpoly"
15838 | "singular_values" | "svals"
15839 | "nullspace" | "null_space" | "kernel"
15840 | "polynomial_gcd" | "polygcd"
15842 | "polynomial_quotient" | "polyquot"
15843 | "polynomial_remainder" | "polyrem"
15844 | "polynomial_resultant" | "resultant"
15845 | "polynomial_discriminant" | "discriminant"
15846 | "polynomial_roots" | "polyroots"
15847 | "gumbel_pdf" | "gumbel_cdf" | "gumbel_quantile"
15849 | "frechet_pdf" | "frechet_cdf" | "frechet_quantile"
15850 | "logistic_pdf" | "logistic_cdf" | "logistic_quantile"
15851 | "rayleigh_pdf" | "rayleigh_cdf" | "rayleigh_quantile"
15852 | "inverse_gamma_pdf" | "inverse_gamma_cdf" | "inverse_gamma_quantile"
15853 | "kumaraswamy_pdf" | "kumaraswamy_cdf" | "kumaraswamy_quantile"
15854 | "mathieu_a" | "mathieu_characteristic_a"
15856 | "mathieu_ce" | "mathieu_se"
15857 | "heun_g"
15859 | "haar_transform" | "haar" | "haar_inverse" | "ihaar"
15861 | "daubechies_db4" | "db4" | "daubechies_db4_inverse" | "idb4"
15862 | "topo_sort_adj"
15864 | "scc_tarjan" | "tarjan_scc" | "strongly_connected"
15865 | "bipartite_q" | "is_bipartite"
15866 | "max_flow_edmonds_karp" | "max_flow" | "edmonds_karp"
15867 | "min_cut" | "eccentricity"
15868 | "graph_diameter" | "graph_radius"
15869 | "stieltjes_constant" | "stieltjes"
15871 | "gauss_sum" | "kloosterman_sum"
15872 | "eta_quotient" | "root_approximant"
15873 | "numerical_gradient" | "ngrad"
15875 | "numerical_jacobian" | "njac"
15876 | "numerical_hessian" | "nhess"
15877 | "numerical_divergence" | "ndiv"
15878 | "numerical_curl" | "ncurl"
15879 | "numerical_laplacian" | "nlap"
15880 | "nelder_mead" | "simplex_min"
15882 | "gradient_descent" | "gd_min"
15883 | "bfgs_minimize" | "bfgs"
15884 | "levenberg_marquardt" | "lev_marq" | "lm_min"
15885 | "conjugate_gradient" | "cg_solve"
15886 | "least_squares" | "lstsq"
15887 | "romberg" | "romberg_int"
15889 | "gauss_legendre_quad" | "glquad" | "gl_quad"
15890 | "monte_carlo_integrate" | "mc_int"
15891 | "adaptive_simpson" | "asimp"
15892 | "lu_decompose" | "ludec"
15894 | "qr_decompose" | "qrdec"
15895 | "householder_reflector" | "householder"
15896 | "givens_rotation" | "givens"
15897 | "forward_substitute" | "fwdsub"
15898 | "back_substitute" | "backsub"
15899 | "hessenberg_reduce" | "hessen"
15900 | "poly_derivative" | "polyder"
15902 | "poly_integrate" | "polyint"
15903 | "poly_compose" | "poly_eval_horner" | "horner"
15904 | "pade_approximant" | "pade"
15905 | "quat_mul" | "quat_conj" | "quat_norm" | "quat_inv"
15907 | "quat_from_axis_angle" | "axis_angle_to_quat"
15908 | "quat_to_axis_angle"
15909 | "quat_to_matrix" | "quat_from_matrix" | "matrix_to_quat"
15910 | "quat_slerp" | "slerp"
15911 | "euler_zyx_to_matrix" | "matrix_to_euler_zyx"
15912 | "rotate_3d_vec"
15913 | "kl_divergence" | "kl_div"
15915 | "js_divergence" | "js_div"
15916 | "mutual_information" | "mi"
15917 | "cross_entropy_arr" | "cross_entropy_dist"
15918 | "renyi_entropy" | "tsallis_entropy"
15919 | "pauli_x" | "pauli_y" | "pauli_z"
15921 | "pauli_id" | "pauli_i" | "pauli_identity"
15922 | "ket_bra" | "density_matrix" | "expectation_value" | "expval"
15923 | "commutator" | "anticommutator"
15924 | "partial_trace" | "ptrace"
15925 | "von_neumann_entropy" | "vn_entropy"
15926 | "bose_einstein" | "fermi_dirac"
15928 | "maxwell_boltzmann_speed" | "mb_speed"
15929 | "partition_function" | "z_partition"
15930 | "helmholtz_free_energy" | "free_energy_f"
15931 | "boltzmann_factor"
15932 | "einstein_specific_heat" | "einstein_cv"
15933 | "fresnel_reflection_te" | "fresnel_reflection_tm"
15935 | "fresnel_transmission_te" | "fresnel_transmission_tm"
15936 | "abcd_thin_lens" | "abcd_free_space"
15937 | "gaussian_beam_q"
15938 | "kepler_solve"
15940 | "true_to_eccentric" | "eccentric_to_mean"
15941 | "julian_date" | "j_date"
15942 | "jd_to_gregorian" | "jd_to_date"
15943 | "sidereal_time_gmst" | "gmst"
15944 | "vis_viva" | "orbital_period_kepler"
15945 | "orbital_elements_to_state" | "elem_to_state"
15946 | "kalman_step" | "kalman_filter"
15948 | "exponential_smoothing" | "exp_smooth"
15949 | "holt_winters" | "arma_yw_fit" | "ar_yw"
15950 | "pagerank" | "betweenness_centrality" | "closeness_centrality"
15952 | "eigenvector_centrality" | "degree_centrality" | "triangle_count"
15953 | "rgumbel" | "rfrechet" | "rrayleigh"
15955 | "rlogistic" | "rkumaraswamy" | "rinverse_gamma" | "rinvgamma"
15956 | "graham_scan" | "convex_hull_2d"
15958 | "line_line_intersect_2d" | "ll_intersect_2d"
15959 | "point_segment_distance" | "p_seg_dist"
15960 | "forward_diff" | "fdiff"
15962 | "forward_diff_grad" | "fdiff_grad"
15963 | "bartlett_test" | "levene_test"
15965 | "fishers_exact_test_2x2" | "fishers_exact"
15966 | "mcnemar_test"
15967 | "runs_test" | "wald_wolfowitz"
15968 | "friedman_test" | "kruskal_wallis_test" | "kruskal"
15969 | "sign_test"
15970 | "anderson_darling_normality" | "ad_normality"
15971 | "jarque_bera_test" | "jb_test"
15972 | "ljung_box_test" | "ljung_box"
15973 | "durbin_watson_stat" | "durbin_watson"
15974 | "mahalanobis_distance" | "mahalanobis_dist"
15976 | "cosine_distance" | "canberra_distance"
15977 | "bray_curtis_distance" | "bray_curtis"
15978 | "l1_distance"
15979 | "chi_squared_distance"
15980 | "multivariate_normal_pdf" | "mvn_pdf"
15982 | "multivariate_normal_sample" | "rmvn"
15983 | "dirichlet_pdf" | "dirichlet_sample" | "rdirichlet"
15984 | "skellam_pmf"
15985 | "inverse_gaussian_pdf" | "wald_pdf"
15986 | "inverse_gaussian_cdf" | "wald_cdf"
15987 | "inverse_gaussian_sample" | "rwald"
15988 | "non_central_chi2_pdf" | "ncchi2_pdf"
15989 | "matrix_exp" | "expm" | "matrix_log" | "logm"
15991 | "matrix_sqrt" | "sqrtm" | "matrix_sin" | "sinm"
15992 | "matrix_cos" | "cosm"
15993 | "rk45_dormand_prince" | "rk45" | "dopri5"
15995 | "midpoint_step" | "ode_midpoint"
15996 | "heun_step" | "ode_heun"
15997 | "verlet_step" | "ode_verlet"
15998 | "logistic_regression" | "logit_fit"
16000 | "poisson_regression"
16001 | "ridge_regression" | "ridge"
16002 | "lasso_coord" | "lasso"
16003 | "bootstrap_mean_ci" | "boot_mean_ci"
16005 | "jackknife_estimate" | "jackknife"
16006 | "permutation_test_diff" | "perm_test_diff"
16007 | "acf_at_lag" | "diff_op" | "lag_op"
16009 | "decompose_classical" | "decompose_ts"
16010 | "combinations_list" | "permutations_list"
16012 | "cyclic_permutations" | "subsets_of_size"
16013 | "longest_increasing_subseq" | "lis"
16015 | "knapsack_01" | "knapsack"
16016 | "subset_sum_target" | "subset_sum"
16017 | "coin_change_min" | "coin_change_minimum"
16018 | "edit_distance_levenshtein" | "edit_distance"
16019 | "one_hot_encode" | "onehot" | "label_encode"
16021 | "categorical_cross_entropy" | "cce"
16022 | "classification_metrics" | "binary_metrics"
16023 | "roc_auc" | "auroc"
16024 | "gaussian_blur_kernel" | "sobel_x" | "sobel_y"
16026 | "prewitt_x" | "prewitt_y"
16027 | "laplacian_of_gaussian" | "log_kernel"
16028 | "brownian_path" | "wiener_path"
16030 | "geometric_brownian_path" | "gbm_path"
16031 | "poisson_process" | "random_walk_1d"
16032 | "lempel_ziv_complexity" | "lz_complexity"
16034 | "huffman_code_lengths" | "huffman"
16035 | "shannon_entropy_rate" | "block_entropy_rate"
16036 | "planck_blackbody" | "blackbody"
16038 | "rayleigh_jeans" | "compton_shift"
16039 | "rydberg_energy"
16040 | "hydrogen_radial_wavefunction" | "h_rad_psi"
16041 | "integer_log" | "ilog"
16043 | "aks_primality" | "aks"
16044 | "elliptic_curve_add" | "ec_add"
16045 | "berlekamp_massey" | "bm_lfsr"
16046 | "bezout_coefficients" | "bezout" | "extended_euclid"
16047 | "factor_quadratic" | "complete_square"
16049 | "partial_fraction_simple" | "partial_fraction"
16050 | "gauss_chebyshev_quad" | "gc_quad"
16052 | "gauss_hermite_quad" | "gh_quad"
16053 | "gauss_laguerre_quad" | "glag_quad"
16054 | "clenshaw_curtis_quad" | "cc_quad"
16055 | "tanh_sinh_quad" | "ts_quad"
16056 | "gauss_legendre_2d" | "gl_2d"
16057 | "monte_carlo_2d" | "mc_2d"
16058 | "simulated_annealing" | "sa_min"
16060 | "simplex_lp" | "lp_simplex"
16061 | "particle_swarm" | "pso_min"
16062 | "gev_pdf" | "gev_cdf" | "gev_sample" | "rgev"
16064 | "gen_pareto_pdf" | "gen_pareto_cdf"
16065 | "gen_pareto_sample" | "rgenpareto"
16066 | "skew_normal_pdf" | "skew_normal_cdf"
16067 | "mixture_normal_pdf"
16068 | "categorical_sample" | "rcat"
16069 | "multinomial_pmf" | "multinomial_sample" | "rmultinom"
16070 | "truncated_normal_pdf"
16071 | "truncated_normal_sample" | "rtnorm"
16072 | "dbscan" | "gmm_em_1d" | "gmm_1d"
16074 | "silhouette_score"
16075 | "davies_bouldin_index" | "db_index"
16076 | "calinski_harabasz_index" | "ch_index"
16077 | "mds_2d" | "pcoa_2d" | "mean_shift"
16078 | "batch_norm" | "layer_norm"
16080 | "dropout_mask"
16081 | "max_pool_1d" | "avg_pool_1d"
16082 | "attention_softmax" | "positional_encoding"
16083 | "glorot_init" | "xavier_init"
16084 | "he_init" | "kaiming_init"
16085 | "adam_step" | "rmsprop_step"
16086 | "ewma" | "ccf" | "periodogram"
16088 | "welch_psd" | "welch"
16089 | "lag_features"
16090 | "median_filter_2d"
16092 | "threshold_otsu" | "otsu"
16093 | "histogram_equalize" | "hist_eq"
16094 | "erode_2d" | "dilate_2d"
16095 | "mse_loss" | "mae_loss" | "huber_loss"
16097 | "vincenty_distance" | "vincenty"
16099 | "mercator_project"
16100 | "destination_from_bearing" | "dest_bearing"
16101 | "recaman" | "recaman_seq"
16103 | "sylvester" | "sylvester_seq"
16104 | "happy_q" | "is_happy"
16105 | "amicable_pair_q"
16106 | "aliquot_sequence"
16107 | "magic_constant"
16108 | "clustering_coefficient_local" | "cc_local"
16110 | "clustering_coefficient_global" | "cc_global"
16111 | "assortativity" | "common_neighbors" | "jaccard_neighbors"
16112 | "adamic_adar"
16113 | "preferential_attachment_score" | "pa_score"
16114 | "triangle_3d_normal" | "triangle_3d_area"
16116 | "tetrahedron_volume"
16117 | "plane_from_3_points" | "plane_from_pts"
16118 | "point_to_plane_distance" | "pt_plane_dist"
16119 | "ray_triangle_intersect" | "moller_trumbore"
16120 | "ray_sphere_intersect" | "aabb_overlap"
16121 | "gauss_seidel"
16123 | "jacobi_iteration" | "jacobi_solve"
16124 | "sor_solve" | "sor"
16125 | "thomas_tridiag_solve" | "thomas"
16126 | "richardson_extrapolation" | "richardson"
16127 | "finite_difference_5pt" | "fd5pt"
16128 | "tonelli_shanks_sqrt" | "tonelli_shanks"
16130 | "baby_step_giant_step" | "bsgs"
16131 | "pollard_rho_factor" | "pollard_rho"
16132 | "modular_lcm" | "mlcm"
16133 | "crt_general" | "crt_arbitrary"
16134 | "van_der_waals_p" | "vdw_pressure"
16136 | "nernst_equation" | "nernst"
16137 | "arrhenius_rate" | "arrhenius"
16138 | "reduced_mass"
16139 | "ph_to_concentration" | "ph_to_h"
16140 | "metropolis_hastings" | "mh_sampler"
16142 | "gibbs_sampler_step" | "gibbs_step"
16143 | "euler_maruyama" | "em_sde"
16144 | "milstein" | "milstein_sde"
16145 | "ornstein_uhlenbeck_path" | "ou_path"
16146 | "hmm_forward" | "hmm_viterbi" | "hmm_backward"
16147 | "kaplan_meier" | "km_estimator" | "log_rank_test"
16149 | "needleman_wunsch" | "nw_align"
16150 | "smith_waterman" | "sw_align"
16151 | "gibbs_free_energy" | "delta_g"
16153 | "henderson_hasselbalch" | "hh_eq"
16154 | "radioactive_decay"
16155 | "half_life_to_constant" | "hl_to_lambda"
16156 | "pid_step"
16158 | "transfer_function_eval" | "tf_eval"
16159 | "bode_magnitude_db" | "bode_mag_db"
16160 | "bode_phase_deg"
16161 | "lqr_2x2"
16162 | "nash_eq_2x2" | "nash_2x2"
16164 | "shapley_value" | "expected_utility"
16165 | "hungarian_assignment" | "hungarian"
16167 | "tsp_nearest_neighbor" | "tsp_nn"
16168 | "vertex_cover_2approx" | "vc_2approx"
16169 | "heat_eq_1d" | "wave_eq_1d"
16171 | "laplace_2d_jacobi" | "laplace_jacobi"
16172 | "beta_binomial_update"
16174 | "normal_normal_update"
16175 | "gamma_poisson_update"
16176 | "dirichlet_multinomial_update"
16177 | "hadamard_gate" | "h_gate"
16179 | "cnot_gate" | "cx_gate"
16180 | "swap_gate" | "cz_gate"
16181 | "qft_matrix" | "phase_gate"
16182 | "s_gate" | "t_gate"
16183 | "bezier_eval"
16185 | "catmull_rom_eval" | "cmr_eval"
16186 | "cubic_hermite_eval" | "ch_eval"
16187 | "bspline_basis" | "nik_basis"
16188 | "freq_to_midi" | "midi_to_freq"
16190 | "equal_temperament_freq"
16191 | "cents_difference" | "cents_diff"
16192 | "redshift_z" | "hubble_distance" | "luminosity_distance"
16194 | "reynolds_number" | "mach_number"
16196 | "prandtl_number" | "bernoulli_velocity"
16197 | "negative_binomial_pmf" | "nb_pmf"
16199 | "hypergeometric_pmf"
16200 | "beta_binomial_pmf" | "bb_pmf"
16201 | "von_mises_pdf" | "vmf_pdf"
16202 | "erdos_renyi_random" | "erdos_renyi"
16204 | "barabasi_albert_random" | "barabasi_albert"
16205 | "watts_strogatz_random" | "watts_strogatz"
16206 | "rgb_to_lab" | "lab_to_rgb"
16208 | "kelvin_to_rgb" | "color_temp_rgb"
16209 | "bell_triangle" | "surjection_count"
16211 | "distinct_partition_count" | "q_partition"
16212 | "fibonacci_q" | "is_fib_number"
16213 | "bonferroni_correction" | "bonferroni"
16215 | "benjamini_hochberg" | "bh_fdr"
16216 | "tukey_hsd"
16217 | "hellinger_distance"
16218 | "wasserstein_1d" | "earth_movers_1d"
16219 | "chi_squared_divergence"
16220 | "beta_geometric_pmf"
16221 | "generalized_gamma_pdf" | "gengamma_pdf"
16222 | "zip_pmf" | "zero_inflated_poisson_pmf"
16223 | "stefan_boltzmann_luminosity" | "stellar_luminosity"
16224 | "photon_momentum" | "photon_energy_ev"
16225 | "dipole_radiation_power" | "larmor_power"
16226 | "parallax_to_distance" | "hawking_temperature"
16227 | "roche_limit" | "apparent_magnitude" | "distance_modulus"
16228 | "beer_lambert" | "absorbance"
16229 | "rate_law_n"
16230 | "freezing_point_depression" | "fpd"
16231 | "mixed_nash_2x2" | "minimax_2x2"
16232 | "barycentric_coords_2d" | "barycentric_2d"
16234 | "bresenham_line" | "bilinear_interp_2d"
16235 | "point_in_polygon_2d"
16236 | "hilbert_transform" | "cepstrum"
16237 | "butterworth_lowpass_coeffs" | "butter_lp"
16238 | "savitzky_golay_coeffs" | "sg_coeffs"
16239 | "savitzky_golay_filter" | "sg_filter"
16240 | "canny_edge_intensity" | "canny_intensity"
16241 | "bilateral_filter_basic" | "bilateral_filter"
16242 | "kmeans_pp_init" | "kpp_init"
16243 | "elbow_score" | "wcss"
16244 | "young_tableaux_count" | "syt_count"
16245 | "euler_alt_permutation" | "euler_zigzag"
16246 | "genocchi_number" | "lattice_paths_count"
16247 | "tetration"
16248 | "ackermann_limited" | "ackermann"
16249 | "perfect_power_q" | "b_smooth_q"
16250 | "k_core"
16252 | "rich_club_coefficient" | "rich_club"
16253 | "rsa_basic_encrypt" | "rsa_enc_int"
16254 | "rsa_basic_decrypt" | "rsa_dec_int"
16255 | "dh_shared_secret"
16256 | "bell_state_phi_plus" | "bell_phi_plus"
16257 | "bell_state_psi_minus" | "bell_psi_minus"
16258 | "density_matrix_purity" | "rho_purity"
16259 | "concurrence_2qubit"
16260 | "point_in_circle"
16261 | "circle_circle_intersect_2d"
16262 | "polygon_centroid"
16263 | "sutherland_hodgman_clip" | "sh_clip"
16264 | "kalman_rts_smoother" | "rts_smoother"
16265 | "gc_content" | "codon_to_aa"
16267 | "reverse_complement_dna" | "rev_comp_dna"
16268 | "hamming_dna"
16269 | "blosum62_pair_score" | "blosum62"
16270 | "kmer_count"
16271 | "great_circle_bearing" | "gc_bearing"
16273 | "midpoint_lat_lon" | "mid_geo"
16274 | "utm_zone_for"
16275 | "area_polygon_lat_lon" | "geo_polygon_area"
16276 | "crr_binomial_option" | "crr_option"
16278 | "bond_price_clean"
16279 | "bond_yield_to_maturity" | "bond_ytm"
16280 | "modified_duration_bond"
16281 | "convexity_bond" | "bond_convexity"
16282 | "ssim" | "psnr" | "mssim"
16284 | "db_spl_from_pa" | "db_spl"
16286 | "a_weighting_factor" | "a_weight"
16287 | "octave_band_center" | "octave_center"
16288 | "semitone_ratio"
16289 | "hardy_weinberg"
16291 | "expected_heterozygosity" | "het_e"
16292 | "fst_simple"
16293 | "allele_frequencies"
16294 | "sir_step" | "sir_r0" | "doubling_time"
16296 | "theil_index"
16298 | "herfindahl_hirschman" | "hhi"
16299 | "atkinson_index"
16300 | "lorenz_curve_points"
16301 | "iota_range" | "iota"
16303 | "reshape_array" | "reshape"
16304 | "grade_up" | "grade_asc"
16305 | "grade_down" | "grade_desc"
16306 | "plasma_frequency" | "omega_p"
16308 | "debye_length" | "lambda_d"
16309 | "cyclotron_frequency" | "omega_c"
16310 | "larmor_radius" | "gyroradius"
16311 | "jaro_winkler_similarity" | "jaro_winkler"
16313 | "metaphone_simple"
16314 | "elo_rating_update" | "elo"
16316 | "glicko_rating_update" | "glicko"
16317 | "dice_sum_pmf"
16318 | "cohens_d" | "effect_size_d"
16320 | "cliff_delta"
16321 | "vargha_delaney_a12" | "a12"
16322 | "step_response_2nd_order" | "step_2nd"
16324 | "overshoot_2nd_order" | "overshoot_pct"
16325 | "frobenius_norm"
16327 | "spectral_norm" | "operator_norm_2"
16328 | "trace_matrix" | "tr_mat"
16329 | "homophily_index" | "homophily"
16331 | "dyad_census" | "triad_census"
16332 | "sigmoid_inverse" | "logit"
16334 | "partition_at" | "drop_at" | "insert_at_idx"
16336 | "replace_at_index" | "set_at"
16337 | "swap_indices" | "nth_largest" | "nth_smallest"
16338 | "position_of_all_matching" | "positions_of_all"
16339 | "string_take_first" | "string_take_last"
16340 | "string_drop_first" | "string_drop_last"
16341 | "pluralize_simple"
16342 | "singularize_simple" | "singularize"
16343 | "capitalize_words" | "title_words"
16344 | "format_table_simple" | "ascii_table"
16345 | "days_between" | "weeks_between"
16346 | "months_between" | "years_between"
16347 | "first_of_month" | "last_of_month"
16348 | "day_of_week_iso" | "iso_dow"
16349 | "easter_sunday" | "chinese_zodiac"
16350 | "iso_week_number" | "iso_week"
16351 | "relative_luminance" | "wcag_luminance"
16352 | "contrast_ratio_wcag" | "wcag_contrast"
16353 | "delta_e_76" | "delta_e"
16354 | "color_blend_t" | "lerp_color"
16355 | "chord_to_freqs" | "scale_to_intervals"
16356 | "interval_semitones"
16357 | "transpose_freq_semitones" | "transpose_semi"
16358 | "bpm_to_period" | "midi_to_pitch_class"
16359 | "key_signature_for" | "circle_of_fifths_step"
16360 | "moon_phase" | "equation_of_time"
16361 | "solar_declination" | "sidereal_day_period" | "ecliptic_obliquity"
16362 | "permutation_order"
16363 | "permutation_parity" | "perm_sign"
16364 | "identity_permutation"
16365 | "permutation_compose" | "perm_mul"
16366 | "flesch_reading_ease" | "flesch_kincaid_grade"
16367 | "gunning_fog"
16368 | "automated_readability_index" | "ari"
16369 | "lix"
16370 | "adjusted_r_squared" | "adj_r2"
16371 | "aic" | "bic"
16372 | "residuals_compute" | "compute_residuals"
16373 | "composition_count" | "weak_composition_count"
16374 | "necklace_count" | "bracelet_count"
16375 | "multiset_permutations_count" | "multinomial_count"
16376 | "pearson_hash_byte" | "pearson_hash"
16377 | "xorshift32_step" | "lcg_next_u32"
16378 | "fisher_yates_shuffle"
16379 | "tetrahedral_number" | "square_pyramidal_number"
16381 | "octahedral_number" | "pentagonal_pyramidal_number"
16382 | "cake_number" | "cuban_number" | "centered_hexagonal_number"
16383 | "carmichael_q" | "is_carmichael"
16384 | "sphenic_q" | "is_sphenic"
16385 | "seven_smooth_q" | "is_7_smooth"
16386 | "cartesian_product_n" | "cart_n"
16387 | "multiset_union" | "multiset_intersection" | "multiset_difference"
16388 | "polynomial_roots_dk" | "durand_kerner"
16389 | "lin_bairstow_step" | "bairstow"
16390 | "heap_sift_down"
16391 | "fenwick_build" | "bit_build"
16392 | "fenwick_query" | "bit_query"
16393 | "segment_tree_sum" | "seg_sum"
16394 | "kmp_failure" | "kmp"
16395 | "z_array" | "z_func"
16396 | "suffix_array_naive"
16397 | "manacher_radii" | "manacher"
16398 | "rabin_karp_hash" | "lcp_array"
16399 | "regex_escape_simple"
16400 | "horspool_search" | "bm_horspool"
16401 | "lpt_schedule" | "lpt"
16402 | "johnsons_rule" | "johnson_2m"
16403 | "bit_reverse_32" | "bit_reverse"
16404 | "bin_to_gray" | "gray_to_bin"
16405 | "swap_bits_pos" | "swap_bits"
16406 | "hamming_weight" | "popcnt"
16407 | "hamming_distance_int" | "hamdist_int"
16408 | "internal_rate_of_return"
16409 | "modified_irr" | "mirr"
16410 | "payback_period_simple" | "payback_simple"
16411 | "rfc3339_format" | "rfc3339"
16412 | "rfc3339_parse"
16413 | "iso_ordinal_date" | "ordinal_date"
16414 | "lazy_caterer" | "central_polygonal"
16416 | "centered_square" | "centered_triangular" | "centered_pentagonal"
16417 | "star_number" | "dodecahedral_number" | "icosahedral_number"
16418 | "pronic_number" | "squared_triangular"
16419 | "woodall_number" | "cullen_number"
16420 | "repunit" | "repdigit" | "kaprekar_routine_step"
16421 | "smith_q"
16422 | "keith_q" | "is_keith"
16423 | "armstrong_q" | "is_armstrong"
16424 | "fnv1a_hash" | "djb2_hash"
16425 | "jenkins_one_at_a_time" | "jenkins_oat"
16426 | "murmurhash3_x32"
16427 | "adler32_hash" | "crc16_ccitt"
16428 | "vec_dot"
16429 | "l1_norm" | "l2_norm" | "vec_l2"
16430 | "linf_norm" | "max_norm" | "lp_norm"
16431 | "unit_vector"
16432 | "vector_project" | "proj" | "vector_reject"
16433 | "orthogonalize_vectors" | "gram_schmidt"
16434 | "outer_product" | "vec_outer"
16435 | "matrix_diagonal" | "mdiagvec"
16436 | "matrix_anti_diagonal"
16437 | "matrix_symmetric_q" | "matrix_orthogonal_q"
16438 | "geometric_mean_arr" | "harmonic_mean_arr"
16439 | "quadratic_mean_arr" | "lehmer_mean"
16440 | "running_mean" | "running_variance"
16441 | "outlier_iqr_q" | "z_score_robust"
16442 | "geometric_sequence" | "arithmetic_sequence"
16443 | "log_sum_exp" | "lse"
16444 | "log_sigmoid" | "log1p_exp"
16445 | "string_chars"
16446 | "string_words_count" | "word_count_simple"
16447 | "string_lines_count" | "line_count_simple"
16448 | "string_intersperse" | "string_replicate"
16449 | "string_uniq_chars" | "string_letter_frequency"
16450 | "anagram_q" | "is_anagram_q"
16451 | "string_take_while" | "string_drop_while"
16452 | "string_split_at_first" | "string_partition_at_word"
16453 | "relativistic_kinetic"
16455 | "lorentz_factor_v" | "doppler_relativistic"
16456 | "drag_force_quadratic" | "terminal_velocity"
16457 | "carnot_efficiency" | "otto_efficiency"
16458 | "brayton_efficiency" | "diesel_efficiency"
16459 | "specific_heat_const_v" | "speed_of_sound_ideal"
16460 | "kepler_period_au" | "synodic_period"
16461 | "hill_radius" | "jeans_length"
16462 | "chandrasekhar_mass" | "eddington_luminosity"
16463 | "schwarzschild_radius_m" | "gravity_at_radius"
16464 | "gravitational_pe"
16465 | "freefall_time" | "pendulum_freq" | "spring_period"
16466 | "centripetal_accel" | "lens_focal_length"
16467 | "avogadros_number" | "boltzmann_const"
16468 | "planck_const_h" | "gas_constant_r"
16469 | "concentration_dilute" | "partial_pressure"
16470 | "mole_fraction" | "molarity" | "molality"
16471 | "normality_chem" | "ionic_strength"
16472 | "titration_volume"
16473 | "atomic_radius_pm" | "de_broglie_wavelength_kg"
16474 | "lotka_volterra_step"
16475 | "michaelis_menten" | "hill_equation"
16476 | "lineweaver_burk" | "eadie_hofstee_y"
16477 | "arrhenius_temp_q10"
16478 | "body_surface_area_dubois" | "bsa_dubois"
16479 | "bmr_harris_benedict_male" | "bmr_harris_benedict_female"
16480 | "max_heart_rate" | "target_heart_rate"
16481 | "vo2_max_estimate" | "pulse_pressure"
16482 | "mean_arterial_pressure" | "map_bp"
16483 | "dew_point_magnus" | "heat_index_celsius"
16484 | "wind_chill_celsius" | "pressure_altitude_m"
16485 | "density_altitude_m" | "saturation_vapor_pressure"
16486 | "humidex" | "utci_simple"
16487 | "resistance_parallel" | "r_parallel"
16488 | "resistance_series" | "r_series"
16489 | "capacitance_parallel" | "c_parallel"
16490 | "capacitance_series" | "c_series"
16491 | "inductance_parallel" | "l_parallel"
16492 | "inductance_series" | "l_series"
16493 | "voltage_divider" | "current_divider"
16494 | "lc_resonant" | "q_factor_rlc"
16495 | "skin_depth" | "wire_resistance"
16496 | "motor_torque" | "efficiency_ratio"
16497 | "dB_voltage" | "db_voltage"
16498 | "dB_power" | "db_power"
16499 | "bfs_distances" | "dfs_preorder" | "connected_components"
16501 | "graph_is_tree" | "graph_density"
16502 | "graph_average_degree" | "graph_max_degree" | "graph_min_degree"
16503 | "graph_complement"
16504 | "in_degree_directed" | "out_degree_directed"
16505 | "graph_eccentricity_all" | "is_connected"
16506 | "articulation_points" | "bridges_edges"
16507 | "eulerian_path_q" | "hamiltonian_brute"
16508 | "string_to_charcodes" | "charcodes_to_string"
16509 | "string_xor"
16510 | "string_camel_to_snake" | "string_snake_to_camel"
16511 | "string_kebab_to_snake" | "string_snake_to_kebab"
16512 | "palindromic_q" | "substring_count"
16513 | "string_truncate_ellipsis" | "string_expand_tabs"
16514 | "string_normalize_spaces"
16515 | "days_in_year" | "quarter_of_year"
16516 | "zeller_day_of_week" | "age_from_birthdate"
16517 | "business_days_between" | "unix_epoch_to_iso"
16518 | "loan_payment_pmt" | "loan_balance"
16519 | "amortization_total_interest"
16520 | "apr_to_apy" | "apy_to_apr"
16521 | "compound_interest_periods" | "simple_interest_compute"
16522
16523 | "perpetuity_value" | "growing_perpetuity"
16524 | "annuity_present_value" | "annuity_future_value"
16525 | "capm_expected_return"
16526 | "treynor_ratio"
16527 | "jensens_alpha" | "information_ratio"
16528 | "friction_factor_laminar" | "swamee_jain_factor"
16529 | "pipe_pressure_drop" | "orifice_velocity"
16530 | "chezy_velocity" | "manning_velocity"
16531 | "froude_number" | "weber_number" | "grashof_number"
16532 | "nusselt_dittus_boelter"
16533 | "mollweide_project" | "robinson_project" | "sinusoidal_project"
16535 | "equirectangular_project" | "lambert_azimuthal_project" | "albers_conic_project"
16536 | "geohash_encode" | "geohash_decode" | "geohash_neighbor" | "geohash_bbox"
16537 | "gabor_kernel" | "unsharp_mask_kernel" | "emboss_kernel"
16538 | "box_blur_kernel" | "motion_blur_kernel" | "sharpen_kernel"
16539 | "edge_detect_kernel" | "sobel_diagonal_kernel" | "haar_2d_step"
16540 | "db4_coeffs" | "db6_coeffs" | "sym4_coeffs" | "coif1_coeffs"
16541 | "aes_sbox_byte" | "aes_inv_sbox_byte"
16542 | "chacha20_qround" | "xtea_round" | "speck_round" | "simon_round"
16543 | "kepler_hyperbolic" | "hohmann_dv1" | "hohmann_dv2" | "hohmann_total"
16544 | "bielliptic_total" | "lambert_simple"
16545 | "horizon_distance" | "solar_zenith_angle" | "air_mass_kasten"
16546 | "solar_constant" | "julian_centuries_j2000"
16547 | "mean_solar_longitude" | "mean_solar_anomaly" | "lst_to_solar"
16548 | "ra_dec_to_az_alt" | "ecliptic_to_equatorial" | "equatorial_to_galactic"
16549 | "orbital_eccentricity" | "semi_major_axis"
16550 | "specific_orbital_energy" | "specific_angular_momentum"
16551 | "toffoli_gate" | "ccx_gate" | "fredkin_gate" | "cswap_gate"
16552 | "iswap_gate" | "sqrt_swap_gate"
16553 | "rx_gate" | "ry_gate" | "rz_gate"
16554 | "ghz_state_n" | "w_state_n"
16555 | "depolarizing_channel" | "dephasing_channel" | "amplitude_damping_channel"
16556 | "quantum_fidelity_pure" | "trace_distance"
16557 | "bell_inequality_chsh" | "pauli_decomposition_2x2"
16558 | "quantum_relative_entropy" | "qft_4_real"
16559 | "bwt_encode" | "bwt_decode" | "mtf_encode" | "mtf_decode"
16560
16561 | "lyndon_factorize" | "christoffel_word" | "sturmian_word"
16562 | "z_function_alt" | "period_of_string" | "borders_of_string"
16563 | "thue_morse_string" | "fibonacci_word"
16564 | "mann_kendall_tau" | "theil_sen_slope" | "hodges_lehmann"
16565 | "huber_m_estimator" | "winsorized_variance_arr"
16566 | "bowley_skewness" | "pearson_skewness_2"
16567 | "concordance_correlation" | "quantile_p"
16568 | "label_propagation_step" | "modularity_q"
16569 | "clique_count_3" | "local_efficiency" | "global_efficiency"
16570 | "diameter_unweighted"
16571 | "aitken_delta_squared" | "wynn_epsilon"
16572 | "shanks_transform" | "levin_t_transform"
16573 | "harmonic_seq_sum" | "alternating_seq_sum"
16574 | "sparse_csr_build" | "sparse_csr_mul_vec" | "sparse_density"
16576 | "lower_triangular_q" | "upper_triangular_q"
16577 | "diagonal_dominance_q" | "matrix_zero_q" | "matrix_identity_q"
16578 | "matrix_random_uniform" | "matrix_random_normal"
16579 | "andrew_monotone_chain" | "polygon_area_signed"
16580 | "polygon_convex_q" | "iou_2d_axis_aligned" | "hausdorff_distance_2d"
16581 | "minkowski_sum_simple" | "circle_3_points"
16582 | "polygon_winding_number" | "segment_length"
16583 | "segments_parallel_q" | "segments_perpendicular_q"
16584 | "burr_xii_pdf" | "burr_xii_cdf" | "dagum_pdf" | "lomax_pdf"
16585 | "birnbaum_saunders_pdf" | "tukey_lambda_quantile"
16586 | "half_cauchy_pdf" | "half_logistic_pdf" | "reciprocal_pdf"
16587 | "levy_pdf" | "voigt_profile_simple"
16588 | "gompertz_pdf" | "inverse_weibull_pdf"
16589 | "log_gamma_simple" | "inverse_chi2_pdf"
16590 | "poly1305_block_step" | "x25519_field_mul" | "curve25519_mul_simple"
16591 | "secp256k1_y_recover" | "hmac_step_xor"
16592 | "pkcs7_pad" | "pkcs7_unpad" | "xor_byte_string"
16593 | "atbash_cipher"
16594 | "vigenere_encrypt" | "vigenere_decrypt" | "xor_brute_keylen"
16595 | "arima_diff" | "seasonal_diff"
16596 | "garch_step" | "egarch_step"
16597 | "realized_volatility" | "max_drawdown_arr"
16598 | "calmar_ratio" | "omega_ratio" | "kelly_criterion"
16599 | "var_historical" | "cvar_historical"
16600 | "graph_degree_distribution" | "graph_count_edges"
16601 | "graph_bipartite_match_simple" | "graph_count_triangles"
16602 | "graph_avg_clustering" | "graph_transitivity"
16603 | "graph_max_clique_brute" | "graph_independent_set_brute"
16604 | "graph_count_paths_length_k" | "graph_pagerank_simple"
16605 | "boole_rule" | "boole_int"
16607 | "gauss_legendre_5" | "gl5"
16608 | "gauss_kronrod_15" | "gk15"
16609
16610 | "midpoint_rule"
16611 | "adams_bashforth_4" | "ab4"
16612 | "heun_method" | "rk45_cash_karp" | "rkck"
16613 | "milne_pc" | "milne"
16614 | "modified_midpoint_ode" | "modmidpoint"
16615 | "backward_euler" | "implicit_euler"
16616 | "crank_nicolson_ode" | "cn_ode"
16617 | "brent_root" | "brent" | "ridders_root" | "ridders"
16618 | "steffensen_root" | "steffensen" | "halley_root" | "halley"
16619 | "householder_root" | "muller_root" | "muller"
16620 | "regula_falsi" | "false_position"
16621 | "secant_root" | "secant"
16622 | "anderson_step" | "aberth_step" | "inverse_quad_interp"
16623 | "lm_step" | "gradient_descent_step"
16624 | "nesterov_step" | "adagrad_step"
16625 | "cg_beta_pr" | "cg_beta_fr" | "bfgs_h_update_1d"
16626 | "wolfe_strong_q" | "dogleg_step"
16627 | "nelder_mead_reflect" | "nelder_mead_expand" | "nelder_mead_contract"
16628 | "sa_accept_prob" | "sa_boltzmann_temp" | "sa_cauchy_temp"
16629 | "sa_geometric_temp" | "acceptance_target"
16630 | "bs_call" | "blackscholes_call" | "bs_put" | "blackscholes_put"
16632 | "bs_theta_call" | "bs_rho_call"
16633 | "bachelier_call" | "black76_call"
16634 | "crr_american_call" | "crr_american_put" | "jr_european_call"
16635 | "trinomial_call" | "heston_price_simple" | "sabr_implied_vol"
16636 | "merton_jump_call" | "asian_call_mc" | "barrier_up_out_call"
16637 | "digital_call" | "lookback_call"
16638 | "macaulay_duration" | "forward_rate"
16639 | "discount_continuous" | "ytm_newton"
16640 | "vasicek_bond" | "cir_bond" | "hull_white_drift"
16641 | "cds_upfront" | "black_karasinski_drift" | "quanto_adjustment"
16642 | "fx_forward" | "garman_kohlhagen_call" | "margrabe" | "stulz_min_call"
16643 | "sharpe_annualized"
16644 | "jensen_alpha" | "modified_sharpe"
16645 | "ph_from_h" | "poh_from_oh" | "pka_from_ka"
16647 | "henderson_base"
16648 | "arrhenius_k" | "eyring_k"
16649 | "first_order_concentration" | "first_order_half_life"
16650 | "second_order_concentration" | "second_order_half_life"
16651 | "zero_order_concentration"
16652
16653 | "ideal_gas_n" | "redlich_kwong_p"
16654 | "compressibility_z"
16655 | "kc_from_rates" | "kp_from_kc" | "reaction_quotient" | "rxn_q"
16656 | "le_chatelier_dir"
16657 | "dg_from_k" | "k_from_dg" | "vant_hoff" | "clausius_clapeyron" | "antoine_p"
16658 | "emf_from_half_cells" | "faraday_mass_deposited"
16659 | "transmittance" | "ksp_from_concs"
16660 | "debye_huckel"
16661 | "cp_monatomic_ideal" | "cv_monatomic_ideal"
16662 | "heat_capacity_q" | "calorimeter_dt" | "enthalpy_reaction"
16663 | "avogadro_count" | "moles_from_mass"
16664 | "dilution_v2" | "raoult_law" | "bp_elevation" | "fp_depression"
16665 | "osmotic_pressure" | "rydberg_lambda" | "bohr_radius_n"
16666 | "bohr_energy_ev" | "photon_energy_freq" | "photon_energy_lambda"
16667 | "de_broglie"
16668 | "logistic_growth_step" | "logistic_growth_analytic"
16670 | "gompertz_growth_step" | "allee_growth_step"
16671 | "growth_rate_from_ratio"
16672 | "seir_step" | "seird_step" | "sis_step"
16673 | "r0_basic" | "rt_effective" | "herd_immunity_threshold" | "generation_time"
16674 | "inverse_simpson"
16675 | "pielou_evenness" | "margalef_richness" | "menhinick_richness"
16676 | "berger_parker" | "sorensen_dice"
16677 | "rao_quadratic_entropy"
16678 | "selection_step" | "nei_genetic_distance"
16679 | "effective_pop_size" | "carrying_capacity_from_data"
16680 | "petersen_estimator" | "chapman_estimator"
16681 | "lv_competition_step"
16682 | "holling_type1" | "holling_type2" | "holling_type3"
16683 | "leslie_step" | "net_reproductive_rate" | "generation_time_demo"
16684 | "finite_rate_lambda" | "kleibers_law" | "bergmann_adjust"
16685 | "q10" | "species_area" | "intrinsic_growth_rate"
16686 | "macarthur_wilson_immigration" | "macarthur_wilson_extinction"
16687 | "island_equilibrium"
16688 | "efield_point" | "epotential_point"
16690 | "capacitor_charge"
16691 | "ohm_voltage" | "power_vi" | "power_i2r"
16692
16693 | "capacitance_parallel_sum"
16694 | "bfield_wire" | "bfield_solenoid" | "lorentz_force_mag"
16695 | "faraday_emf"
16696 | "lc_frequency" | "lc_omega"
16697 | "rc_tau" | "rl_tau"
16698 | "poynting_magnitude" | "em_intensity" | "radiation_pressure"
16699 | "em_wavelength" | "em_frequency"
16700 | "snell_theta2"
16701 | "index_from_speed" | "fresnel_reflection_normal"
16702 | "fresnel_rs" | "fresnel_rp"
16703 | "lensmaker" | "thin_lens_v" | "mirror_equation_v"
16704 | "lens_magnification" | "diffraction_grating_angle"
16705 | "single_slit_min" | "rayleigh_resolution"
16706 | "lorentz_gamma"
16707 | "rel_momentum" | "rel_ke" | "rel_total_energy" | "rel_energy_pm"
16708 | "relativistic_doppler" | "rel_velocity_add"
16709
16710 | "wave_string_speed" | "sound_solid" | "sound_gas"
16711 | "doppler_classical" | "standing_wave_fundamental"
16712 | "open_pipe_harmonic" | "closed_pipe_harmonic"
16713 | "sound_db"
16714 | "alfven_speed"
16715 | "grav_time_dilation" | "grav_redshift"
16716 | "kosaraju_scc" | "bridges"
16718 | "max_flow_ek" | "min_cut_value" | "hopcroft_karp"
16719
16720 | "katz_centrality" | "hits_simple"
16721 | "pagerank_damped" | "cc_count" | "cc_labels"
16722 | "topological_sort_kahn" | "has_cycle_directed" | "has_cycle_undirected"
16723 | "diameter_bfs" | "radius_bfs"
16724 | "num_edges" | "k_coreness"
16725 | "greedy_coloring" | "chromatic_number_greedy"
16726 | "sum_degrees" | "avg_degree" | "max_degree"
16727 | "is_tree" | "girth"
16728 | "hamming_window" | "hann_window" | "blackman_window"
16730 | "blackman_harris_window" | "bartlett_window" | "welch_window"
16731 | "kaiser_window" | "tukey_window" | "gaussian_window"
16732 | "hilbert_envelope"
16733 | "biquad_step" | "biquad_lowpass_coeffs" | "biquad_highpass_coeffs"
16734 | "biquad_bandpass_coeffs" | "biquad_notch_coeffs" | "biquad_allpass_coeffs"
16735 | "biquad_peak_coeffs" | "biquad_lowshelf_coeffs" | "biquad_highshelf_coeffs"
16736 | "butterworth_prewarp" | "butterworth_order"
16737 | "fir_moving_average" | "fir_lowpass_design"
16738 | "spectrogram_simple"
16739 | "zero_pad" | "resample_nearest" | "resample_linear" | "quantize"
16740 | "mu_law_encode" | "mu_law_decode" | "a_law_encode" | "a_law_decode"
16741 | "chirp_linear"
16742 | "fnv1a_32" | "fnv1a_64" | "sdbm_hash"
16744 | "siphash24"
16745 | "pbkdf2_hmac_step" | "scrypt_round" | "bcrypt_cost_iters"
16746 | "argon2_block_mix" | "hkdf_expand_step"
16747 | "lfsr_galois_step" | "mt19937_temper" | "xorshift64" | "xorshift32"
16748 | "pcg32_step" | "lcg_numrec_step" | "splitmix64_step" | "wyhash_mix"
16749
16750 | "xor_cipher_byte"
16751 | "railfence_encrypt" | "beaufort" | "affine_encrypt" | "substitution_encrypt"
16752 | "letter_frequency" | "english_chi2" | "index_of_coincidence" | "kasiski_repeats"
16753 | "deterministic_prime" | "dh_shared" | "rsa_encrypt_simple"
16754 | "monobit_test" | "approximate_entropy"
16755 | "gini_impurity" | "entropy_bits" | "information_gain" | "gain_ratio"
16757 | "nb_gaussian_likelihood" | "nb_bernoulli_likelihood" | "nb_multinomial_log_likelihood"
16758 | "adaboost_alpha" | "hinge_loss" | "squared_hinge"
16759 | "logistic_loss"
16760 | "sigmoid_grad" | "tanh_grad"
16761 | "relu_grad"
16762 | "softsign" | "prelu" | "threshold_act"
16763 | "confusion_counts" | "mcc" | "f_beta" | "specificity"
16764 | "balanced_accuracy" | "cohen_kappa" | "brier_score" | "log_loss"
16765 | "tversky" | "mahalanobis_1d"
16766 | "one_hot" | "topk_indices"
16767 | "minmax_scale" | "zscore_norm" | "robust_scale"
16768 | "triangle_area_heron" | "triangle_area_pts"
16770 | "triangle_inradius" | "triangle_circumradius"
16771 | "regular_ngon_area" | "regular_ngon_inradius" | "regular_ngon_circumradius"
16772 | "n_ball_volume"
16773 | "cylinder_surface" | "cone_surface"
16774
16775 | "ellipsoid_volume" | "ellipsoid_surface_approx"
16776 | "dist_point_line_2d" | "dist_point_plane_3d" | "closest_pt_segment_2d"
16777 | "bbox_from_points"
16778 | "euclidean_distance_nd"
16779 | "hamming_distance_str"
16780 | "great_circle_law_of_cos"
16781 | "initial_bearing" | "midpoint_great_circle"
16782 | "shoelace_area" | "polygon_is_convex" | "convex_hull_jarvis"
16783 | "euler_characteristic" | "genus_from_euler"
16784 | "spherical_triangle_area" | "polygon_with_holes_area" | "picks_theorem"
16785 | "centroid_nd" | "covariance_matrix_pts" | "simplex_volume_3d"
16786 | "hyper2f1" | "hyper1f1" | "hyper0f1" | "pochhammer"
16788 | "mathieu_ce0" | "mathieu_se1" | "parabolic_d0" | "parabolic_d1"
16789 | "whittaker_m" | "struve_h0" | "struve_h1"
16790 | "lambert_w0" | "wright_omega"
16791 | "sinhc" | "cosh_minus1_over_x2"
16792 | "sine_integral_si" | "cosine_integral_ci" | "exp_integral_e1"
16793 | "dawson_function" | "owen_t"
16794 | "spherical_bessel_j0" | "spherical_bessel_j1"
16795 | "spherical_bessel_y0" | "spherical_bessel_y1"
16796 | "mod_sph_bessel_i0" | "mod_sph_bessel_i1" | "mod_sph_bessel_k0"
16797 | "coulomb_f0"
16798 | "polylog_li2" | "polylog_n"
16799
16800 | "ti2" | "clausen_cl2"
16801 | "bose_einstein_g" | "fermi_dirac_int"
16802 | "theta3" | "theta2"
16803 | "jacobi_sn_small_q" | "jacobi_cn_small_q" | "jacobi_dn_small_q"
16804 | "riemann_xi" | "bessel_jn_general" | "bessel_in_general"
16805 | "absolute_magnitude"
16807 | "pc_to_ly" | "ly_to_pc" | "pc_to_au" | "au_to_m"
16808 | "solar_mass_to_kg" | "solar_luminosity_to_w"
16809 | "hubble_distance_mpc" | "comoving_distance_approx" | "critical_density"
16810 | "et_freq_ratio" | "midi_to_hz" | "hz_to_midi" | "cents_between"
16811 | "just_intonation_ratio" | "pythagorean_ratio"
16812 | "beat_frequency" | "bpm_to_spb" | "note_name_to_midi"
16813 | "rgb_to_yiq" | "rgb_to_yuv601"
16814 | "srgb_to_xyz" | "xyz_to_lab" | "delta_e_94"
16815
16816
16817 | "feet_to_meters" | "meters_to_feet"
16818 | "lb_to_kg" | "kg_to_lb"
16819 | "mph_to_kmh" | "kmh_to_mph" | "mps_to_kmh" | "kmh_to_mps" | "knots_to_kmh"
16820 | "atm_to_pa" | "pa_to_atm" | "mmhg_to_pa"
16821 | "ev_to_joules" | "joules_to_ev" | "btu_to_joules" | "kwh_to_joules"
16822 | "bpm_to_midi_tick_us" | "iso226_phon_adjustment"
16823 | "db_to_amp" | "amp_to_db"
16824 | "roman_encode" | "roman_decode" | "number_to_english"
16825 | "hubble_lcdm" | "hubble_time" | "hubble_distance_si" | "critical_density_si"
16827 | "comoving_distance" | "angular_diameter_distance"
16828 | "lookback_time" | "age_at_z" | "scale_factor" | "redshift_from_a"
16829 | "omega_m_at_z" | "lcdm_eos" | "cpl_w" | "deceleration_q"
16830 | "schwarzschild_radius_kg" | "kerr_ergosphere_eq" | "kerr_horizon"
16831 | "bh_entropy" | "bh_evaporation_time"
16832 | "schwarzschild_isco" | "photon_sphere_radius"
16833 | "tidal_force" | "grav_dilation_factor" | "lense_thirring_omega"
16834 | "gw_strain_amplitude" | "chirp_mass" | "grav_binding_energy"
16835 | "roche_limit_rigid" | "roche_limit_fluid"
16836 | "lagrange_l1" | "sphere_of_influence"
16837 | "freefall_velocity_schwarzschild" | "einstein_ring_radius"
16838 | "microlensing_magnification" | "cosmic_distance_modulus_si"
16839 | "cmb_temperature" | "cmb_temperature_at_z"
16840 | "stefan_boltzmann_si" | "planck_spectral_radiance"
16841 | "schwarzschild_g_tt" | "schwarzschild_g_rr" | "kretschmann_schwarzschild"
16842 | "hill_velocity" | "vacuum_energy_density"
16843 | "sound_horizon_recomb" | "bao_scale_today" | "sigma8_default"
16844 | "lensing_convergence" | "sigma_crit"
16845 | "perihelion_precession" | "shapiro_delay" | "light_deflection_angle"
16846 | "tov_mass_limit"
16847 | "main_sequence_lifetime" | "schwarzschild_freefall_time"
16848 | "friedmann_density_total" | "cosmological_constant"
16849
16850 | "planck_energy"
16851 | "pure_state_density" | "purity"
16853 | "linear_entropy" | "quantum_mutual_info"
16854 | "eof_from_concurrence"
16855 | "bell_state_index" | "chsh_expectation" | "tsirelson_bound"
16856 | "pauli_real_part" | "pauli_y_imag"
16857 | "bloch_to_density_real" | "bloch_purity_check"
16858 | "fidelity_pure_real" | "l1_coherence" | "relative_entropy_coherence"
16859 | "kraus_apply" | "bit_flip_prob" | "phase_flip_prob"
16860 | "depolarizing_density_2x2" | "amplitude_damping_excited"
16861 | "quantum_fisher_info" | "cramer_rao_bound" | "squeezing_db" | "heisenberg_min"
16862 | "coherent_mean_photons" | "thermal_mean_photons" | "poisson_photon_pmf"
16863 | "bose_einstein_pmf" | "mandel_q" | "g2_zero"
16864 | "free_particle_energy" | "infinite_well_energy" | "harmonic_oscillator_energy"
16865 | "hydrogen_energy_n" | "stark_shift_linear"
16866 | "zeeman_energy" | "larmor_frequency" | "rabi_frequency"
16867 | "schrodinger_step_real" | "probability_density" | "state_norm" | "state_normalize"
16868 | "quantum_variance" | "spin_casimir"
16869 | "cg_simple" | "wigner_3j_bound" | "qho_ground_state"
16870 | "tunneling_prob" | "gamow_factor" | "compton_wavelength" | "uncertainty_position"
16871 | "berry_phase_spin_half" | "zeno_survival" | "decoherence_time"
16872 | "ramsey_visibility" | "fermi_golden_rule"
16873 | "needleman_wunsch_score" | "smith_waterman_score" | "pam250_score"
16875 | "tanimoto_bits" | "translate_dna" | "transcribe_dna_rna" | "reverse_transcribe"
16876 | "at_content" | "tm_wallace" | "tm_marmur" | "codon_adaptation_index"
16877 | "kmer_jaccard" | "sequence_shannon_info" | "pwm_score"
16878 | "msa_column_entropy" | "seq_logo_information"
16879 | "damerau_levenshtein" | "lcs_length"
16880 | "hirschberg_lcs_length" | "common_kmers"
16881 | "jukes_cantor_distance" | "kimura_2p_distance" | "felsenstein_step"
16882 | "branch_length_substitutions" | "num_unrooted_trees" | "bayes_posterior"
16883 | "hw_expected_counts" | "allele_frequency" | "ld_d" | "ld_r_squared"
16884 | "heterozygosity" | "ne_from_variance"
16885 | "expected_coverage" | "lander_waterman_gaps"
16886 | "bh_adjusted_p" | "zscore_count"
16887 | "go_enrichment_p" | "blosum45_score"
16888 | "henikoff_weight" | "hamming_protein" | "codon_usage_variance"
16889 | "dnds_ratio" | "mutation_rate" | "tajimas_d" | "wattersons_theta"
16890 | "coalescent_expected_time" | "coalescent_tree_length" | "nm_from_fst"
16891 | "bdf1_step" | "bdf2_step" | "bdf3_step" | "bdf4_step" | "bdf5_step" | "bdf6_step"
16893 | "ab1_step" | "ab2_step" | "ab3_step"
16894 | "am2_step" | "am3_step" | "am4_step"
16895 | "ros2_step" | "imex_euler_step" | "symplectic_euler_step"
16896 | "leapfrog_step" | "stormer_verlet_step"
16897 | "rk4_single" | "dopri5_combine" | "rkf45_error"
16898 | "lobatto_iiia_2" | "lobatto_iiic_3" | "gauss_irk_2_stage" | "magnus_1st"
16899 | "euler_lte" | "trapezoidal_lte" | "pi_step_size"
16900 | "stiffness_ratio" | "spectral_radius"
16901 | "heun_euler_step" | "bogacki_shampine_step" | "verner_8_combine"
16902 | "rk_combine" | "ab_coeff_sum"
16903 | "newmark_beta_step" | "wilson_theta_step"
16904 | "strang_split" | "lie_split"
16905 | "exp_euler_step" | "etd_rk2" | "dde_euler_step"
16906 | "em_step" | "milstein_step" | "heun_sde_step" | "stratonovich_correction"
16907 | "predictor_corrector" | "numerical_jacobian_col"
16908 | "cn_coefficient" | "imex_theta_split" | "bulirsch_stoer_step"
16909 | "cfl_number" | "diffusion_stability"
16910 | "lax_friedrichs_flux" | "lax_wendroff_flux"
16911 | "van_leer_limiter" | "minmod_limiter" | "superbee_limiter" | "mc_limiter"
16912 | "pollard_p_minus_1" | "fermat_factor"
16914 | "trial_smallest_factor" | "bsgs_discrete_log"
16915 | "mertens" | "liouville"
16916 | "is_b_smooth" | "primorial_n"
16917 | "pseudoprime_base2" | "strong_pseudoprime"
16918 | "aks_witness_count" | "qs_relation"
16919 | "index_calculus_naive" | "lll_2x2_step" | "coppersmith_bound"
16920 | "shor_period_prob" | "rsa_d_from_e" | "dh_secret"
16921 | "elgamal_encrypt" | "ecc_point_double" | "continued_fraction_sqrt"
16922 | "pell_fundamental" | "sum_two_squares" | "class_number_bound"
16923 | "smith_normal_2x2_step" | "regulator_naive"
16924 | "power_residue_check" | "wieferich_check" | "wilson_test"
16925 | "goldbach_pair" | "english_likeness" | "xor_break_singlebyte"
16926 | "bit_reverse_64"
16927 | "gf256_multiply" | "hash_combine"
16928 | "arch_lm_test" | "breusch_pagan_test" | "white_robust_se"
16930 | "newey_west_se" | "hansen_j_test" | "gmm_moment_condition"
16931 | "hausman_test" | "breusch_godfrey_test" | "box_pierce_test"
16932 | "adf_test_stat" | "pp_test_stat" | "kpss_test_stat"
16933 | "dickey_fuller_critical" | "engle_granger_step"
16934 | "johansen_trace_step" | "vecm_alpha_beta"
16935 | "panel_within_estimator" | "panel_between_estimator"
16936 | "panel_random_effects" | "arellano_bond_step"
16937 | "ols_estimator" | "ols_residual_variance" | "ols_r_squared"
16938 | "ols_adjusted_r2" | "akaike_info_crit" | "bayesian_info_crit"
16939 | "hannan_quinn_ic" | "f_statistic_pooled" | "breusch_pagan_lm"
16940 | "ramsey_reset_test" | "chow_test_stat" | "white_test_stat"
16941 | "goldfeld_quandt" | "wald_test_stat" | "score_test_stat"
16942 | "likelihood_ratio_test" | "two_sls_iv" | "iv_estimator"
16943 | "mle_normal_log_lik" | "mle_exponential_log_lik"
16944 | "mle_poisson_log_lik" | "gmm_moment_function"
16945 | "pooling_test_stat" | "heteroskedasticity_test"
16946 | "robust_se_huber_white" | "bootstrap_se_estimate"
16947 | "heckman_correction" | "tobit_log_likelihood"
16948 | "probit_log_likelihood" | "logit_log_likelihood"
16949 | "multinomial_logit_prob" | "ordered_probit_threshold"
16950 | "panel_var_step" | "impulse_response_step"
16951 | "variance_decomposition" | "granger_causality_chi2"
16952 | "cointegration_residual" | "error_correction_step"
16953 | "random_walk_innovation" | "random_walk_drift_step"
16954 | "ar_model_likelihood" | "ma_model_likelihood"
16955 | "arma_model_innovation"
16956 | "euler_char_complex" | "betti_zero" | "betti_one" | "betti_two"
16958 | "genus_surface" | "chern_first_2d" | "genus_curve_arith"
16959 | "genus_curve_geo" | "hodge_diamond_value" | "poincare_duality"
16960 | "fundamental_group_zn" | "homology_rank" | "cohomology_rank"
16961 | "homotopy_group_sphere_pi" | "mapping_class_torus"
16962 | "linking_number_two" | "writhe_polygon" | "torsion_coefficient"
16963 | "simplex_volume_n" | "simplicial_volume" | "nerve_complex_count"
16964 | "cech_zero_cohomology" | "de_rham_zero"
16965 | "poincare_polynomial_eval" | "chromatic_homology_rank"
16966 | "khovanov_q_grading" | "hochschild_zero" | "cyclic_homology_step"
16967 | "group_cohomology_dim" | "group_homology_dim"
16968 | "abelianization_quotient" | "free_group_rank_lower"
16969 | "nilpotency_class_lower" | "solvable_length_upper"
16970 | "schreier_index" | "todd_genus_eval" | "hirzebruch_signature"
16971 | "chern_simons_action" | "gauss_bonnet_total"
16972 | "seifert_genus_lower" | "alexander_polynomial_at_one"
16973 | "jones_polynomial_at_minus_one" | "jones_polynomial_at_i"
16974 | "homfly_evaluation" | "kauffman_bracket_eval"
16975 | "cabling_pair_signature" | "seifert_form_2x2"
16976 | "turaev_alexander_step" | "v_polynomial_eval"
16977 | "polynomial_jones_skein" | "delta_complex_count"
16978 | "poset_zeta_two" | "mobius_poset_two" | "mobius_function_pair"
16979 | "mobius_inversion_step" | "incidence_algebra_dim"
16980 | "quiver_path_count" | "representation_dim_step"
16981 | "weyl_group_order" | "root_system_count"
16982 | "cartan_determinant_a2" | "cartan_matrix_b2"
16983 | "killing_form_su2" | "casimir_eigenvalue_su2"
16984 | "universal_enveloping_dim" | "verma_character_step"
16985 | "plethystic_substitution_value" | "schur_polynomial_eval"
16986 | "hall_inner_product_two" | "plactic_class_size"
16987 | "robinson_schensted_pair" | "yamanouchi_word_count"
16988 | "rsk_size" | "character_su2" | "character_sun"
16989 | "quantum_dimension_su2" | "quantum_dimension_q"
16990 | "fusion_rule_su2_step" | "modular_data_s_value"
16991 | "modular_data_t_value" | "verlinde_count_step"
16992 | "quantum_invariant_eval" | "operad_count_two"
16993 | "moduli_dimension_curves" | "hodge_polynomial_eval"
16994 | "mirror_symmetry_check" | "gromov_witten_invariant"
16995 | "donaldson_invariant" | "seiberg_witten_value"
16996 | "floer_homology_rank" | "khovanov_rasmussen_s"
16997 | "ozsvath_szabo_tau" | "heegaard_genus_lower"
16998 | "fintushel_stern_step" | "bauer_furuta_step"
16999 | "geometric_intersection_number"
17000 | "algebraic_intersection_number"
17001 | "nernst_potential_full" | "electrode_potential_step"
17003 | "exchange_current_density" | "butler_volmer_current"
17004 | "tafel_anodic_current" | "tafel_cathodic_current"
17005 | "mass_transport_overpotential" | "limiting_current_density"
17006 | "diffusion_layer_thickness" | "faradaic_efficiency"
17007 | "coulombic_efficiency_cell" | "energy_efficiency_cell"
17008 | "voltaic_efficiency" | "charge_capacity_battery"
17009 | "energy_density_battery" | "power_density_battery"
17010 | "specific_capacity_active" | "columbic_capacity_lihalfcell"
17011 | "ragone_point" | "peukert_capacity" | "peukert_exponent_fit"
17012 | "shepherd_voltage_step" | "nernst_planck_flux"
17013 | "debye_length_electrolyte" | "debye_huckel_activity"
17014 | "gouy_chapman_potential" | "stern_layer_capacitance"
17015 | "double_layer_capacitance" | "helmholtz_capacitance"
17016 | "zeta_potential_estimate" | "electroosmotic_velocity"
17017 | "hagen_poiseuille_eo" | "diffuse_layer_thickness"
17018 | "poisson_boltzmann_step" | "linearized_pb_step"
17019 | "electrochem_impedance_z" | "randles_circuit_z"
17020 | "warburg_impedance" | "cole_cole_eis" | "nyquist_phase"
17021 | "charge_transfer_resistance" | "solution_resistance_estimate"
17022 | "ionic_conductivity_arrhenius" | "nernst_einstein_diffusivity"
17023 | "walden_product" | "kohlrausch_law"
17024 | "onsager_relation_two_species" | "trasatti_voltammetry_charge"
17025 | "randles_sevcik_peak" | "levich_current_rde"
17026 | "koutecky_levich_intercept" | "mott_schottky_capacitance"
17027 | "flat_band_potential" | "schottky_barrier_height"
17028 | "photocurrent_density" | "quantum_efficiency_photo"
17029 | "overall_efficiency_pec" | "fuel_cell_polarization"
17030 | "electrolyzer_voltage" | "faraday_efficiency_h2"
17031 | "overpotential_oer" | "overpotential_her"
17032 | "electrocrystallization_step" | "nucleation_rate_constant"
17033 | "metal_corrosion_rate" | "pourbaix_line_value"
17034 | "mixed_potential_step" | "electrochemiluminescence_yield"
17035 | "solid_electrolyte_capacity" | "ionic_liquid_viscosity_step"
17036 | "lithium_ion_diffusivity" | "soc_estimate_coulomb"
17037 | "soh_capacity_fade" | "ocv_lithium_ion_step"
17038 | "state_of_charge_kalman" | "thermal_runaway_threshold"
17039 | "joule_heating_battery" | "calorimetric_heat_battery"
17040 | "abuse_test_voltage" | "swelling_strain_step"
17041 | "sei_resistance_growth" | "binder_content_optimal"
17042 | "porosity_active_layer" | "tortuosity_estimate_bruggeman"
17043 | "electrolyte_decomposition_temp" | "gibbs_thomson_undercooling"
17044 | "nernst_diffusion_layer" | "diff_coeff_aqueous_estimate"
17045 | "salt_activity_coefficient" | "mean_activity_coeff_pitzer"
17046 | "osmotic_coefficient_pitzer" | "debye_huckel_screening_factor"
17047 | "ph_at_isoelectric" | "buffer_capacity_acid_base"
17048 | "henderson_hasselbalch_solve" | "titration_endpoint_index"
17049 | "tensor_contract_two" | "tensor_outer_two" | "tensor_trace_index"
17051 | "tensor_symmetrize_two" | "tensor_antisymmetrize_two"
17052 | "levi_civita_three" | "levi_civita_four"
17053 | "kronecker_three" | "kronecker_four"
17054 | "metric_minkowski_eta_step" | "metric_schwarzschild_step"
17055 | "metric_kerr_step_simple" | "metric_frw_lapse"
17056 | "christoffel_first_kind_step" | "christoffel_second_kind_step"
17057 | "riemann_tensor_step_zero" | "riemann_curvature_normal_form"
17058 | "ricci_tensor_step_zero" | "scalar_curvature_step"
17059 | "einstein_tensor_step" | "weyl_tensor_step_zero"
17060 | "schouten_tensor_step" | "geodesic_equation_step_zero"
17061 | "parallel_transport_step" | "covariant_derivative_step"
17062 | "christoffel_symbol_normalize" | "ricci_identity_step"
17063 | "bianchi_first_identity_check" | "bianchi_second_identity_check"
17064 | "killing_vector_lie_step" | "lie_derivative_scalar_step"
17065 | "lie_derivative_vector_step" | "exterior_derivative_one_form"
17066 | "hodge_star_one_form" | "codifferential_step"
17067 | "laplace_de_rham_step" | "volume_form_riemannian"
17068 | "hodge_inner_product_one" | "sectional_curvature_two_plane"
17069 | "gauss_codazzi_step" | "mainardi_codazzi_step"
17070 | "weingarten_map_step" | "shape_operator_eig"
17071 | "mean_curvature_step" | "gaussian_curvature_step"
17072 | "extrinsic_principal_curv" | "intrinsic_principal_curv"
17073 | "geodesic_curvature_step" | "darboux_frame_step"
17074 | "fermi_normal_step" | "synge_world_function"
17075 | "raychaudhuri_step" | "expansion_scalar_step"
17076 | "shear_tensor_step" | "twist_tensor_step"
17077 | "optical_scalars_step" | "peeling_step_psi4"
17078 | "ads_metric_step" | "de_sitter_metric_step"
17079 | "warped_product_step_zero" | "kaluza_klein_step"
17080 | "brans_dicke_step" | "horndeski_step"
17081 | "einstein_dilaton_step" | "gauss_bonnet_term_2d"
17082 | "chern_pontryagin_4d_step" | "adm_mass_step"
17083 | "komar_mass_step" | "bondi_mass_step"
17084 | "brown_york_quasilocal" | "isolated_horizon_charge"
17085 | "trapped_surface_check" | "apparent_horizon_step"
17086 | "event_horizon_check" | "cosmological_constant_term"
17087 | "de_sitter_radius_step" | "anti_de_sitter_radius_step"
17088 | "penrose_diagram_factor" | "conformal_compactification_step"
17089 | "schwarzschild_kruskal_step" | "gullstrand_painleve_step"
17090 | "kerr_newman_charge_term" | "boyer_lindquist_step"
17091 | "hartle_thorne_metric" | "oppenheimer_volkoff_step"
17092 | "post_newtonian_step" | "shapiro_delay_step"
17093 | "mercury_perihelion_advance"
17094 | "gravitational_wave_quadrupole"
17095 | "plus_polarization_amp" | "cross_polarization_amp"
17096 | "chirp_mass_inspiral_step" | "isco_radius_kerr_step"
17097 | "spin_orbit_coupling_term" | "spin_spin_coupling_term"
17098 | "hawking_area_increase" | "unruh_temperature_full"
17099 | "bekenstein_entropy_step" | "holographic_entanglement_step"
17100 | "ryu_takayanagi_step" | "swampland_distance_check"
17101 | "conditional_entropy_step" | "joint_entropy_step"
17103 | "relative_entropy_kl" | "mutual_information_step"
17104 | "chain_rule_entropy" | "fano_inequality_bound"
17105 | "data_processing_inequality" | "arithmetic_coding_interval"
17106 | "range_coding_step" | "golomb_rice_code"
17107 | "elias_gamma_code" | "elias_delta_code" | "exp_golomb_code"
17108 | "fibonacci_code" | "shannon_fano_elias_code"
17109 | "huffman_balanced_step" | "arithmetic_decode_interval"
17110 | "range_decode_step" | "universal_code_length"
17111 | "ziv_lempel_estimate" | "lz77_match_length"
17112 | "lz78_dictionary_growth" | "lzw_step_dict"
17113 | "ppm_predict_prob" | "deflate_huffman_lit"
17114 | "brotli_distance_code_count" | "zstd_window_size_log"
17115 | "mpeg_quant_value" | "jpeg_zig_zag_index"
17116 | "jpeg_dct_8x8_quant" | "hadamard_walsh_transform_step"
17117 | "karhunen_loeve_step" | "discrete_haar_step"
17118 | "db4_wavelet_step" | "biorthogonal_step"
17119 | "beylkin_wavelet_step" | "coiflet_wavelet_step"
17120 | "mallat_pyramid_step" | "threshold_soft_value"
17121 | "threshold_hard_value" | "median_filter_window"
17122 | "mean_filter_window" | "gaussian_filter_window"
17123 | "unsharp_mask_step" | "sobel_kernel_value"
17124 | "prewitt_kernel_value" | "roberts_kernel_value"
17125 | "laplacian_kernel_value" | "canny_threshold_step"
17126 | "hough_accumulator_step" | "ransac_iteration_count"
17127 | "optical_flow_lk_step" | "horn_schunck_step"
17128 | "kalman_predict_state" | "kalman_update_state"
17129 | "particle_filter_resample" | "unscented_sigma_point"
17130 | "ekf_jacobian_step" | "markov_decision_value"
17131 | "bellman_equation_step" | "q_learning_update"
17132 | "policy_iteration_step" | "value_iteration_step"
17133 | "sarsa_update" | "double_q_learning_step"
17134 | "ucb1_action_value" | "thompson_sample_beta"
17135 | "boltzmann_softmax_action" | "explore_exploit_epsilon"
17136 | "montecarlo_returns_step" | "td_zero_update"
17137 | "td_lambda_update" | "gradient_temporal_diff"
17138 | "deep_q_target" | "ddpg_critic_loss_step"
17139 | "ppo_clip_term" | "trpo_kl_constraint"
17140 | "a3c_advantage_step" | "ppo_advantage_step"
17141 | "gae_advantage_step" | "generalized_advantage"
17142 | "information_bottleneck_step" | "free_energy_principle"
17143 | "fisher_info_metric" | "kullback_jensen_div"
17144 | "hellinger_distance_step" | "total_variation_distance"
17145 | "bhattacharyya_coefficient" | "wasserstein_dist_emp"
17146 | "chisquare_metric" | "hellinger_kernel"
17147 | "jensen_shannon_div" | "renyi_divergence_step"
17148 | "amari_alpha_div" | "csiszar_phi_div"
17149 | "sinkhorn_iteration_step" | "sliced_wasserstein"
17150 | "gromov_wasserstein_step" | "spectral_signature_match"
17151 | "mfcc_coeff_step" | "chroma_feature_step"
17152 | "tsp_lower_bound_mst" | "tsp_held_karp_step"
17154 | "christofides_ratio_bound" | "two_opt_swap_delta"
17155 | "or_opt_delta" | "three_opt_delta" | "lin_kernighan_step"
17156 | "nearest_neighbor_tour_step" | "greedy_edge_tour"
17157 | "nearest_insertion_step" | "farthest_insertion_step"
17158 | "cheapest_insertion_step" | "max_flow_ford_fulkerson_step"
17159 | "edmonds_karp_step" | "dinic_blocking_flow"
17160 | "push_relabel_step" | "boykov_kolmogorov_step"
17161 | "mincut_stoer_wagner" | "gomory_hu_step"
17162 | "karger_contract_edge" | "karger_min_cut_count"
17163 | "maximum_bipartite_matching" | "hopcroft_karp_phase"
17164 | "blossom_match_step" | "weighted_match_kuhn_step"
17165 | "hungarian_method_step" | "ap_jonker_volgenant_step"
17166 | "assignment_lower_bound" | "job_shop_makespan_lower"
17167 | "flow_shop_johnson_step" | "parallel_machine_lpt"
17168 | "parallel_machine_spt" | "list_scheduling_step"
17169 | "graham_2approx_bound" | "chc_bound_makespan"
17170 | "bin_packing_first_fit" | "bin_packing_best_fit"
17171 | "bin_packing_next_fit" | "bin_packing_lower_bound_l1"
17172 | "multidim_packing_step" | "knapsack_01_dp_value"
17173 | "knapsack_unbounded_dp" | "knapsack_fractional_step"
17174 | "knapsack_branch_bound" | "knapsack_lp_relaxation"
17175 | "multi_knapsack_step" | "quadratic_assignment_step"
17176 | "qap_lower_bound" | "graph_coloring_dsatur_step"
17177 | "graph_coloring_welsh_powell"
17178 | "graph_coloring_brooks_bound" | "graph_coloring_lp_bound"
17179 | "fractional_chromatic_lower" | "list_coloring_step"
17180 | "edge_coloring_vizing_step" | "clique_number_lower"
17181 | "independence_number_upper" | "vertex_cover_lp_round"
17182 | "dominating_set_greedy_step" | "dominating_set_lp_bound"
17183 | "set_cover_greedy_step" | "set_cover_lp_round"
17184 | "hitting_set_greedy" | "weighted_set_cover_step"
17185 | "matroid_greedy_step" | "matroid_intersection_step"
17186 | "submodular_greedy_step" | "submodular_curvature_bound"
17187 | "nemhauser_wolsey_bound" | "lp_relax_round"
17188 | "branch_and_bound_step" | "cutting_plane_step"
17189 | "gomory_cut_step" | "chvatal_gomory_cut"
17190 | "mixed_integer_round_up" | "mixed_integer_round_down"
17191 | "sos_constraint_check" | "column_generation_step"
17192 | "benders_decomposition_step" | "dantzig_wolfe_step"
17193 | "lagrangian_relax_step" | "lagrangian_dual_step"
17194 | "subgradient_step_size" | "nonlinear_dual_step"
17195 | "augmented_lagrangian_step" | "admm_primal_step"
17196 | "admm_dual_step" | "proximal_gradient_step"
17197 | "nesterov_accelerate_step" | "fista_step" | "ista_step"
17198 | "mirror_descent_step" | "frank_wolfe_step"
17199 | "conditional_gradient_step" | "greedy_set_cover_round"
17200 | "local_search_swap_step" | "tabu_search_move_score"
17201 | "simulated_annealing_step" | "genetic_crossover_one_point"
17202 | "mutation_bit_flip_prob" | "roulette_wheel_select_index"
17203 | "stefan_boltzmann_radiation" | "emissivity_grey_body"
17205 | "albedo_blackbody_balance" | "solar_constant_at_distance"
17206 | "total_solar_irradiance_step" | "absorbed_short_wave"
17207 | "emitted_long_wave" | "clausius_clapeyron_full"
17208 | "relative_humidity_step" | "dewpoint_temperature_full"
17209 | "wet_bulb_potential" | "virtual_temperature_full"
17210 | "density_altitude_full" | "geopotential_height_full"
17211 | "geometric_height_full" | "adiabatic_lapse_rate_dry"
17212 | "adiabatic_lapse_rate_moist" | "brunt_vaisala_full"
17213 | "richardson_number_step" | "gradient_richardson_full"
17214 | "flux_richardson_full" | "turbulent_kinetic_energy_step"
17215 | "mixing_length_prandtl" | "monin_obukhov_length"
17216 | "similarity_function_phi" | "log_law_wind_profile"
17217 | "power_law_wind_profile" | "ekman_layer_depth"
17218 | "ekman_pumping_step" | "geostrophic_wind_step"
17219 | "gradient_wind_step" | "thermal_wind_step"
17220 | "quasi_geostrophic_omega" | "omega_equation_step"
17221 | "potential_temperature_step" | "equivalent_potential_temp"
17222 | "saturation_equivalent_pt" | "ipv_potential_vorticity"
17223 | "ertel_pv_step" | "absolute_vorticity_step"
17224 | "relative_vorticity_step" | "divergence_omega_step"
17225 | "streamfunction_step" | "velocity_potential_step"
17226 | "helmholtz_decomp_step" | "courant_friedrichs_lewy"
17227 | "peclet_number_step" | "prandtl_number_step"
17228 | "reynolds_full_number" | "schmidt_number_step"
17229 | "sherwood_number_step" | "nusselt_full_number"
17230 | "grashof_number_step" | "rayleigh_number_step"
17231 | "weber_number_step" | "froude_number_step"
17232 | "strouhal_full" | "mach_full_step"
17233 | "biot_number_step" | "fourier_number_step"
17234 | "turbulence_intensity_step" | "hurst_exponent_estimate"
17235 | "detrended_fluct_alpha" | "power_spectrum_slope"
17236 | "spectral_kappa_minus53" | "batchelor_scale_step"
17237 | "kolmogorov_microscale" | "taylor_microscale_step"
17238 | "integral_length_scale" | "turbulent_dissipation_eps"
17239 | "isotropic_relation_check" | "sst_anomaly_step"
17240 | "enso_index_step" | "amo_index_step" | "nao_index_step"
17241 | "soi_oscillation_index" | "pdo_index_step" | "mjo_phase_step"
17242 | "walker_circulation_step" | "hadley_cell_max_lat"
17243 | "ferrel_cell_step" | "itcz_position_lat" | "trade_wind_speed"
17244 | "westerlies_jet_speed" | "polar_vortex_radius"
17245 | "arctic_oscillation_step" | "indian_monsoon_index"
17246 | "african_monsoon_index" | "qbo_oscillation_step"
17247 | "solar_cycle_phase" | "sunspot_relative_number"
17248 | "geomagnetic_kp_index" | "ozone_dobson_total"
17249 | "chlorine_radical_decay" | "montreal_protocol_track"
17250 | "co2_growth_rate_step" | "methane_growth_rate"
17251 | "aerosol_optical_depth" | "ice_age_milankovitch"
17252 | "greenhouse_forcing_step"
17253 | "game_two_player_value" | "nash_equilibrium_pair"
17255 | "mixed_strategy_value" | "zero_sum_minmax"
17256 | "saddle_point_check" | "correlated_equilibrium_value"
17257 | "shapley_value_two_step" | "banzhaf_index_two"
17258 | "nucleolus_lp_step" | "core_membership_check"
17259 | "imputation_efficient_check" | "imputation_individual_rational"
17260 | "prisoners_dilemma_payoff" | "matching_pennies_payoff"
17261 | "chicken_game_payoff" | "stag_hunt_payoff"
17262 | "battle_sexes_payoff" | "public_goods_game_payoff"
17263 | "tragedy_commons_metric" | "ultimatum_acceptance_prob"
17264 | "dictator_game_share" | "trust_game_repayment"
17265 | "cooperative_game_value" | "characteristic_function"
17266 | "bargaining_set_check" | "kalai_smorodinsky_step"
17267 | "nash_bargaining_solution" | "egalitarian_solution"
17268 | "utilitarian_solution" | "social_welfare_sum"
17269 | "arrow_impossibility_check" | "gibbard_satterthwaite_check"
17270 | "borda_count_step" | "condorcet_winner_check"
17271 | "plurality_winner_step" | "kemeny_score_step"
17272 | "dodgson_swap_count" | "coombs_runoff_step"
17273 | "single_transferable_vote" | "range_voting_score"
17274 | "approval_voting_max" | "schulze_method_step"
17275 | "copeland_score_step" | "black_method_winner"
17276 | "median_voter_step" | "hotelling_location_step"
17277 | "arrow_pareto_check" | "fair_division_envy_free"
17278 | "proportional_share" | "maximin_share"
17279 | "egalitarian_split" | "nash_social_welfare"
17280 | "divisible_goods_proportional" | "indivisible_envy_free_check"
17281 | "adjusted_winner_pct" | "sealed_bid_first_price"
17282 | "sealed_bid_second_price" | "english_auction_step"
17283 | "dutch_auction_step" | "all_pay_auction_step"
17284 | "vcg_payment_step" | "revenue_equivalence_check"
17285 | "truthful_mechanism_check" | "incentive_compatibility_check"
17286 | "mechanism_design_obj" | "double_auction_step"
17287 | "combinatorial_auction_step" | "posted_price_offer_accept"
17288 | "matching_market_step" | "deferred_acceptance_step"
17289 | "boston_mechanism_step" | "top_trading_cycles_step"
17290 | "school_choice_match" | "roommate_match_step"
17291 | "network_formation_step" | "coordination_game_payoff"
17292 | "evolutionary_stable_strategy" | "replicator_dynamics_step"
17293 | "hawk_dove_payoff" | "fictitious_play_step"
17294 | "best_response_dynamic" | "quantal_response_logit"
17295 | "level_k_step" | "cognitive_hierarchy_step"
17296 | "sequential_eq_check" | "subgame_perfect_eq"
17297 | "stackelberg_step" | "cournot_quantity_step"
17298 | "bertrand_price_step" | "hotelling_price_step"
17299 | "collusion_payoff_step" | "folk_theorem_value"
17300 | "repeated_game_avg_payoff" | "discount_factor_step"
17301 | "trigger_strategy_payoff" | "grim_trigger_step"
17302 | "tit_for_tat_step" | "prisoners_repeated_eq"
17303 | "mertens_zamir_step" | "ex_post_value_check"
17304 | "ex_ante_value_check" | "common_knowledge_iterations"
17305 | "cas_simplify_term" | "cas_expand_two_terms"
17307 | "cas_factor_quadratic" | "cas_partial_fraction_simple"
17308 | "cas_polynomial_gcd_step" | "cas_polynomial_div_step"
17309 | "cas_lagrange_interpolate" | "cas_chebyshev_eval"
17310 | "cas_legendre_eval" | "cas_hermite_eval"
17311 | "cas_laguerre_eval" | "cas_jacobi_eval"
17312 | "cas_gegenbauer_eval" | "cas_taylor_coefficient"
17313 | "cas_padé_diagonal" | "cas_continued_fraction_step"
17314 | "cas_resultant_two" | "cas_subresultant_two"
17315 | "cas_groebner_lt_step" | "cas_buchberger_step"
17316 | "cas_macaulay_matrix_step" | "cas_modular_inverse"
17317 | "cas_extended_euclid_step" | "cas_smith_normal_step"
17318 | "cas_hermite_normal_step" | "cas_radical_simplify"
17319 | "cas_minimal_polynomial" | "cas_gcd_polynomial_step"
17320 | "cas_resultant_x_y" | "cas_solve_linear"
17321 | "cas_solve_quadratic" | "cas_solve_cubic"
17322 | "cas_solve_quartic" | "cas_solve_polynomial_n"
17323 | "cas_root_isolate_step" | "cas_sturm_sequence_step"
17324 | "cas_descartes_rule_count" | "cas_companion_matrix_root"
17325 | "cas_polynomial_roots_kahan"
17326 | "cas_eigenvalue_inverse_iteration" | "cas_qr_iteration_step"
17327 | "cas_jacobi_eigen_step" | "cas_lanczos_iteration_step"
17328 | "cas_arnoldi_iteration_step" | "cas_givens_rotation_apply"
17329 | "cas_householder_reflection" | "cas_modified_gram_schmidt"
17330 | "cas_classical_gram_schmidt" | "cas_rank_revealing_qr"
17331 | "cas_pivoted_lu_step" | "cas_block_lu_step"
17332 | "cas_cholesky_step" | "cas_modified_cholesky"
17333 | "cas_ldlt_step" | "cas_bunch_kaufman_step"
17334 | "cas_woodbury_identity" | "cas_matrix_pencil_step"
17335 | "cas_generalized_eigen" | "cas_singular_value_step"
17336 | "cas_truncated_svd_value" | "cas_pseudoinverse_step"
17337 | "cas_polar_decomposition" | "cas_schur_decomposition_step"
17338 | "cas_quasi_triangular" | "cas_riccati_continuous_step"
17339 | "cas_riccati_discrete_step" | "cas_lyapunov_continuous_step"
17340 | "cas_lyapunov_discrete_step" | "cas_sylvester_equation_step"
17341 | "cas_kronecker_product_step" | "cas_vec_operator_step"
17342 | "cas_matrix_function_step" | "cas_matrix_log_step"
17343 | "cas_matrix_exp_pade" | "cas_matrix_sqrt_step"
17344 | "cas_drazin_inverse_step" | "cas_moore_penrose_step"
17345 | "cas_least_squares_solve" | "cas_total_least_squares"
17346 | "cas_constrained_ls_step" | "cas_truncated_lsq"
17347 | "cas_regularized_lsq_tikhonov" | "cas_basis_pursuit_step"
17348 | "cas_lasso_soft_threshold" | "cas_elastic_net_step"
17349 | "cas_omp_step" | "cas_iht_iteration"
17350 | "cas_cosamp_step" | "cas_admm_lasso_step"
17351 | "cas_proximal_l1_step" | "cas_proximal_l2_step"
17352 | "cas_proximal_l_inf_step" | "cas_indicator_simplex_proj"
17353 | "cas_proj_l1_ball" | "cas_proj_l2_ball"
17354 | "cas_proj_box" | "cas_proj_psd_cone"
17355 | "cas_proj_soc_step" | "cas_proj_exp_cone"
17356 | "cas_dykstra_step" | "cas_alternating_projection"
17357 | "cas_polya_enumeration_step" | "cas_burnside_count_step"
17358 | "ml_relu_step" | "ml_leaky_relu_step" | "ml_elu_step"
17360 | "ml_selu_step" | "ml_gelu_step" | "ml_swish_step"
17361 | "ml_mish_step" | "ml_softplus_step" | "ml_softsign_step"
17362 | "ml_hard_sigmoid" | "ml_hard_tanh" | "ml_prelu_step"
17363 | "ml_celu_step" | "ml_silu_step" | "ml_logsumexp_step"
17364 | "ml_log_softmax_step" | "ml_log_sigmoid"
17365 | "ml_glu_step" | "ml_geglu_step" | "ml_swiglu_step"
17366 | "ml_attention_score_step" | "ml_scaled_dot_product"
17367 | "ml_multihead_avg" | "ml_softmax_temperature"
17368 | "ml_dropout_mask_prob" | "ml_layer_norm_step"
17369 | "ml_batch_norm_step" | "ml_group_norm_step"
17370 | "ml_rms_norm_step" | "ml_instance_norm_step"
17371 | "ml_weight_norm_step" | "ml_spectral_norm_step"
17372 | "ml_l2_normalize_step" | "ml_huber_loss_step"
17373 | "ml_smooth_l1_loss" | "ml_focal_loss_step"
17374 | "ml_dice_loss_step" | "ml_iou_loss_step"
17375 | "ml_giou_loss_step" | "ml_diou_loss_step"
17376 | "ml_ciou_loss_step" | "ml_contrastive_loss"
17377 | "ml_triplet_loss_step" | "ml_arcface_loss_step"
17378 | "ml_center_loss_step" | "ml_kl_divergence_loss"
17379 | "ml_cross_entropy_loss" | "ml_binary_cross_entropy"
17380 | "ml_label_smoothing" | "ml_mixup_lambda"
17381 | "ml_cutmix_box_iou" | "ml_random_erasing_step"
17382 | "ml_cosine_lr_schedule" | "ml_warmup_lr_step"
17383 | "ml_step_lr_schedule" | "ml_exponential_lr"
17384 | "ml_polynomial_lr" | "ml_one_cycle_lr"
17385 | "ml_inverse_sqrt_lr" | "ml_cyclic_lr_step"
17386 | "ml_sgd_step" | "ml_momentum_step"
17387 | "ml_nesterov_momentum" | "ml_adagrad_step"
17388 | "ml_rmsprop_step" | "ml_adam_step"
17389 | "ml_adamw_step" | "ml_adamax_step"
17390 | "ml_nadam_step" | "ml_radam_step"
17391 | "ml_lookahead_step" | "ml_lamb_step"
17392 | "ml_lars_step" | "ml_yogi_step"
17393 | "ml_amsgrad_step" | "ml_adabelief_step"
17394 | "ml_shampoo_step" | "ml_lion_step"
17395 | "ml_sophia_step" | "ml_gradient_clip_norm"
17396 | "ml_gradient_clip_value" | "ml_gradient_accumulate"
17397 | "ml_gradient_centralize" | "ml_weight_decay_step"
17398 | "ml_he_init_value" | "ml_xavier_init_value"
17399 | "ml_glorot_init_value" | "ml_orthogonal_init"
17400 | "ml_truncnormal_init" | "ml_kaiming_init"
17401 | "ml_lecun_init_value" | "ml_zero_init"
17402 | "ml_constant_init" | "ml_uniform_init"
17403 | "ml_one_hot_index" | "ml_label_to_id"
17404 | "ml_id_to_label_step" | "ml_token_logit_top_k"
17405 | "ml_topk_argmax" | "ml_nucleus_sample_p"
17406 | "ml_temperature_decay" | "ml_repetition_penalty"
17407 | "ml_eos_logit_boost"
17408 | "nlp_bm25_score" | "nlp_tf_idf_step" | "nlp_okapi_score"
17410 | "nlp_word_freq_value" | "nlp_doc_freq_step"
17411 | "nlp_inverse_doc_freq" | "nlp_cosine_similarity_two"
17412 | "nlp_jaccard_similarity_two" | "nlp_overlap_coefficient"
17413 | "nlp_dice_coefficient_two" | "nlp_simpson_coefficient"
17414 | "nlp_levenshtein_dist" | "nlp_damerau_levenshtein"
17415 | "nlp_jaro_distance" | "nlp_jaro_winkler"
17416 | "nlp_hamming_distance" | "nlp_lcs_length" | "nlp_lcs_ratio"
17417 | "nlp_meteor_score" | "nlp_bleu_score_n"
17418 | "nlp_rouge_score_n" | "nlp_chrf_score" | "nlp_ter_score"
17419 | "nlp_wer_score" | "nlp_cer_score" | "nlp_perplexity_value"
17420 | "nlp_bits_per_character" | "nlp_char_ngram_count"
17421 | "nlp_word_ngram_count" | "nlp_skip_gram_count"
17422 | "nlp_byte_pair_merge_step" | "nlp_wordpiece_score"
17423 | "nlp_unigram_lm_score" | "nlp_kneser_ney_step"
17424 | "nlp_witten_bell_step" | "nlp_good_turing_count"
17425 | "nlp_laplace_smoothing" | "nlp_lidstone_smoothing"
17426 | "nlp_jelinek_mercer" | "nlp_dirichlet_smoothing"
17427 | "nlp_query_likelihood_step" | "nlp_kl_lm_div"
17428 | "nlp_pmi_score" | "nlp_npmi_score"
17429 | "nlp_chi2_collocation" | "nlp_loglikelihood_collocation"
17430 | "nlp_t_score_collocation" | "nlp_dunning_log_likelihood"
17431 | "nlp_lda_alpha_step" | "nlp_lda_beta_step"
17432 | "nlp_lda_topic_dist" | "nlp_plsa_step"
17433 | "nlp_word2vec_skipgram_loss" | "nlp_word2vec_cbow_loss"
17434 | "nlp_glove_loss_step" | "nlp_fasttext_subword_count"
17435 | "nlp_byte_level_bpe_step" | "nlp_sentencepiece_score"
17436 | "nlp_unigram_subword_loss" | "nlp_subword_regularization"
17437 | "nlp_pointwise_attn_score" | "nlp_relative_position_bias"
17438 | "nlp_alibi_position_bias" | "nlp_rope_rotary_angle"
17439 | "nlp_rope_apply_step" | "nlp_position_encoding_sin"
17440 | "nlp_position_encoding_cos" | "nlp_pe_freq_band"
17441 | "nlp_max_seq_len_check" | "nlp_token_drop_rate"
17442 | "nlp_byte_frequency" | "nlp_char_frequency"
17443 | "nlp_punct_ratio" | "nlp_uppercase_ratio"
17444 | "nlp_digit_ratio" | "nlp_emoji_ratio"
17445 | "nlp_url_count" | "nlp_email_count" | "nlp_phone_count"
17446 | "nlp_hashtag_count" | "nlp_mention_count"
17447 | "nlp_token_overlap_two" | "nlp_word_mover_dist"
17448 | "nlp_sif_weight_step" | "nlp_doc_embedding_avg"
17449 | "nlp_attention_pool_step" | "nlp_max_pool_step"
17450 | "nlp_avg_pool_step" | "nlp_sum_pool_step"
17451 | "nlp_self_attn_compute_step" | "nlp_cross_attn_compute_step"
17452 | "nlp_window_attn_step" | "nlp_strided_attn_step"
17453 | "nlp_block_attn_step" | "nlp_sliding_window_step"
17454 | "nlp_local_attn_step" | "nlp_dilated_attn_step"
17455 | "nlp_global_attn_step" | "nlp_sparse_attn_score"
17456 | "nlp_linformer_step" | "nlp_performer_step"
17457 | "nlp_reformer_step" | "nlp_longformer_step"
17458 | "nlp_bigbird_step" | "nlp_routing_attn_step"
17459 | "gfx_perspective_proj_x" | "gfx_perspective_proj_y"
17461 | "gfx_orthographic_proj" | "gfx_view_matrix_step"
17462 | "gfx_lookat_forward" | "gfx_lookat_right" | "gfx_lookat_up"
17463 | "gfx_quat_to_axis_angle" | "gfx_axis_angle_to_quat"
17464 | "gfx_quat_slerp_step" | "gfx_quat_nlerp_step"
17465 | "gfx_quat_dot_two" | "gfx_quat_inverse_step"
17466 | "gfx_quat_to_euler_pitch" | "gfx_quat_to_euler_yaw"
17467 | "gfx_quat_to_euler_roll" | "gfx_euler_to_quat_x"
17468 | "gfx_euler_to_quat_y" | "gfx_euler_to_quat_z"
17469 | "gfx_euler_to_quat_w" | "gfx_rotation_matrix_xx"
17470 | "gfx_rotation_matrix_yy" | "gfx_rotation_matrix_zz"
17471 | "gfx_translation_matrix_step" | "gfx_scale_matrix_step"
17472 | "gfx_shear_matrix_xy" | "gfx_homogeneous_divide"
17473 | "gfx_screen_space_x" | "gfx_screen_space_y"
17474 | "gfx_ndc_to_screen_x" | "gfx_ndc_to_screen_y"
17475 | "gfx_screen_to_ndc_x" | "gfx_screen_to_ndc_y"
17476 | "gfx_clip_polygon_step" | "gfx_sutherland_hodgman"
17477 | "gfx_cohen_sutherland_code" | "gfx_liang_barsky_t"
17478 | "gfx_bresenham_step_x" | "gfx_bresenham_step_y"
17479 | "gfx_xiaolin_wu_intensity" | "gfx_aabb_intersect_check"
17480 | "gfx_obb_overlap_step" | "gfx_sphere_intersect_t"
17481 | "gfx_ray_triangle_t" | "gfx_ray_plane_t" | "gfx_ray_box_t"
17482 | "gfx_ray_sphere_t" | "gfx_ray_disk_t"
17483 | "gfx_ray_cylinder_t" | "gfx_ray_cone_t"
17484 | "gfx_ray_ellipsoid_t" | "gfx_ray_torus_t_approx"
17485 | "gfx_barycentric_alpha" | "gfx_barycentric_beta"
17486 | "gfx_barycentric_gamma" | "gfx_phong_diffuse_step"
17487 | "gfx_phong_specular_step" | "gfx_phong_ambient_step"
17488 | "gfx_blinn_specular_step" | "gfx_lambert_term"
17489 | "gfx_oren_nayar_term" | "gfx_cook_torrance_d_ggx"
17490 | "gfx_cook_torrance_g_smith" | "gfx_cook_torrance_f_schlick"
17491 | "gfx_disney_principled_d" | "gfx_microfacet_brdf_step"
17492 | "gfx_subsurface_scattering_term" | "gfx_translucent_falloff"
17493 | "gfx_normal_distribution_ggx"
17494 | "gfx_geometric_attenuation_smith"
17495 | "gfx_fresnel_dielectric_step" | "gfx_fresnel_conductor_step"
17496 | "gfx_index_of_refraction" | "gfx_snells_law_angle"
17497 | "gfx_total_internal_reflection" | "gfx_refract_direction_x"
17498 | "gfx_reflect_direction_x" | "gfx_environment_map_uv_u"
17499 | "gfx_environment_map_uv_v" | "gfx_cube_map_face_index"
17500 | "gfx_octahedral_encode_x" | "gfx_octahedral_encode_y"
17501 | "gfx_spherical_harmonic_y00" | "gfx_spherical_harmonic_y10"
17502 | "gfx_spherical_harmonic_y11" | "gfx_spherical_harmonic_y20"
17503 | "gfx_zonal_harmonic_step" | "gfx_irradiance_sh_eval"
17504 | "gfx_radiance_sh_eval" | "gfx_skybox_uv_u" | "gfx_skybox_uv_v"
17505 | "gfx_tonemap_reinhard" | "gfx_tonemap_aces"
17506 | "gfx_tonemap_uncharted2" | "gfx_tonemap_filmic"
17507 | "gfx_gamma_correct_step" | "gfx_srgb_to_linear"
17508 | "gfx_linear_to_srgb" | "gfx_dither_bayer_4x4"
17509 | "gfx_dither_floyd_steinberg" | "gfx_oklab_l_step"
17510 | "gfx_oklab_a_step" | "gfx_oklab_b_step"
17511 | "gfx_oklch_chroma" | "gfx_oklch_hue"
17512 | "gfx_pcg_hash_step" | "gfx_xorshift_step"
17513 | "gfx_halton_step" | "gfx_sobol_step"
17514 | "gfx_van_der_corput" | "gfx_low_discrepancy_step"
17515 | "gfx_blue_noise_value" | "gfx_perlin_noise_step"
17516 | "gfx_simplex_noise_step" | "gfx_fbm_noise_step"
17517 | "gfx_worley_noise_step" | "gfx_voronoi_distance"
17518 | "gfx_curl_noise_step" | "gfx_gradient_noise_step"
17519 | "gfx_value_noise_step" | "gfx_signed_distance_box"
17520 | "gfx_signed_distance_sphere" | "gfx_signed_distance_capsule"
17521 | "db_b_tree_split" | "db_b_tree_merge"
17523 | "db_lsm_compaction_step" | "db_skiplist_height_pick"
17524 | "db_bloom_filter_bit_index" | "db_cuckoo_filter_fingerprint"
17525 | "db_quotient_filter_canonical" | "db_count_min_sketch_bin"
17526 | "db_hyperloglog_register_max" | "db_min_hash_value"
17527 | "db_simhash_bit" | "db_consistent_hash_index"
17528 | "db_rendezvous_hash_score" | "db_jump_hash_bucket"
17529 | "db_maglev_hash_step" | "db_lru_cache_eviction_age"
17530 | "db_lfu_cache_decay" | "db_arc_cache_score"
17531 | "db_clock_cache_hand" | "db_tinylfu_admit_score"
17532 | "db_w_tinylfu_freq" | "db_buffer_pool_score"
17533 | "db_query_plan_cost_step" | "db_join_selectivity_step"
17534 | "db_index_seek_cost" | "db_seq_scan_cost"
17535 | "db_index_scan_cost" | "db_sort_cost_estimate"
17536 | "db_hash_join_cost" | "db_merge_join_cost"
17537 | "db_nested_loop_cost" | "db_query_cardinality"
17538 | "db_histogram_bucket_index" | "db_quantile_estimate_p99"
17539 | "db_t_digest_centroid" | "db_kll_quantile_step"
17540 | "db_dd_sketch_bin" | "db_reservoir_sample_index"
17541 | "db_chao_estimator_step" | "db_jaccard_minhash_estimate"
17542 | "db_distinct_estimate_lpc" | "db_distinct_estimate_hll"
17543 | "db_throttle_token_step" | "db_leaky_bucket_step"
17544 | "db_token_bucket_step" | "db_circuit_breaker_step"
17545 | "db_two_phase_commit_step" | "db_three_phase_commit_step"
17546 | "db_paxos_propose_id" | "db_raft_term_advance"
17547 | "db_raft_log_match_check" | "db_zab_epoch_step"
17548 | "db_chubby_lease_step" | "db_logical_clock_step"
17549 | "db_lamport_timestamp" | "db_vector_clock_merge"
17550 | "db_hybrid_logical_clock" | "db_crdt_g_counter_merge"
17551 | "db_crdt_pn_counter_merge" | "db_crdt_lww_register_merge"
17552 | "db_crdt_set_or_merge" | "db_consensus_quorum_size"
17553 | "db_replication_lag_step" | "db_partitions_for_n"
17554 | "db_consistent_lookup_id" | "db_chord_finger_index"
17555 | "db_kademlia_xor_distance" | "db_pastry_routing_step"
17556 | "db_dht_replicate_factor" | "db_partition_failure_check"
17557 | "db_byzantine_quorum_size" | "db_pbft_view_change"
17558 | "db_honey_badger_step" | "db_avalanche_query_step"
17559 | "db_quorum_intersection_check" | "db_anti_entropy_step"
17560 | "db_merkle_node_hash" | "db_merkle_path_verify"
17561 | "db_gossip_fanout_step" | "db_anti_entropy_pull_step"
17562 | "db_split_brain_check" | "db_clock_skew_estimate"
17563 | "db_freshness_score" | "db_read_repair_step"
17564 | "db_hinted_handoff_step" | "db_compaction_score"
17565 | "db_levelled_compaction_step" | "db_size_tiered_compaction"
17566 | "db_universal_compaction_step" | "db_write_amplification"
17567 | "db_read_amplification" | "db_space_amplification"
17568 | "db_block_cache_hit_rate" | "db_page_cache_eviction_age"
17569 | "db_wal_fsync_cost" | "db_group_commit_count"
17570 | "db_replica_lag_threshold" | "db_synchronous_commit_check"
17571 | "db_async_commit_check" | "db_eventual_consistency_check"
17572 | "db_strong_consistency_check" | "db_linearizability_check"
17573 | "db_causal_consistency_check"
17574 | "net_tcp_cwnd_step" | "net_tcp_ssthresh_update"
17576 | "net_tcp_reno_step" | "net_tcp_cubic_step"
17577 | "net_tcp_bbr_step" | "net_tcp_vegas_step"
17578 | "net_tcp_westwood_step" | "net_tcp_compound_step"
17579 | "net_tcp_dctcp_step" | "net_tcp_yeah_step"
17580 | "net_tcp_htcp_step" | "net_tcp_hybla_step"
17581 | "net_tcp_illinois_step" | "net_tcp_lp_step"
17582 | "net_tcp_scalable_step" | "net_tcp_veno_step"
17583 | "net_aiad_step" | "net_aimd_step"
17584 | "net_miad_step" | "net_mimd_step"
17585 | "net_aqm_red_drop_prob" | "net_aqm_codel_target"
17586 | "net_aqm_pie_drop_rate" | "net_aqm_fq_codel_step"
17587 | "net_aqm_blue_step" | "net_aqm_choke_step"
17588 | "net_aqm_sfq_step" | "net_aqm_drr_step"
17589 | "net_aqm_wrr_step" | "net_token_rate_limit"
17590 | "net_traffic_shaper_step" | "net_priority_queue_index"
17591 | "net_packet_loss_estimate" | "net_jitter_estimate"
17592 | "net_latency_avg" | "net_rtt_smoothed"
17593 | "net_rtt_variation" | "net_rto_compute"
17594 | "net_bandwidth_delay_product" | "net_path_capacity_kleinrock"
17595 | "net_loss_rate_to_throughput" | "net_throughput_padhye"
17596 | "net_throughput_mathis" | "net_throughput_response"
17597 | "net_router_buffer_size" | "net_drop_tail_check"
17598 | "net_burst_size_compute" | "net_packet_pacing_step"
17599 | "net_link_capacity_share" | "net_proportional_fair_share"
17600 | "net_max_min_fair_step" | "net_alpha_fair_step"
17601 | "net_kelly_pricing_step" | "net_network_utility_max"
17602 | "net_lyapunov_drift_plus_penalty" | "net_backpressure_step"
17603 | "net_max_weight_match" | "net_qcsma_propose"
17604 | "net_csma_back_off" | "net_alohanet_throughput"
17605 | "net_slotted_aloha_throughput" | "net_csma_efficiency"
17606 | "net_token_ring_efficiency" | "net_polling_efficiency"
17607 | "net_radio_path_loss" | "net_friis_received_power"
17608 | "net_two_ray_ground_loss" | "net_okumura_hata_loss"
17609 | "net_log_distance_path" | "net_shadowing_normal"
17610 | "net_rician_k_factor" | "net_rayleigh_envelope"
17611 | "net_doppler_shift" | "net_capacity_shannon"
17612 | "net_mimo_capacity_step" | "net_zero_forcing_beam"
17613 | "net_mmse_beam_step" | "net_water_filling_power"
17614 | "net_amc_threshold_index" | "net_harq_combining_gain"
17615 | "net_turbo_decode_iter" | "net_ldpc_iteration_step"
17616 | "net_polar_decode_step" | "net_viterbi_step"
17617 | "net_bcjr_step" | "net_outage_probability"
17618 | "net_diversity_gain" | "net_array_gain"
17619 | "net_multiplexing_gain" | "net_coding_gain"
17620 | "net_pruning_gain" | "net_macro_diversity_step"
17621 | "net_micro_diversity_step" | "net_handoff_threshold"
17622 | "net_call_admission_check" | "net_blocking_probability"
17623 | "net_erlang_b_formula" | "net_erlang_c_formula"
17624 | "net_engset_formula" | "net_little_law_l"
17625 | "net_throughput_law" | "net_response_time_law"
17626 | "net_utilization_law" | "net_forced_flow_law"
17627 | "os_priority_aging_step" | "os_mlfq_demote_step"
17629 | "os_mlfq_promote_step" | "os_round_robin_quantum"
17630 | "os_completely_fair_vruntime" | "os_lottery_ticket_count"
17631 | "os_stride_pass_step" | "os_eevdf_eligible"
17632 | "os_cfs_load_balance_step" | "os_eas_energy_estimate"
17633 | "os_smt_threading_share" | "os_numa_node_distance"
17634 | "os_cpu_affinity_score" | "os_thread_migration_cost"
17635 | "os_load_average_decay" | "os_runqueue_depth"
17636 | "os_io_scheduler_deadline" | "os_io_scheduler_cfq_step"
17637 | "os_io_scheduler_noop_step" | "os_io_scheduler_bfq_step"
17638 | "os_io_scheduler_kyber_step" | "os_io_scheduler_mq_deadline"
17639 | "os_anticipation_window" | "os_elevator_step"
17640 | "os_disk_seek_time" | "os_disk_rotational_lat"
17641 | "os_disk_transfer_time" | "os_pre_fetch_window"
17642 | "os_buffer_cache_pages" | "os_dirty_page_threshold"
17643 | "os_writeback_step" | "os_swappiness_factor"
17644 | "os_kswapd_wake_threshold" | "os_oom_score_step"
17645 | "os_page_replacement_lru" | "os_page_replacement_clock"
17646 | "os_page_replacement_2q" | "os_working_set_size"
17647 | "os_thrashing_threshold" | "os_demand_paging_step"
17648 | "os_copy_on_write_check" | "os_zero_page_optimization"
17649 | "os_huge_page_threshold" | "os_transparent_hugepage"
17650 | "os_kasan_shadow_offset" | "os_kfence_check"
17651 | "os_kfence_alloc_index" | "os_slub_object_size_round"
17652 | "os_slab_color_offset" | "os_per_cpu_cache_size"
17653 | "os_buddy_order_pick" | "os_compact_memory_step"
17654 | "os_kvm_vmcs_field_offset" | "os_apic_irq_priority"
17655 | "os_msi_x_vector_count" | "os_iommu_domain_step"
17656 | "os_pci_bus_address" | "os_acpi_state_transition"
17657 | "os_cpufreq_governor_step" | "os_intel_pstate_target"
17658 | "os_amd_pstate_target" | "os_thermal_zone_trip"
17659 | "os_throttle_temperature" | "os_battery_capacity_pct"
17660 | "os_powertop_score" | "os_idle_state_select"
17661 | "os_c_state_residency" | "os_p_state_voltage"
17662 | "os_dvfs_step" | "os_voltage_scaling_step"
17663 | "os_frequency_scaling_step" | "os_inotify_event_count"
17664 | "os_epoll_ctl_count" | "os_io_uring_sqe_count"
17665 | "os_io_uring_cqe_count" | "os_kqueue_event_count"
17666 | "os_systemd_journal_size" | "os_dmesg_severity_level"
17667 | "os_audit_event_priority" | "os_apparmor_profile_active"
17668 | "os_selinux_context_match" | "os_smack_label_compare"
17669 | "os_capability_check" | "os_seccomp_filter_step"
17670 | "os_namespace_isolation" | "os_cgroup_v1_count"
17671 | "os_cgroup_v2_count" | "os_pid_max_value"
17672 | "os_thread_max_value" | "os_file_max_value"
17673 | "os_open_files_count" | "os_socket_max_value"
17674 | "os_inotify_max_watches" | "os_oom_kill_score"
17675 | "os_zswap_compress_ratio" | "os_zram_compress_ratio"
17676 | "os_swap_pressure_score" | "os_pressure_stall_step"
17677 | "os_psi_avg10_step" | "os_psi_avg60_step"
17678 | "os_psi_avg300_step" | "os_load_proc_avg"
17679 | "os_load_user_avg" | "os_load_iowait_avg"
17680 | "sec_argon2_memcost" | "sec_argon2_timecost"
17682 | "sec_argon2_parallelism" | "sec_argon2_block_step"
17683 | "sec_pbkdf2_iter" | "sec_scrypt_n_param"
17684 | "sec_scrypt_r_param" | "sec_scrypt_p_param"
17685 | "sec_balloon_hash_step" | "sec_yescrypt_step"
17686 | "sec_bcrypt_cost_factor" | "sec_bcrypt_round_step"
17687 | "sec_password_strength_zxcvbn" | "sec_haveibeenpwned_check"
17688 | "sec_diceware_word_index" | "sec_xkcd_passphrase_score"
17689 | "sec_passphrase_entropy" | "sec_chosen_charset_strength"
17690 | "sec_keystroke_timing_var" | "sec_2fa_totp_window"
17691 | "sec_totp_drift_check" | "sec_hotp_counter_step"
17692 | "sec_yubikey_otp_check" | "sec_webauthn_attestation_check"
17693 | "sec_fido2_assertion_check" | "sec_certificate_chain_depth"
17694 | "sec_revocation_ocsp_check" | "sec_crl_age_seconds"
17695 | "sec_pki_path_validate" | "sec_x509_subject_match"
17696 | "sec_san_match_count" | "sec_basic_constraints_ca"
17697 | "sec_pinning_compare" | "sec_certificate_transparency"
17698 | "sec_dane_tlsa_match" | "sec_hpkp_pin_match"
17699 | "sec_csp_directive_match" | "sec_csrf_token_match"
17700 | "sec_cors_origin_match" | "sec_xss_filter_score"
17701 | "sec_html_escape_check" | "sec_url_safe_encode_check"
17702 | "sec_path_traversal_detect" | "sec_sqli_pattern_score"
17703 | "sec_xxe_pattern_score" | "sec_xxe_dtd_check"
17704 | "sec_command_injection_score" | "sec_idor_check"
17705 | "sec_jwt_alg_safe" | "sec_jwt_kid_match"
17706 | "sec_jwt_signature_verify" | "sec_oauth2_state_validate"
17707 | "sec_oauth2_pkce_step" | "sec_oauth_nonce_check"
17708 | "sec_session_lifetime" | "sec_idle_timeout_step"
17709 | "sec_login_throttle_step" | "sec_account_lockout_step"
17710 | "sec_password_history_check" | "sec_complexity_policy_score"
17711 | "sec_dictionary_attack_check" | "sec_brute_force_attempts"
17712 | "sec_credential_stuffing_score" | "sec_kerberos_ticket_age"
17713 | "sec_kerberos_pac_check" | "sec_kerberos_pre_auth"
17714 | "sec_ldap_bind_step" | "sec_radius_auth_step"
17715 | "sec_diameter_avp_step" | "sec_saml_assertion_age"
17716 | "sec_oidc_id_token_age" | "sec_acme_dns_challenge"
17717 | "sec_dnssec_signature_check" | "sec_spf_pass_check"
17718 | "sec_dkim_signature_check" | "sec_dmarc_policy_check"
17719 | "sec_arc_chain_step" | "sec_smtp_ssl_check"
17720 | "sec_imap_starttls_check" | "sec_pop3_security_step"
17721 | "sec_tls_alert_severity" | "sec_tls13_handshake_step"
17722 | "sec_tls12_handshake_step" | "sec_tls11_deprecation_check"
17723 | "sec_ssl3_disabled_check" | "sec_cipher_suite_strength"
17724 | "sec_cbc_mac_block_count" | "sec_gcm_iv_unique_check"
17725 | "sec_chachapoly_nonce_check" | "sec_x25519_clamping_step"
17726 | "sec_ed25519_signature_step" | "sec_ed448_signature_step"
17727 | "sec_p384_curve_step" | "sec_secp256k1_step"
17728 | "sec_blake3_chunk_step" | "sec_keccak_round_step"
17729 | "sec_sha3_padding_step" | "sec_argon2_state_advance"
17730 | "sec_chacha20_quarterround" | "sec_aes_round_step"
17731 | "sec_aes_keyschedule_step" | "sec_des_round_step"
17732 | "sec_blowfish_round_step" | "sec_serpent_round_step"
17733 | "sec_twofish_round_step"
17734 | "fixed_from_gregorian" | "gregorian_from_fixed"
17736 | "fixed_from_julian" | "julian_from_fixed"
17737 | "iso_week_date" | "hebrew_leap_year"
17738 | "hebrew_year_length" | "fixed_from_hebrew"
17739 | "islamic_leap_year" | "fixed_from_islamic"
17740 | "persian_arithmetic_leap" | "fixed_from_persian"
17741 | "coptic_from_fixed" | "ethiopic_from_fixed"
17742 | "french_revolutionary_leap" | "fixed_from_french"
17743 | "chinese_year_zodiac" | "chinese_lunation_winter"
17744 | "hindu_solar_year" | "hindu_lunisolar_month"
17745 | "maya_long_count_from_fixed" | "mayan_haab_from_fixed"
17746 | "mayan_tzolkin_from_fixed" | "badi_year_from_fixed"
17747 | "bahai_from_fixed" | "easter_gregorian_year"
17748 | "easter_orthodox_year" | "easter_julian_year"
17749 | "day_of_week_zeller" | "iso_day_number"
17750 | "weekday_name_short" | "leap_year_gregorian"
17751
17752 | "dnorm" | "dt" | "df_dist" | "dchisq"
17754 | "glm" | "aov" | "shapiro_wilk" | "anderson_darling"
17755 | "kolmogorov_smirnov" | "spearmanr" | "kendalltau" | "pearsonr"
17756 | "mannwhitneyu" | "wilcoxon" | "kruskal_h"
17757
17758 | "iota_n" | "reduce_axis" | "scan_axis" | "fold_axis"
17760 | "rotate_axis" | "transpose_axis" | "reshape_dim"
17761 | "encode_base" | "decode_base" | "nub_list" | "nub_count"
17762 | "membership_idx" | "deal_n_k" | "roll_n"
17763 | "permute_idx" | "invert_perm"
17764
17765 | "julian_day" | "jd_to_calendar" | "tt_to_tdb"
17767 | "ra_dec_to_alt_az" | "alt_az_to_ra_dec"
17768 | "precession_iau2006" | "nutation_iau2000a"
17769 | "aberration_annual" | "proper_motion_apply"
17770 | "parallax_correction" | "sun_position_low" | "sun_distance_au"
17771 | "moon_position_low" | "moon_phase_age" | "lunation_index"
17772 | "eclipse_magnitude" | "saros_cycle" | "metonic_cycle"
17773 | "orbit_kepler3" | "orbital_period_au" | "orbit_eccentric_anomaly"
17774 | "escape_velocity_body" | "hill_sphere_radius" | "tisserand_param"
17775 | "tle_mean_motion" | "sgp4_propagate_step" | "airy_disk_radius"
17776 | "rayleigh_criterion" | "strehl_ratio" | "au_to_km"
17777
17778 | "elo_expected" | "elo_update" | "glicko_rating"
17780 | "trueskill_update" | "trueskill_match_quality"
17781 | "pythagorean_expectation" | "war_above_replacement"
17782 | "woba_weight" | "wrc_plus" | "ops_plus" | "era_plus"
17783 | "fip" | "xfip" | "siera" | "babip" | "wpa"
17784 | "win_probability" | "leverage_index" | "clutch_score"
17785 | "shooting_pct" | "save_pct" | "corsi_for" | "fenwick_for"
17786 | "goals_above_avg" | "tackle_efficiency" | "yards_per_attempt"
17787 | "qbr_metric" | "epa_per_play"
17788
17789 | "vlookup" | "hlookup" | "xlookup" | "index_match"
17791 | "indirect" | "choose" | "offset"
17792 | "sumif" | "countif" | "averageif"
17793 | "sumifs" | "countifs" | "averageifs"
17794 | "sumproduct" | "rank_eq" | "rank_avg" | "percentrank"
17795 | "quartile_inc" | "quartile_exc"
17796 | "xnpv" | "ppmt" | "ipmt" | "rate"
17797 | "macauley_duration" | "convexity" | "yield_to_maturity"
17798 | "accrued_interest" | "clean_price" | "dirty_price"
17799 | "coupon_count" | "skill_score" | "reliability_diagram"
17800 | "taylor_diagram_score"
17801
17802 | "geohash_neighbors" | "h3_index" | "h3_geo_to_h3"
17804 | "h3_h3_to_geo" | "h3_k_ring" | "h3_neighbor" | "h3_resolution"
17805 | "s2_cell_id" | "s2_cell_at_lat_lng" | "s2_cell_neighbors"
17806 | "utm_from_lat_lng" | "utm_to_lat_lng"
17807 | "mgrs_encode" | "mgrs_decode"
17808 | "lat_lng_to_xy_mercator" | "lat_lng_to_xy_lambert"
17809 | "haversine_dist" | "vincenty_dist" | "andoyer_dist"
17810 | "rhumb_line_bearing"
17811 | "destination_point" | "tile_xyz_to_lat_lng" | "lat_lng_to_tile_xyz"
17812 | "polygon_winding_order" | "point_in_polygon_ray"
17813 | "point_in_polygon_winding" | "segment_intersection"
17814 | "segment_distance_point" | "convex_hull_chan"
17815
17816 | "pid_anti_windup" | "pid_ziegler_nichols"
17818 | "smith_predictor_step" | "lqr_gain_continuous"
17819 | "lqr_gain_discrete" | "lqg_step" | "h_infinity_norm"
17820 | "bode_gain_margin" | "bode_phase_margin"
17821 | "nyquist_encirclement" | "nichols_chart_step"
17822 | "servo_position_velocity" | "servo_torque_step"
17823 | "imu_madgwick_step" | "imu_mahony_step" | "quaternion_from_imu"
17824 | "denavit_hartenberg_h" | "forward_kinematics_dh"
17825 | "inverse_kinematics_2link" | "jacobian_2dof"
17826 | "manipulability_yoshikawa" | "singularity_check_2link"
17827 | "path_dubins_lsl" | "path_dubins_rsr" | "path_reeds_shepp"
17828 | "rrt_extend" | "rrt_star_rewire" | "prm_node_connect"
17829
17830 | "life_expectancy_e0" | "force_of_mortality" | "select_ultimate"
17832 | "annuity_due_an" | "annuity_immediate_an"
17833 | "term_life_a_n_t" | "whole_life_a"
17834 | "endowment_pure_e" | "endowment_combined_a"
17835 | "premium_net" | "level_premium"
17836 | "reserve_prospective" | "reserve_retrospective"
17837 | "gross_premium_load" | "experience_factor"
17838 | "mortality_table_q" | "select_period_step"
17839 | "multi_decrement_q" | "multi_state_pij"
17840 | "credibility_buhlmann" | "loss_severity_lognormal"
17841 | "loss_frequency_poisson" | "ruin_probability_lundberg"
17842 | "cramer_lundberg_step" | "bornhuetter_ferguson"
17843 | "chain_ladder_step" | "ibnr_estimate" | "run_off_triangle_step"
17844
17845 | "r_naught_basic" | "r_effective_t" | "doubling_time_growth"
17847 | "sirs_step" | "seirs_step" | "susceptible_to_infected"
17848 | "attack_rate" | "vaccination_coverage_required"
17849 | "cfr_case_fatality" | "ifr_infection_fatality"
17850 | "dalys_disability_weight" | "qaly_lifetime" | "ylll_pml"
17851 | "rt_serial_interval" | "generation_time_step"
17852 | "gini_inequality_health" | "standardized_mortality_smr"
17853 | "indirect_age_adjusted" | "direct_age_adjusted"
17854 | "odds_ratio_2x2" | "risk_ratio_2x2" | "number_needed_to_treat"
17855 | "attributable_fraction_pop" | "preventive_fraction"
17856 | "contact_tracing_eff" | "cluster_attack_rate"
17857 | "transmission_pair_index"
17858
17859 | "tar_header_checksum" | "tar_pad_512" | "tar_member_record"
17861 | "zip_local_header" | "zip_central_dir" | "zip_eocd"
17862 | "gzip_member_step" | "gzip_crc32_init" | "gzip_isize"
17863 | "deflate_dynamic_huffman" | "deflate_static_block"
17864 | "lz4_block_step" | "lz4_match_offset"
17865 | "zstd_frame_header" | "brotli_huffman_table"
17866 | "brotli_meta_block" | "lzma_range_step"
17867 | "quoted_printable_encode" | "uuencode_step"
17868 | "modhex_encode" | "percent_encode_full"
17869 | "punycode_encode" | "idn_to_ascii" | "idn_to_unicode"
17870 | "msgpack_pack_int" | "msgpack_pack_str"
17871 | "cbor_encode_uint" | "cbor_encode_str"
17872
17873 | "molecular_weight_compound" | "molarity_dilution"
17875 | "gas_constant_value" | "eyring_rate" | "van_t_hoff_kp"
17876 | "henderson_buffer" | "titration_ph_endpoint"
17877 | "isoelectric_point_protein" | "ka_to_pka" | "pkb_to_kb"
17878 | "amphoteric_check" | "oxidation_number"
17879 | "half_reaction_balance" | "redox_potential_cell"
17880 | "electrolysis_mass" | "spectrophotometer_beer_lambert"
17881 | "epsilon_extinction" | "transmittance_to_a"
17882 | "crystal_field_ligand" | "jahn_teller_check"
17883 | "vsepr_geometry" | "lewis_dot_count"
17884 | "formal_charge" | "resonance_count"
17885 | "ramachandran_phi_psi" | "rg_radius_of_gyration"
17886 | "spectroscopic_factor" | "avogadro_constant"
17887
17888 | "cents_between_freqs" | "note_name_from_midi"
17890 | "interval_quality_size" | "scale_pitches_major"
17891 | "scale_pitches_minor" | "mode_pitches_dorian"
17892 | "mode_pitches_phrygian" | "mode_pitches_lydian"
17893 | "chord_root_inversion" | "chord_quality_classify"
17894 | "chord_voicing_close" | "key_signature_sharps"
17895 | "key_signature_flats" | "tempo_to_ms" | "beat_to_seconds"
17896 | "time_sig_subdivision" | "equal_tempered_freq"
17897 | "just_intonation_freq" | "pythagorean_freq"
17898 | "mean_tone_freq" | "werckmeister_iii" | "kirnberger_iii"
17899 | "dynamics_db_level" | "harmonics_partial"
17900
17901 | "moment_magnitude_mw" | "richter_local_ml"
17903 | "surface_wave_ms" | "body_wave_mb"
17904 | "gutenberg_richter_b" | "omori_aftershock"
17905 | "pga_attenuation" | "arias_intensity" | "shake_map_pga"
17906 | "liquefaction_potential_index" | "spt_n_correction"
17907 | "mineral_mohs_hardness" | "streak_color_index"
17908 | "specific_gravity_water" | "feldspar_classify"
17909 | "silicate_classify" | "igneous_qapf"
17910 | "metamorphic_grade" | "crustal_density_depth"
17911 | "pwave_velocity_depth" | "swave_velocity_depth"
17912 | "gradient_geothermal" | "heat_flow_radiogenic"
17913
17914 | "dgemm" | "sgemm" | "zgemm" | "cgemm"
17916 | "dgemv" | "sgemv" | "dtrsm" | "strsm"
17917 | "dgesv" | "dgetrf" | "dgeqrf" | "dgesvd"
17918 | "dsyevd" | "dpotrf" | "daxpy" | "ddot"
17919 | "dnrm2" | "dscal" | "dasum" | "idamax"
17920 | "dsyrk" | "dgerqf" | "dorgqr" | "dorglq"
17921 | "drot" | "drotg" | "dpbsv" | "dgbsv"
17922 | "dtbsv" | "dtrsv" | "ddrot" | "dgemm3m"
17923 | "dgels" | "dgelsd"
17924
17925 | "cnf_unit_propagate" | "cnf_pure_literal_elim"
17927 | "cnf_dpll_branch" | "dpll_clause_learning"
17928 | "two_watched_literals" | "walksat_step"
17929 | "resolution_step" | "subsumption_check"
17930 | "tableau_branch_close" | "sequent_left_intro"
17931 | "sequent_right_intro" | "nbe_normalize"
17932 | "church_numeral_n" | "encode_pair" | "encode_succ"
17933 | "simply_typed_check" | "hindley_milner_step"
17934 | "unification_robinson" | "bdd_apply" | "bdd_restrict"
17935 | "bdd_quantify" | "aig_simplify_step"
17936 | "smt_qf_lia_solve_step" | "smt_qf_uf_combine"
17937 | "model_checking_ctl" | "model_checking_ltl"
17938 | "bisimulation_step" | "coq_tactic_apply"
17939 | "coq_unify_term" | "refl_check" | "sym_check" | "trans_check"
17940
17941 | "nfa_to_dfa" | "subset_construction"
17943 | "dfa_minimize_hopcroft" | "regex_to_nfa_thompson"
17944 | "glushkov_construction" | "brzozowski_derivative"
17945 | "ll1_first_set" | "ll1_follow_set" | "ll1_predict_table"
17946 | "lr0_items_step" | "lalr_lookahead_compute"
17947 | "lr1_canonical_collection"
17948 | "earley_scan" | "earley_predict" | "earley_complete"
17949 | "packrat_parse_step" | "ascent_parser_step"
17950 | "pratt_parse_step" | "shunting_yard_step"
17951 | "regex_compile_thompson" | "regex_match_dfa"
17952 | "lex_keyword_classify"
17953 | "peg_seq" | "peg_choice" | "peg_repeat" | "peg_lookahead"
17954 | "dfa_simulate_step" | "bytecode_disasm_step"
17955 | "ssa_phi_insert" | "dom_tree_idom" | "dominance_frontier"
17956
17957 | "porter_stem_step" | "snowball_stem_english"
17959 | "snowball_stem_french" | "lemmatize_wordnet"
17960 | "lemmatize_lemmy" | "stem_lancaster"
17961 | "soundex_phonetic" | "metaphone_phonetic"
17962 | "caverphone_2" | "nysiis_phonetic"
17963 | "match_rating_codex" | "daitch_mokotoff"
17964 | "viterbi_pos_tag" | "forward_backward_pos"
17965 | "crf_log_likelihood" | "bigram_perplexity"
17966 | "trigram_perplexity" | "ner_bilou_decode"
17967 | "constituency_cyk" | "dependency_parse_eisner"
17968 | "transition_arc_eager" | "transition_arc_standard"
17969 | "word_alignment_ibm1" | "word_alignment_ibm2"
17970 | "lexicalized_parse" | "coreference_singleton"
17971 | "anaphora_distance" | "head_finding_collins"
17972 | "tree_kernel_collins"
17973
17974 | "btrim" | "translate" | "ascii"
17976 | "regexp_split" | "regexp_matches" | "regexp_replace"
17977 | "json_build_object" | "jsonb_set"
17978 | "json_array_length" | "json_extract_path"
17979 | "json_strip_nulls" | "jsonb_pretty"
17980 | "jsonb_path_query" | "json_each"
17981 | "jsonb_array_length" | "jsonb_object_keys"
17982 | "jsonb_typeof" | "array_to_jsonb"
17983 | "ts_match" | "ts_rank" | "ts_headline"
17984 | "substring_similarity" | "levenshtein_dist"
17985 | "word_similarity" | "strict_word_similarity"
17986 | "hstore_to_array" | "array_to_hstore"
17987 | "string_agg" | "array_agg"
17988 | "corr_agg" | "covar_pop" | "covar_samp"
17989 | "regr_slope" | "regr_intercept" | "regr_r2"
17990 | "percentile_cont" | "percentile_disc" | "mode_agg"
17991 | "array_to_string" | "array_position" | "array_positions"
17992 | "array_remove" | "array_replace"
17993 | "xmlforest" | "xmlagg"
17994
17995 | "zadd" | "zrem" | "zrangebyscore"
17997 | "zrank" | "zrevrank" | "zincrby"
17998 | "zcard" | "zcount" | "zlexcount"
17999 | "lpush" | "rpush" | "lrange" | "lrem"
18000 | "hset" | "hget" | "hgetall" | "hlen"
18001 | "hkeys" | "hvals" | "hmset" | "hincrby"
18002 | "sadd" | "srem" | "smembers"
18003 | "sinter" | "sunion" | "sdiff"
18004 | "scard" | "sismember" | "spop"
18005 | "setex" | "setnx" | "expire"
18006 | "ttl" | "pttl" | "persist"
18007 | "incr" | "decr" | "incrby" | "decrby"
18008 | "getset" | "mset" | "mget" | "renamenx"
18009 | "dbsize" | "type_redis" | "exists_key"
18010 | "strlen" | "getrange" | "setrange" | "append_redis"
18011 | "bitcount" | "bitop" | "bitpos"
18012 | "pfadd" | "pfcount"
18013 | "geoadd" | "geodist" | "geohash"
18014 | "xadd" | "xlen" | "xrange"
18015 | "object_encoding" | "debug_object" | "cluster_slots"
18016
18017 | "argpartition" | "bincount" | "nonzero_count"
18019 | "flatnonzero" | "searchsorted" | "digitize"
18020 | "histogram_bin_edges" | "unique_count"
18021 | "polyfit_rmse"
18022 | "ellipk" | "ellipe"
18023 | "hyp1f1" | "hyp2f1" | "mathieu_b"
18024 | "spherical_jn" | "spherical_yn"
18025 | "jv" | "yn" | "iv" | "kv"
18026 | "airyai" | "airybi"
18027 | "polygamma" | "trigamma" | "loggamma"
18028 | "factorial2" | "factorialk"
18029 | "owens_t" | "marcum_q" | "voigt_profile"
18030 | "chebyt" | "chebyu" | "sph_harm"
18031 | "wofz" | "erfcx" | "erfi" | "dawsn"
18032 | "interp1d"
18033 | "convolve_full" | "convolve_valid" | "correlate_full"
18034 | "kron_product"
18035 | "simpson_rule" | "romberg_quad" | "fixed_quad"
18036 | "ode45_step" | "ode_lsoda" | "solve_ivp_step"
18037 | "root_brentq" | "root_newton" | "root_secant"
18038 | "fmin_powell" | "fmin_cobyla"
18039
18040 | "cobb_douglas" | "ces_production"
18042 | "leontief_input" | "leontief_output"
18043 | "slutsky_decompose"
18044 | "marshallian_demand" | "hicksian_demand"
18045 | "expenditure_function" | "indirect_utility"
18046 | "gale_shapley_step" | "deferred_acceptance"
18047 | "top_trading_cycle" | "vcg_payment" | "myerson_optimal"
18048 | "gini_market" | "hhi_concentration"
18049 | "cournot_eq" | "stackelberg_eq" | "bertrand_eq"
18050 | "monopoly_lerner"
18051 | "consumer_surplus" | "producer_surplus"
18052 | "deadweight_loss" | "tax_incidence"
18053 | "pareto_efficiency" | "edgeworth_box_alloc"
18054 | "social_welfare_utilitarian"
18055 | "social_welfare_rawls" | "social_welfare_nash"
18056 | "arrow_independence"
18057 | "vickrey_auction" | "first_price_seal"
18058 | "english_auction" | "dutch_auction"
18059 | "core_coalition" | "stable_matching_count"
18060 | "gale_optimal" | "pareto_dominance"
18061 | "lerner_index"
18062 | "price_elasticity" | "supply_elasticity"
18063 | "income_elasticity" | "engel_curve" | "cross_elasticity"
18064 | "diff_in_diff" | "did_estimator" | "rdd_estimate"
18065 | "hann_w" | "hamming_w" | "blackman_w" | "barthann_w"
18067 | "nuttall_w" | "flattop_w" | "parzen_window" | "tukey_w"
18068 | "taylor_window" | "dpss_window" | "kaiserord_step"
18069 | "butter_lp_re" | "butter_hp_mag"
18070 | "cheby1_lp" | "cheby2_lp" | "ellip_lp" | "bessel_lp"
18071 | "notch_filter"
18072 | "sosfilt_step" | "lfilter_zi_init" | "filtfilt_pad"
18073 | "freqz_eval" | "freqs_eval" | "group_delay_eval"
18074 | "impulse_response_n"
18075 | "tf2zpk_step" | "zpk2tf_step" | "tf2sos_step"
18076 | "zpk2sos_step" | "sos2tf_step"
18077 | "bilinear_xform" | "bilinear_zpk_xform"
18078 | "firwin_lowpass" | "firwin_highpass"
18079 | "firwin_bandpass" | "firwin_bandstop"
18080 | "firwin2_freq" | "remez_design"
18081 | "stft_step" | "istft_step"
18082 | "cwt_morlet" | "ricker_wavelet" | "mexican_hat_wavelet"
18083 | "coherence_xy" | "csd_xy" | "welch_psd_avg"
18084 | "periodogram_basic" | "lombscargle_freq"
18085 | "hilbert_signal" | "envelope_amplitude"
18086 | "deconvolve_step" | "fftconvolve_step" | "oaconvolve_step"
18087 | "upfirdn_step" | "resample_poly_step" | "decimate_step"
18088 | "savgol_coef" | "detrend_linear"
18089 | "wiener_filter" | "medfilt_1d" | "peak_widths_at"
18090 | "dijkstra_relax" | "bellman_ford_relax"
18092 | "floyd_warshall_step" | "johnson_reweight"
18093 | "astar_search" | "bidirectional_dijkstra"
18094 | "yen_k_shortest" | "ida_star"
18095 | "bfs_count" | "dfs_postorder_done" | "topo_kahn_step"
18096 | "tarjan_scc_step" | "kosaraju_step"
18097 | "kruskal_step" | "prim_step" | "boruvka_step"
18098 | "reverse_delete_step"
18099 | "ford_fulkerson_step" | "edmonds_karp_bfs"
18100 | "dinic_step" | "push_relabel_relabel"
18101 | "stoer_wagner_step" | "karger_step"
18102 | "pagerank_iter" | "hits_authority" | "hits_hub"
18103 | "personalized_pagerank"
18104 | "centrality_degree" | "centrality_closeness"
18105 | "centrality_betweenness" | "centrality_eigenvector"
18106 | "centrality_katz" | "harmonic_centrality" | "load_centrality"
18107 | "clustering_coefficient" | "triangles_count" | "transitivity"
18108 | "modularity_score" | "louvain_gain"
18109 | "label_propagation" | "girvan_newman"
18110 | "articulation_point" | "bridge_edge"
18111 | "edge_connectivity" | "vertex_connectivity"
18112 | "biconnected_components"
18113 | "gx_diameter" | "gx_radius" | "gx_eccentricity"
18114 | "warshall_step"
18115 | "tsp_held_karp" | "tsp_nn_step" | "tsp_christofides"
18116 | "graph_coloring_greedy" | "welsh_powell"
18117 | "vf2_consistent" | "subgraph_isomorphism"
18118 | "hungarian_step" | "hopcroft_karp_step"
18119 | "bron_kerbosch"
18120 | "min_vertex_cover" | "max_independent_set"
18121 | "dominating_set_greedy" | "hamiltonian_path"
18122 | "min_steiner_tree" | "k_shortest_spanning"
18123 | "random_walk_hitting" | "simrank"
18124 | "df_groupby" | "df_aggregate" | "df_apply"
18126 | "df_transform" | "df_pivot" | "df_pivot_table"
18127 | "df_melt" | "df_stack" | "df_unstack"
18128 | "df_explode" | "df_get_dummies" | "df_crosstab"
18129 | "df_merge" | "df_join" | "df_concat"
18130 | "df_resample" | "df_rolling" | "df_expanding"
18131 | "df_ewm" | "df_shift" | "df_diff"
18132 | "df_pct_change" | "df_corr" | "df_cov"
18133 | "df_corrwith" | "df_describe" | "df_kurtosis"
18134 | "df_skew" | "df_sem" | "df_mad"
18135 | "df_dropna" | "df_fillna" | "df_interpolate"
18136 | "df_replace" | "df_isnull" | "df_notnull"
18137 | "df_sort_values" | "df_rank" | "df_quantile"
18138 | "df_value_counts" | "df_sample" | "df_nlargest"
18139 | "df_nsmallest" | "df_idxmax" | "df_idxmin"
18140 | "df_clip" | "df_round" | "df_to_datetime"
18141 | "df_to_timedelta" | "df_to_numeric" | "df_eval"
18142 | "df_query" | "df_filter" | "df_drop_duplicates"
18143 | "df_duplicated" | "df_set_index" | "df_reset_index"
18144 | "image_resize" | "image_grayscale" | "image_threshold"
18146 | "image_blur_gaussian" | "image_blur_box" | "image_sharpen"
18147 | "image_edge_canny" | "image_edge_sobel" | "image_edge_laplacian"
18148 | "image_dilate" | "image_erode" | "image_morphology_open"
18149 | "image_morphology_close" | "image_histogram" | "image_equalize"
18150 | "image_clahe" | "image_contrast" | "image_brightness"
18151 | "image_gamma" | "image_invert" | "image_sepia"
18152 | "image_posterize" | "image_solarize" | "convolve_2d"
18153 | "filter_median" | "filter_bilateral" | "filter_nlmeans"
18154 | "gabor_filter" | "hog_features" | "harris_corners"
18155 | "shi_tomasi_corners" | "sift_keypoints" | "orb_keypoints"
18156 | "surf_keypoints" | "template_match" | "face_detect_haar"
18157 | "watershed_segment" | "slic_superpixels" | "felzenszwalb_segment"
18158 | "graph_cut_segment" | "hough_lines" | "hough_circles"
18159 | "ransac_homography" | "optical_flow_lk" | "optical_flow_farneback"
18160 | "corner_subpix" | "image_rotate" | "image_flip_h"
18161 | "image_flip_v" | "image_emboss" | "image_motion_blur"
18162 | "arima_fit" | "arima_forecast" | "arma_order_select"
18164 | "sarimax_fit" | "garch_fit" | "ewma_smooth"
18165 | "holt_winters_additive" | "holt_winters_multiplicative" | "kalman_filter_step"
18166 | "kalman_smoother_step" | "var_fit" | "vecm_fit"
18167 | "johansen_test" | "phillips_perron" | "adfuller"
18168 | "kpss_test" | "breusch_godfrey" | "ljung_box_q"
18169 | "durbin_watson_d" | "granger_causality" | "cointegration_eg"
18170 | "seasonal_decompose" | "stl_decompose" | "acf_basis"
18171 | "pacf_basis" | "moving_average_filter" | "exp_smooth_simple"
18172 | "exp_smooth_double" | "markov_switching_ar" | "markov_switching_mr"
18173 | "arch_lm" | "state_space_kalman" | "ucm_unobserved_components"
18174 | "spectral_density_estimate" | "bayesian_step" | "pivoted_cholesky_var"
18175 | "sk_logistic_predict" | "sk_logistic_fit" | "sk_random_forest_fit"
18177 | "sk_gbt_fit" | "sk_xgb_fit" | "sk_lightgbm_fit"
18178 | "sk_svm_fit" | "sk_kmeans_fit" | "sk_dbscan_fit"
18179 | "sk_agglomerative_fit" | "sk_pca_fit" | "sk_tsne_fit"
18180 | "sk_umap_fit" | "sk_isolation_forest_fit" | "sk_lof_fit"
18181 | "sk_kfold_split" | "sk_stratified_kfold" | "sk_cross_val_score"
18182 | "sk_grid_search" | "sk_random_search" | "sk_bayes_search"
18183 | "sk_pipeline_fit" | "sk_standard_scaler" | "sk_min_max_scaler"
18184 | "sk_robust_scaler" | "sk_quantile_transform" | "sk_power_transform"
18185 | "sk_one_hot" | "sk_ordinal_encode" | "sk_label_encode"
18186 | "sk_tfidf" | "sk_count_vectorize" | "sk_silhouette"
18187 | "sk_calinski_harabasz" | "sk_davies_bouldin" | "sk_adjusted_rand"
18188 | "sk_mutual_info" | "sk_lda_topic" | "sk_nmf_topic"
18189 | "sk_word2vec_train" | "sk_doc2vec_train" | "sk_naive_bayes_predict"
18190 | "sk_knn_predict" | "sk_decision_tree_split"
18191 | "qubit_x" | "qubit_y" | "qubit_z"
18193 | "qubit_h" | "qubit_s" | "qubit_t"
18194 | "qubit_rx" | "qubit_ry" | "qubit_rz"
18195 | "qubit_u3" | "qubit_u2" | "qubit_u1"
18196 | "qubit_phase" | "qubit_cnot" | "qubit_cz"
18197 | "qubit_swap" | "qubit_ccx" | "qubit_measure"
18198 | "qubit_reset" | "bell_state" | "ghz_state"
18199 | "w_state" | "qft" | "inverse_qft"
18200 | "grover_iter" | "shor_period" | "vqe_step"
18201 | "qaoa_step" | "qpe_iteration" | "pauli_string_expect"
18202 | "circuit_depth" | "circuit_width" | "gate_decompose"
18203 | "ancilla_alloc" | "bloch_sphere_x" | "bloch_sphere_z"
18204 | "density_matrix_purity_q" | "entanglement_entropy" | "quantum_teleportation"
18205 | "superdense_coding" | "noise_model_depolarize"
18206 | "mirr_excel" | "accrint" | "cumipmt"
18208 | "cumprinc" | "dollarde" | "dollarfr"
18209 | "received" | "yieldmat" | "yielddisc"
18210 | "duration_macaulay" | "mduration" | "odddyield"
18211 | "disc_excel" | "effect" | "nominal"
18212 | "intrate" | "price_disc" | "cityhash64"
18213 | "farmhash_64" | "metro_hash_64" | "spookyhash_128"
18214 | "t1ha" | "highway_hash" | "fnv0_32"
18215 | "lose_lose"
18216 | "oat_hash" | "lz4_encode_block" | "snappy_encode"
18217 | "zstd_encode_step" | "brotli_encode_meta" | "lzma_encode_step"
18218 | "bz2_encode_step" | "lzo_encode_step" | "deflate_encode_huffman"
18219 | "lzw_encode" | "gzip_encode_step" | "uri_template_expand"
18220 | "uri_resolve" | "uri_normalize" | "percent_decode_url"
18221 | "url_encode_form" | "url_decode_form" | "punycode_decode_step"
18222 | "idn_normalize" | "url_origin" | "etag_validate"
18223 | "cache_control_parse" | "vary_match" | "content_negotiate"
18224 | "accept_lang_pick" | "range_header_parse" | "if_match_check"
18225 | "if_none_match_check" | "digest_auth_quote" | "www_auth_parse"
18226 | "iso8601_duration_parse" | "iso8601_duration_to_seconds" | "rrule_next_occurrence"
18228 | "cron_next_fire" | "date_round_iso" | "week_number_iso"
18229 | "fiscal_year_us" | "age_at_date" | "easter_western"
18230 | "easter_orthodox_year_2" | "chinese_new_year" | "solstice_winter"
18231 | "equinox_spring" | "rgb_to_oklab" | "oklab_to_rgb"
18232 | "rgb_to_cmyk" | "cmyk_to_rgb" | "rgb_to_xyz"
18233 | "xyz_to_rgb" | "rgb_to_yuv" | "yuv_to_rgb"
18234 | "luminance_relative" | "contrast_ratio" | "wcag_pass"
18235 | "color_temperature_kelvin" | "delta_e76" | "delta_e94"
18236 | "delta_e2000" | "color_blend_alpha" | "isbn10_check"
18237 | "isbn13_check" | "ean13_check" | "upc_check"
18238 | "eth_addr_check" | "btc_addr_check" | "ssn_check"
18239 | "vin_check" | "imei_check" | "iban_check"
18240 | "cusip_check" | "kde_silverman_bw" | "kde_scott_bw"
18241 | "kde_bandwidth_lscv" | "kde_epanechnikov" | "kde_gaussian_2d"
18242 | "kde_uniform" | "kde_triangular" | "kde_biweight"
18243 | "kde_triweight" | "kde_cosine" | "kde_logistic_kernel"
18244 | "mod_exp" | "modexp" | "powmod"
18246 | "mod_inv" | "modinv" | "chinese_remainder" | "crt"
18247 | "miller_rabin" | "millerrabin" | "is_probable_prime"
18248 | "derangements" | "stirling2" | "stirling_second"
18250 | "bernoulli_number" | "bernoulli" | "harmonic_number" | "harmonic"
18251 | "drag_force" | "fdrag" | "ideal_gas" | "pv_nrt"
18253 | "bs_delta" | "bsdelta" | "option_delta"
18255 | "bs_gamma" | "bsgamma" | "option_gamma"
18256 | "bs_vega" | "bsvega" | "option_vega"
18257 | "bs_theta" | "bstheta" | "option_theta"
18258 | "bs_rho" | "bsrho" | "option_rho"
18259 | "bond_duration" | "mac_duration"
18260 | "dct" | "idct" | "goertzel" | "chirp" | "chirp_signal"
18262 | "base85_encode" | "b85e" | "ascii85_encode" | "a85e"
18264 | "base85_decode" | "b85d" | "ascii85_decode" | "a85d"
18265 | "pnorm" | "pbinom" | "dbinom" | "ppois"
18267 | "punif" | "pexp" | "pweibull" | "plnorm" | "pcauchy"
18268 | "rbind" | "cbind"
18270 | "row_sums" | "rowSums" | "col_sums" | "colSums"
18271 | "row_means" | "rowMeans" | "col_means" | "colMeans"
18272 | "outer" | "crossprod" | "tcrossprod"
18273 | "nrow" | "ncol" | "prop_table" | "proptable"
18274 | "cummax" | "cummin" | "scale_vec" | "scale"
18276 | "which_fn" | "tabulate"
18277 | "duplicated" | "duped" | "rev_vec"
18278 | "seq_fn" | "rep_fn" | "rep"
18279 | "cut_bins" | "cut" | "find_interval" | "findInterval"
18280 | "ecdf_fn" | "ecdf" | "density_est" | "density"
18281 | "embed_ts" | "embed"
18282 | "shapiro_test" | "shapiro" | "ks_test" | "ks"
18284 | "wilcox_test" | "wilcox" | "mann_whitney"
18285 | "prop_test" | "proptest" | "binom_test" | "binomtest"
18286 | "sapply" | "tapply" | "do_call" | "docall"
18288 | "kmeans" | "prcomp" | "pca"
18290 | "pgamma" | "pbeta" | "pchisq" | "pt_cdf" | "pt" | "pf_cdf" | "pf"
18298 | "dgeom" | "dunif" | "dnbinom" | "dhyper"
18300 | "lowess" | "loess" | "approx_fn" | "approx"
18302 | "lm_fit" | "lm"
18304 | "qt_fn" | "qf_fn"
18306
18307 | "acf_fn" | "acf" | "pacf_fn" | "pacf"
18309 | "diff_lag" | "diff_ts" | "ts_filter" | "filter_ts"
18310 | "predict_lm" | "predict" | "confint_lm" | "confint"
18312 | "cor_matrix" | "cor_mat" | "cov_matrix" | "cov_mat"
18314 | "mahalanobis" | "mahal" | "dist_matrix" | "dist_mat"
18315 | "hclust" | "cutree" | "weighted_var" | "wvar" | "cov2cor"
18316 | "scatter_svg" | "scatter_plot" | "line_svg" | "line_plot"
18318 | "plot_svg" | "hist_svg" | "histogram_svg"
18319 | "boxplot_svg" | "box_plot" | "bar_svg" | "barchart_svg"
18320 | "pie_svg" | "pie_chart" | "heatmap_svg" | "heatmap"
18321 | "donut_svg" | "donut" | "area_svg" | "area_chart"
18322 | "hbar_svg" | "hbar" | "radar_svg" | "radar" | "spider"
18323 | "candlestick_svg" | "candlestick" | "ohlc"
18324 | "violin_svg" | "violin" | "cor_heatmap" | "cor_matrix_svg"
18325 | "stacked_bar_svg" | "stacked_bar"
18326 | "wordcloud_svg" | "wordcloud" | "wcloud"
18327 | "treemap_svg" | "treemap"
18328 | "pvw"
18329 | "cyber_city" | "cyber_grid" | "cyber_rain" | "matrix_rain"
18331 | "cyber_glitch" | "glitch_text" | "cyber_banner" | "neon_banner"
18332 | "cyber_circuit" | "cyber_skull" | "cyber_eye"
18333 | "ai" | "ai_agent" | "prompt" | "stream_prompt" | "stream_prompt_cb"
18335 | "tokens_of"
18336 | "ai_estimate" | "ai_cost" | "ai_history" | "ai_history_clear"
18337 | "ai_cache_clear" | "ai_cache_size"
18338 | "ai_mock_install" | "ai_mock_clear"
18339 | "ai_config_get" | "ai_config_set" | "ai_routing_get" | "ai_routing_set"
18340 | "ai_register_tool" | "ai_unregister_tool" | "ai_clear_tools" | "ai_tools_list"
18341 | "ai_filter" | "ai_map" | "ai_classify" | "ai_match" | "ai_sort" | "ai_dedupe"
18342 | "ai_extract" | "ai_summarize" | "ai_translate" | "ai_template"
18343 | "ai_session_new" | "ai_session_send" | "ai_session_history"
18344 | "ai_session_close" | "ai_session_reset"
18345 | "ai_session_export" | "ai_session_import"
18346 | "ai_memory_save" | "ai_memory_recall" | "ai_memory_forget"
18347 | "ai_memory_count" | "ai_memory_clear"
18348 | "ai_vision" | "ai_pdf" | "ai_grounded" | "ai_citations"
18349 | "ai_transcribe" | "ai_speak" | "ai_image" | "ai_image_edit" | "ai_image_variation"
18350 | "ai_models" | "ai_describe" | "ai_pricing" | "ai_dashboard"
18351 | "ai_moderate" | "ai_chunk" | "ai_warm" | "ai_compare"
18352 | "ai_last_thinking" | "ai_budget" | "ai_batch" | "ai_pmap"
18353 | "ai_file_upload" | "ai_file_list" | "ai_file_get" | "ai_file_delete"
18354 | "ai_file_anthropic_upload" | "ai_file_anthropic_list" | "ai_file_anthropic_delete"
18355 | "vec_cosine" | "vec_search" | "vec_topk"
18356 | "web_search_tool" | "fetch_url_tool" | "read_file_tool" | "run_code_tool"
18358 | "mcp_connect" | "mcp_close" | "mcp_tools" | "mcp_call"
18360 | "mcp_resource" | "mcp_resources" | "mcp_prompt" | "mcp_prompts"
18361 | "mcp_attach_to_ai" | "mcp_detach_from_ai" | "mcp_attached"
18362 | "mcp_server_start" | "mcp_serve_registered_tools"
18363 | "pty_spawn" | "pty_send" | "pty_read" | "pty_expect" | "pty_expect_table"
18365 | "pty_buffer" | "pty_alive" | "pty_eof" | "pty_close" | "pty_interact"
18366 | "pty_strip_ansi" | "pty_after_eof" | "pty_pending_events"
18367 | "stress_fp" | "stress_int" | "stress_cache" | "stress_branch"
18369 | "stress_sort" | "stress_alloc" | "stress_mmap" | "stress_disk"
18370 | "stress_iops" | "stress_net" | "stress_http" | "stress_dns"
18371 | "stress_fork" | "stress_thread" | "stress_aes" | "stress_compress"
18372 | "stress_regex" | "stress_json" | "stress_burst" | "stress_ramp"
18373 | "stress_oscillate" | "stress_all" | "stress_temp" | "stress_thermal_zones"
18374 | "stress_freq" | "stress_throttled" | "stress_load" | "stress_meminfo"
18375 | "stress_cores" | "stress_arm_kill_switch" | "stress_killed"
18376 | "stress_disarm_kill_switch"
18377 | "stress_metrics_record" | "stress_metrics_clear" | "stress_metrics_count"
18378 | "stress_metrics_export" | "stress_metrics_prometheus"
18379 | "stress_metrics_json" | "stress_metrics_csv" | "stress_metrics_watch"
18380 | "audit_log" | "audit_log_path"
18382 | "secrets_encrypt" | "secrets_decrypt" | "secrets_random_key" | "secrets_kdf"
18383 | "web_route" | "web_resources" | "web_root" | "web_routes_table"
18385 | "web_application_config" | "web_boot_application"
18386 | "web_render" | "web_render_partial" | "web_redirect"
18387 | "web_json" | "web_text" | "web_csv" | "web_markdown"
18388 | "web_params" | "web_request" | "web_set_header" | "web_status"
18389 | "web_before_action" | "web_after_action"
18390 | "web_session" | "web_session_set" | "web_session_get" | "web_session_clear"
18391 | "web_signed" | "web_unsigned"
18392 | "web_cookies" | "web_set_cookie"
18393 | "web_flash" | "web_flash_set" | "web_flash_get"
18394 | "web_validate" | "web_permit"
18395 | "web_password_hash" | "web_password_verify"
18396 | "web_token_for" | "web_token_consume" | "web_csrf_meta_tag"
18397 | "web_security_headers" | "web_can"
18398 | "web_h" | "web_truncate" | "web_pluralize" | "web_time_ago_in_words"
18399 | "web_image_tag" | "web_link_to" | "web_button_to"
18400 | "web_form_with" | "web_form_close"
18401 | "web_text_field" | "web_text_area" | "web_check_box"
18402 | "web_stylesheet_link_tag" | "web_javascript_link_tag"
18403 | "web_yield_content" | "web_content_for"
18404 | "web_etag" | "web_cache_get" | "web_cache_set"
18405 | "web_cache_delete" | "web_cache_clear"
18406 | "web_db_connect" | "web_db_execute" | "web_db_query"
18407 | "web_db_begin" | "web_db_commit" | "web_db_rollback"
18408 | "web_create_table" | "web_drop_table"
18409 | "web_add_column" | "web_remove_column"
18410 | "web_migrate" | "web_rollback"
18411 | "web_model_all" | "web_model_find" | "web_model_first" | "web_model_last"
18412 | "web_model_where" | "web_model_create" | "web_model_update"
18413 | "web_model_destroy" | "web_model_count" | "web_model_increment"
18414 | "web_model_paginate" | "web_model_search" | "web_model_soft_destroy"
18415 | "web_model_with"
18416 | "web_jobs_init" | "web_job_enqueue" | "web_job_dequeue"
18417 | "web_job_complete" | "web_job_fail"
18418 | "web_jobs_list" | "web_jobs_stats" | "web_job_purge"
18419 | "web_jsonapi_resource" | "web_jsonapi_collection" | "web_jsonapi_error"
18420 | "web_bearer_token" | "web_jwt_encode" | "web_jwt_decode"
18421 | "web_otp_secret" | "web_otp_generate" | "web_otp_verify"
18422 | "web_uuid" | "web_now" | "web_log" | "web_rate_limit"
18423 | "web_t" | "web_load_locale" | "web_openapi"
18424 | "web_faker_int" | "web_faker_email" | "web_faker_name"
18425 | "web_faker_sentence" | "web_faker_paragraph"
18426 | "check" | "check_no_interop" | "check_ni"
18433 | "test" | "test_no_interop" | "test_ni"
18434 | "andrew_monotone_hull" | "aperture_stop_to_fnumber" | "arpad_predict" | "bilateral_filter_2d"
18443 | "black_hat_transform" | "canny_edges_full" | "case_alternating" | "case_constant"
18444 | "case_dot" | "case_pascal" | "case_path" | "case_sentence"
18445 | "case_swap" | "case_title_proper" | "case_train" | "closing_2d"
18446 | "cohen_sutherland_clip" | "constants_au_meters" | "constants_avogadro_n" | "constants_bohr_radius"
18447 | "constants_boltzmann_k" | "constants_earth_mass" | "constants_earth_radius" | "constants_electron_charge"
18448 | "constants_electron_mass" | "constants_faraday" | "constants_gas_r" | "constants_gravitational_g"
18449 | "constants_lightyear_meters" | "constants_neutron_mass" | "constants_parsec_meters" | "constants_planck_h"
18450 | "constants_planck_hbar" | "constants_planck" | "constants_proton_mass" | "constants_rydberg"
18451 | "constants_solar_mass" | "constants_solar_radius" | "constants_speed_of_light" | "constants_stefan_boltzmann"
18452 | "contour_area" | "contour_centroid" | "contour_find" | "contour_perimeter"
18453 | "convex_hull_3d" | "convex_hull_3d_simple" | "crop_factor" | "delaunay_triangulate_2d" | "depth_of_field_far"
18454 | "depth_of_field_near" | "dh_compute_shared" | "dilation_2d" | "ec_point_add"
18455 | "ec_point_double" | "ed25519_keypair_simple" | "ed25519_sign_simple" | "ed25519_verify_simple"
18456 | "erosion_2d" | "exposure_value" | "field_of_view" | "focal_length_35mm_equiv"
18457 | "glicko_rd_update" | "glicko_volatility" | "graham_scan_hull" | "hu_moments"
18458 | "hyperfocal_distance" | "liang_barsky_clip" | "minkowski_sum_2d" | "moment_image"
18459 | "morphological_gradient" | "opening_2d" | "pagerank_tournament" | "polygon_inflate"
18460 | "polygon_offset" | "polygon_self_intersects" | "polygon_shrink" | "polygon_simple_check"
18461 | "polygon_winding" | "prewitt_x_kernel" | "prewitt_y_kernel" | "ranking_average"
18462 | "ranking_kendall_tau" | "ranking_spearman_rho" | "roberts_cross_kernel" | "rsa_keypair_simple"
18463 | "rsa_modular_exp" | "scharr_x_kernel" | "scharr_y_kernel" | "schnorr_sign_simple"
18464 | "schnorr_verify_simple" | "shutter_speed_reciprocal" | "sobel_magnitude" | "sunny_16_rule"
18465 | "swiss_pairing" | "top_hat_transform" | "tournament_score" | "trueskill_simple"
18466 | "unit_energy" | "unit_pressure" | "unit_temperature" | "unit_volume_metric_to_us"
18467 | "unit_volume_us_to_metric" | "voronoi_cell_2d" | "weiler_atherton_clip" | "zernike_radial"
18468
18469 | "a_star_grid" | "all_pass_filter" | "am_synth" | "bidirectional_bfs"
18470 | "buoyancy_force" | "center_of_mass_2d" | "center_of_mass_3d" | "centered_polygonal"
18471 | "chorus_simple" | "collision_response_2d" | "comb_filter" | "compositions_count"
18472 | "critical_damping" | "cube_number" | "damping_factor" | "decagonal_number"
18473 | "derangement_count" | "dodecahedral" | "elastic_collision_1d" | "exponential_search"
18474 | "fbm_noise_2d" | "fibonacci_matrix" | "fibonacci_nth_fast" | "fir_filter"
18475 | "flanger_simple" | "floyd_cycle_detect" | "fm_synth_2op" | "freeverb_lite"
18476 | "gnomonic_number" | "greedy_best_first" | "hash_2d_int" | "heptagonal_number"
18477 | "hexagonal_number" | "hyperfactorial" | "icosahedral" | "ida_star_search"
18478 | "inelastic_collision_1d" | "interpolation_search" | "lattice_paths" | "lift_force"
18479 | "lucas_nth" | "moment_of_inertia_cylinder" | "moment_of_inertia_disc" | "moment_of_inertia_rod"
18480 | "moment_of_inertia_sphere" | "mulberry32_next" | "multinomial_coefficient" | "narayana_cow"
18481 | "nonagonal_number" | "octagonal_number" | "partitions_count" | "pcg32_next"
18482 | "pell_nth" | "perlin_2d" | "perlin_3d" | "phaser_simple"
18483 | "plate_reverb_simple" | "poisson_brackets" | "primorial" | "projectile_position"
18484 | "projectile_velocity" | "ridge_noise_2d" | "ring_modulate" | "schroeder_reverb"
18485 | "simplex_2d" | "splitmix64_next" | "spring_oscillator_pos" | "square_pyramidal"
18486 | "super_factorial" | "ternary_search" | "tetrahedral" | "tetranacci"
18487 | "torque_arm" | "turbulence_noise_2d" | "value_noise_2d" | "wavetable_synth"
18488 | "worley_2d" | "xorshift32_next"
18489
18490 | "adaptive_threshold" | "bayes_factor" | "bayesian_beta_update" | "bayesian_normal_update"
18491 | "bm25_score" | "boltzmann_choose" | "braycurtis_dist" | "canberra_dist"
18492 | "canny_edges_simple" | "chebyshev_norm" | "cidr_to_range" | "ciede2000_color_distance"
18493 | "ciede76_color_distance" | "ciede94_color_distance" | "conv1d_apply" | "conv2d_apply"
18494 | "correlate2d" | "cosine_sim_sparse" | "credible_interval_beta" | "credible_interval_normal"
18495 | "dice_coeff" | "earth_mover_1d" | "epsilon_greedy_choose" | "fenwick_new"
18496 | "fenwick_query_prefix" | "fenwick_query_range" | "fenwick_update" | "gaussian_kernel"
18497 | "gradient_magnitude_2d" | "harris_response" | "integral_image" | "ip_subnet_split"
18498 | "ipv6_global_unicast" | "jaccard_sim" | "laplacian_kernel" | "lch_to_rgb"
18499 | "mahalanobis_sq" | "manhattan_norm" | "maximum_a_posteriori" | "minkowski_norm"
18500 | "non_max_suppression" | "oklch_to_rgb" | "otsu_threshold" | "overlap_coeff"
18501 | "posterior_predictive_beta" | "posterior_predictive_normal" | "prior_jeffreys_uniform" | "qlearning_step"
18502 | "range_to_cidr" | "rgb_to_lch" | "rgb_to_oklch" | "rl_discount_returns"
18503 | "rl_n_step_return" | "rl_td_error" | "sarsa_step" | "sliding_dot_product"
18504 | "sobel_x_kernel" | "sobel_y_kernel" | "softmax_choose" | "tanimoto_coeff"
18505 | "tfidf_compute" | "thompson_beta_choose" | "trie_count" | "trie_insert"
18506 | "trie_keys" | "trie_lookup" | "trie_new" | "trie_prefix_search"
18507 | "trie_remove" | "tversky_index" | "ucb1_choose" | "union_find_components"
18508 | "union_find_find" | "union_find_new" | "union_find_union" | "window_bartlett"
18509 | "window_blackman_harris" | "window_flat_top" | "window_gaussian" | "window_welch"
18510
18511 | "alphabeta_value" | "chem_arrhenius_k" | "chem_avogadro" | "chem_balance_check"
18512 | "chem_boiling_point_elevation" | "chem_buffer_capacity" | "chem_celsius_to_fahrenheit" | "chem_celsius_to_kelvin"
18513 | "chem_concentration_to_molarity" | "chem_dilution" | "chem_fahrenheit_to_celsius" | "chem_fahrenheit_to_kelvin"
18514 | "chem_formula_parse" | "chem_freezing_point_depression" | "chem_h_from_ph" | "chem_henderson_hasselbalch"
18515 | "chem_ideal_gas_volume" | "chem_isoelectric_estimate" | "chem_kelvin_to_celsius" | "chem_kelvin_to_fahrenheit"
18516 | "chem_kelvin_to_rankine" | "chem_molality" | "chem_molar_mass" | "chem_molarity_to_normality"
18517 | "chem_partial_pressure" | "chem_ph_from_h" | "chem_pka_lookup" | "chem_rankine_to_kelvin"
18518 | "conditional_entropy" | "edmonds_karp_max_flow" | "expectiminimax_value" | "ford_fulkerson_max_flow"
18519 | "job_schedule_ljf" | "job_schedule_spt" | "joint_entropy" | "js_divergence_distributions"
18520 | "kl_divergence_distributions" | "knapsack_fractional" | "knapsack_unbounded" | "lp_simplex_max"
18521 | "lp_simplex_min" | "matching_bipartite_greedy" | "matching_bipartite_hungarian" | "minimax_value"
18522 | "mixed_strategy_2x2" | "ml_attention_score" | "ml_batch_norm" | "ml_dot_product_attention"
18523 | "ml_dropout_mask" | "ml_elu_layer" | "ml_gelu_layer" | "ml_hinge_loss"
18524 | "ml_huber_loss" | "ml_kl_div_loss" | "ml_label_smooth" | "ml_layer_norm"
18525 | "ml_leaky_relu_layer" | "ml_mae_loss" | "ml_mish_layer" | "ml_mse_loss"
18526 | "ml_one_hot_encode" | "ml_position_encoding" | "ml_relu_layer" | "ml_self_attention"
18527 | "ml_sigmoid_layer" | "ml_softmax_layer" | "ml_softplus_layer" | "ml_swish_layer"
18528 | "ml_tanh_layer" | "ngram_perplexity" | "ngram_prob" | "ngram_top_k_next"
18529 | "ngram_train" | "payoff_matrix" | "relative_entropy" | "tsp_2opt"
18530 | "zero_sum_value"
18531
18532 | "aabb_contains_point" | "aabb_intersects" | "aabb_new" | "aabb_union"
18533 | "aabb_volume" | "backward_algorithm" | "blast_kmer_index" | "bmp_header_read"
18534 | "bootstrap_resample" | "codon_optimize" | "codon_to_amino_acid" | "codon_usage_table"
18535 | "dna_at_content" | "dna_complement" | "dna_gc_content" | "dna_kmer_count"
18536 | "dna_kmer_index" | "dna_melting_temp" | "dna_reverse_complement" | "dna_transcribe"
18537 | "dna_translate" | "elf_header_read" | "forward_algorithm" | "gif_header_read"
18538 | "ico_header_read"
18539 | "jpeg_markers" | "levenshtein_edit_path" | "mach_o_header_read" | "markov_stationary"
18540 | "markov_transition_matrix" | "mat4_determinant" | "mat4_identity" | "mat4_inverse"
18541 | "mat4_look_at" | "mat4_multiply" | "mat4_orthographic" | "mat4_perspective"
18542 | "mat4_rotate_axis" | "mat4_rotate_x" | "mat4_rotate_y" | "mat4_rotate_z"
18543 | "mat4_scale" | "mat4_translate" | "mat4_transpose" | "nw_score"
18544 | "permutation_test" | "plane_distance_to_point" | "plane_normalize" | "png_header_read"
18545 | "profile_hmm_score" | "protein_charge_at_ph" | "protein_hydrophobicity" | "protein_molecular_weight"
18546 | "protein_pI" | "quat_conjugate" | "quat_dot" | "quat_from_euler"
18547 | "quat_identity" | "quat_inverse" | "quat_multiply" | "quat_normalize"
18548 | "quat_to_euler" | "quat_to_mat4" | "ray_aabb_intersect" | "ray_plane_intersect_2"
18549 | "ray_plane_intersect" | "rna_gc_content" | "rna_hamming" | "rna_reverse_complement"
18550 | "rna_to_dna" | "sequence_identity_pct" | "sequence_similarity_pct" | "shuffle_resample"
18551 | "sphere_aabb_intersect" | "sphere_sphere_intersect" | "sw_score" | "tar_header_read"
18552 | "triangle_area_3d" | "triangle_normal" | "vec3_add" | "vec3_cross"
18553 | "vec3_distance" | "vec3_dot" | "vec3_length" | "vec3_lerp"
18554 | "vec3_normalize" | "vec3_project" | "vec3_reflect" | "vec3_refract"
18555 | "vec3_scale" | "vec3_sub" | "vec4_add" | "vec4_dot"
18556 | "vec4_length" | "vec4_scale" | "vec4_sub" | "viterbi_decode"
18557 | "wav_header_read" | "zip_central_directory" | "zip_local_file_header"
18558
18559 | "adler32_combine" | "ase_palette_extract" | "base58check_decode" | "base58check_encode"
18560 | "base91_decode" | "basE91_decode" | "base91_encode" | "basE91_encode"
18561 | "bwt_invert" | "bwt_transform" | "caverphone" | "caverphone2"
18562 | "crc10_atm" | "crc12_dect" | "crc24" | "crc32_bzip2"
18563 | "crc32_jamcrc" | "crc32_mpeg2" | "crc32_xfer" | "crc6_itu"
18564 | "crc64_ecma" | "crc64_xz" | "delta_decode" | "delta_encode"
18565 | "destination_lat_lon" | "double_metaphone_primary" | "double_metaphone_secondary" | "fletcher16"
18566 | "fletcher32" | "fletcher64" | "full_moon_julian" | "fuzzy_substring_match"
18567 | "gamma_correct" | "gamma_uncorrect" | "geomag_declination" | "huffman_decode"
18568 | "huffman_encode" | "julian_to_unix" | "lambert_project" | "lat_lon_to_utm"
18569 | "match_rating_compare" | "mercator_project_x" | "mercator_project_y" | "mercator_unproject_lat"
18570 | "mercator_unproject_lon" | "modified_julian_date" | "moon_age_days" | "moon_distance_km"
18571 | "new_moon_julian" | "nysiis" | "phonex" | "rgb_blend_color_burn"
18572 | "rgb_blend_color_dodge" | "rgb_blend_darken" | "rgb_blend_lighten" | "rgb_blend_multiply"
18573 | "rgb_blend_normal" | "rgb_blend_overlay" | "rgb_blend_screen" | "rle_compress"
18574 | "rle_decompress" | "season_of_year" | "sidereal_time_greenwich" | "sidereal_time_local"
18575 | "solar_noon_unix" | "soundex_v1" | "soundex_v2" | "unix_to_julian"
18576 | "utm_to_lat_lon" | "utm_zone" | "varint_decode" | "varint_encode"
18577 | "vincenty_bearing" | "z85_decode" | "z85_encode" | "zigzag_decode"
18578 | "zigzag_encode"
18579
18580 | "anova_one_way" | "binomial_test" | "bit_clz"
18581 | "bit_count_ones" | "bit_count_zeros" | "bit_ctz" | "bit_extract"
18582 | "bit_first_clear" | "bit_first_set" | "bit_insert" | "bit_last_clear"
18583 | "bit_last_set" | "bit_log2_int" | "bit_parity" | "bit_reverse_u16"
18584 | "bit_reverse_u32" | "bit_reverse_u64" | "bit_reverse_u8" | "bit_rotate_left"
18585 | "bit_rotate_right" | "bit_swap_bytes" | "chi_square_goodness_fit" | "chi_square_independence"
18586 | "chord_augmented" | "chord_diminished" | "chord_diminished7" | "chord_dominant7"
18587 | "chord_major" | "chord_major7" | "chord_minor" | "chord_minor7"
18588 | "crc16_xmodem" | "crc16" | "crc32_zlib" | "crc32c"
18589 | "crc8" | "detab" | "entab" | "fisher_exact_2x2"
18590 | "gray_code_decode" | "gray_code_encode" | "hmac_md5_hex" | "hmac_sha1_hex"
18591 | "hmac_sha256_hex" | "hmac_sha384_hex" | "hmac_sha512_hex" | "indent_block"
18592 | "interval_name" | "jenkins_hash" | "justify_center" | "justify_left"
18593 | "justify_right" | "kruskal_wallis" | "ks_test_one_sample" | "ks_test_two_sample"
18594 | "loose_hash" | "mann_whitney_u" | "midi_to_note_name" | "popcount_u32"
18595 | "popcount_u64" | "proportion_test" | "rank_data" | "scale_blues"
18596 | "scale_chromatic" | "scale_dorian" | "scale_harmonic_minor" | "scale_locrian"
18597 | "scale_lydian" | "scale_major" | "scale_melodic_minor" | "scale_minor"
18598 | "scale_mixolydian" | "scale_pentatonic" | "scale_phrygian" | "seconds_per_beat"
18599 | "strip_indent" | "t_test_paired" | "tempo_to_ms_per_beat" | "truncate_middle"
18600 | "unicode_codepoints" | "wilcoxon_signed_rank" | "word_wrap"
18601
18602 | "beta_function" | "beta_incomplete" | "date_add_days" | "date_add_months"
18603 | "date_add_years" | "date_business_days_between" | "date_day" | "date_dayofweek"
18604 | "date_dayofyear" | "date_days_in_month" | "date_diff_days" | "date_diff_hours"
18605 | "date_diff_minutes" | "date_diff_seconds" | "date_easter" | "date_first_of_month"
18606 | "date_hour" | "date_is_leap" | "date_is_weekend" | "date_iso_format"
18607 | "date_iso_week" | "date_last_of_month" | "date_minute" | "date_month"
18608 | "date_quarter" | "date_second" | "date_str_to_unix" | "date_unix_to_str"
18609 | "date_weekofyear" | "date_year" | "ei" | "expint"
18610 | "gamma_regularized_p" | "gamma_regularized_q" | "graph_articulation_points" | "graph_bellman_ford"
18611 | "graph_betweenness" | "graph_bfs" | "graph_bridges" | "graph_closeness"
18612 | "graph_clustering_coefficient" | "graph_color_greedy" | "graph_connected_components" | "graph_cycle_detect"
18613 | "graph_degree" | "graph_dfs" | "graph_dijkstra" | "graph_eccentricity"
18614 | "graph_eigenvector_centrality" | "graph_floyd_warshall" | "graph_from_edges" | "graph_has_path"
18615 | "graph_in_degree" | "graph_is_bipartite" | "graph_is_connected" | "graph_kosaraju"
18616 | "graph_kruskal_mst" | "graph_out_degree" | "graph_pagerank" | "graph_prim_mst"
18617 | "graph_shortest_path" | "graph_strongly_connected_components" | "graph_tarjan" | "graph_to_adj_list"
18618 | "graph_to_adj_matrix" | "graph_topological_sort" | "hypergeom_1f1" | "hypergeom_2f1"
18619 | "li" | "matrix_adjugate" | "matrix_cholesky_decompose" | "matrix_cofactor"
18620 | "matrix_cols" | "matrix_concat_h" | "matrix_concat_v" | "matrix_determinant"
18621 | "matrix_from_cols" | "matrix_get" | "matrix_kronecker" | "matrix_lu_decompose"
18622 | "matrix_minor" | "matrix_new" | "matrix_norm_frobenius" | "matrix_norm_l1"
18623 | "matrix_norm_linf" | "matrix_outer_product" | "matrix_qr_decompose" | "matrix_reshape"
18624 | "matrix_rows" | "matrix_set" | "matrix_submatrix" | "matrix_swap_cols"
18625 | "matrix_swap_rows" | "matrix_to_string" | "matrix_vec_mul" | "si"
18626 | "sun_rise_unix" | "sun_set_unix" | "zeta_riemann" | "zodiac_sign"
18627 | "add_seasonality" | "trapezoidal_integrate" | "simpson_integrate"
18629 | "ode_euler" | "fit_curve_least_squares"
18630 | "adf_test" | "adx" | "atr" | "bollinger_lower" | "bollinger_middle"
18631 | "bollinger_upper" | "break_even_price" | "break_even_qty" | "candlestick_pattern_doji"
18632 | "candlestick_pattern_engulfing" | "candlestick_pattern_evening_star" | "candlestick_pattern_hammer" | "candlestick_pattern_morning_star"
18633 | "candlestick_pattern_three_black_crows" | "candlestick_pattern_three_white_soldiers" | "cci"
18634 | "dema" | "diff_pct" | "diff_series"
18635 | "discount_pct" | "donchian_lower" | "donchian_upper" | "double_exponential_smoothing"
18636 | "duration_modified" | "ema" | "expanding_mean" | "expanding_sum"
18637 | "fibonacci_extension" | "fibonacci_retracement" | "finite_difference_central"
18638 | "finite_difference_forward" | "hma"
18639 | "hurst_exponent" | "interp_lagrange"
18640 | "interp_linear" | "kama"
18641 | "keltner_lower" | "keltner_upper" | "lag_series"
18642 | "loan_interest_total" | "loan_payment" | "loan_remaining" | "log_returns"
18643 | "macd_histogram" | "macd_signal" | "macd" | "markup_pct" | "net_present_value" | "obv" | "parabolic_sar" | "pivot_points" | "profit_margin_pct" | "remove_seasonality" | "resistance_level"
18644 | "roc" | "rolling_kurtosis" | "rolling_max"
18645 | "rolling_mean" | "rolling_median" | "rolling_min" | "rolling_skew"
18646 | "rolling_std" | "rolling_sum" | "rolling_var"
18647 | "rsi" | "shift_series" | "simple_returns" | "sma" | "stoch_rsi" | "support_level"
18648 | "tema" | "trend_line" | "treynor"
18649 | "trix" | "true_range" | "twap" | "ulcer_index"
18650 | "volatility_annualized" | "volatility_realized" | "vwap" | "williams_r"
18651 | "wma"
18652 => Some(name),
18653 _ => None,
18654 }
18655 }
18656
18657 fn is_reserved_hash_name(name: &str) -> bool {
18660 matches!(
18661 name,
18662 "b" | "pc"
18663 | "e"
18664 | "a"
18665 | "d"
18666 | "c"
18667 | "p"
18668 | "k"
18669 | "all"
18670 | "stryke::builtins"
18671 | "stryke::perl_compats"
18672 | "stryke::extensions"
18673 | "stryke::aliases"
18674 | "stryke::descriptions"
18675 | "stryke::categories"
18676 | "stryke::primaries"
18677 | "stryke::keywords"
18678 | "stryke::all"
18679 )
18680 }
18681
18682 const RESERVED_FUNCTION_NAMES: &'static [&'static str] = &[
18687 "y",
18688 "tr",
18689 "s",
18690 "m",
18691 "q",
18692 "qq",
18693 "qw",
18694 "qx",
18695 "qr",
18696 "if",
18697 "unless",
18698 "while",
18699 "until",
18700 "for",
18701 "foreach",
18702 "loop",
18703 "ploop",
18704 "pwhile",
18705 "null",
18706 "given",
18707 "when",
18708 "else",
18709 "elsif",
18710 "do",
18711 "eval",
18712 "return",
18713 "last",
18714 "next",
18715 "redo",
18716 "goto",
18717 "my",
18718 "var",
18719 "val",
18720 "our",
18721 "local",
18722 "state",
18723 "sub",
18724 "fn",
18725 "class",
18726 "struct",
18727 "enum",
18728 "trait",
18729 "use",
18730 "import",
18731 "no",
18732 "require",
18733 "package",
18734 "BEGIN",
18735 "END",
18736 "CHECK",
18737 "INIT",
18738 "UNITCHECK",
18739 "and",
18740 "or",
18741 "not",
18742 "x",
18743 "eq",
18744 "ne",
18745 "lt",
18746 "gt",
18747 "le",
18748 "ge",
18749 "cmp",
18750 ];
18751
18752 fn check_udf_shadows_builtin(&self, name: &str, line: usize) -> StrykeResult<()> {
18753 if name.contains("::") {
18756 return Ok(());
18757 }
18758 if Self::RESERVED_FUNCTION_NAMES.contains(&name) {
18761 return Err(self.syntax_err(
18762 format!("`{name}` is a reserved word and cannot be used as a function name"),
18763 line,
18764 ));
18765 }
18766 if self.current_package != "main" {
18771 return Ok(());
18772 }
18773 if Self::is_known_bareword(name)
18777 || Self::is_try_builtin_name(name)
18778 || crate::list_builtins::is_list_builtin_name(name)
18779 {
18780 return Err(self.syntax_err(
18781 format!(
18782"`{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)"
18783 ),
18784 line,
18785 ));
18786 }
18787 Ok(())
18788 }
18789
18790 fn check_hash_shadows_reserved(&self, name: &str, line: usize) -> StrykeResult<()> {
18793 if Self::is_reserved_hash_name(name) {
18794 return Err(self.syntax_err(
18795 format!(
18796"`%{name}` is a stryke reserved hash and cannot be redefined (this is not Perl 5; pass --compat for Perl 5 mode)"
18797 ),
18798 line,
18799 ));
18800 }
18801 Ok(())
18802 }
18803
18804 fn validate_hash_assignment(&self, value: &Expr, line: usize) -> StrykeResult<()> {
18807 match &value.kind {
18808 ExprKind::Integer(_) | ExprKind::Float(_) => {
18809 return Err(self.syntax_err(
18810 "cannot assign scalar to hash — use %h = (key => value) or %h = %{$hashref}",
18811 line,
18812 ));
18813 }
18814 ExprKind::String(_) | ExprKind::InterpolatedString(_) | ExprKind::Bareword(_) => {
18815 return Err(self.syntax_err(
18816 "cannot assign string to hash — use %h = (key => value) or %h = %{$hashref}",
18817 line,
18818 ));
18819 }
18820 ExprKind::ArrayRef(_) => {
18821 return Err(self.syntax_err(
18822 "cannot assign arrayref to hash — use %h = @{$arrayref} for even-length list",
18823 line,
18824 ));
18825 }
18826 ExprKind::ScalarRef(inner) => {
18827 if matches!(inner.kind, ExprKind::ArrayVar(_)) {
18828 return Err(self.syntax_err(
18829 "cannot assign \\@array to hash — use %h = @array for even-length list",
18830 line,
18831 ));
18832 }
18833 if matches!(inner.kind, ExprKind::HashVar(_)) {
18834 return Err(self.syntax_err(
18835 "cannot assign \\%hash to hash — use %h = %other directly",
18836 line,
18837 ));
18838 }
18839 }
18840 ExprKind::HashRef(_) => {
18841 return Err(self.syntax_err(
18842 "cannot assign hashref to hash — use %h = %{$hashref} to dereference",
18843 line,
18844 ));
18845 }
18846 ExprKind::CodeRef { .. } => {
18847 return Err(self.syntax_err("cannot assign coderef to hash", line));
18848 }
18849 ExprKind::Undef => {
18850 return Err(
18851 self.syntax_err("cannot assign undef to hash — use %h = () to empty", line)
18852 );
18853 }
18854 ExprKind::List(items)
18855 if items.len() % 2 != 0
18856 && !items.iter().any(|e| {
18857 matches!(
18858 e.kind,
18859 ExprKind::ArrayVar(_)
18860 | ExprKind::HashVar(_)
18861 | ExprKind::FuncCall { .. }
18862 | ExprKind::Deref { .. }
18863 | ExprKind::ScalarVar(_)
18864 )
18865 }) =>
18866 {
18867 return Err(self.syntax_err(
18868 format!(
18869 "odd-length list ({} elements) in hash assignment — missing value for last key",
18870 items.len()
18871 ),
18872 line,
18873 ));
18874 }
18875 _ => {}
18876 }
18877 Ok(())
18878 }
18879
18880 fn validate_array_assignment(&self, value: &Expr, line: usize) -> StrykeResult<()> {
18885 if let ExprKind::Undef = &value.kind {
18886 return Err(
18887 self.syntax_err("cannot assign undef to array — use @a = () to empty", line)
18888 );
18889 }
18890 Ok(())
18891 }
18892
18893 fn validate_scalar_assignment(&self, value: &Expr, line: usize) -> StrykeResult<()> {
18896 if let ExprKind::List(items) = &value.kind {
18897 if items.len() > 1 {
18898 return Err(self.syntax_err(
18899 format!(
18900 "cannot assign {}-element list to scalar — Perl 5 silently takes last element; use ($x) = (list) or $x = $list[-1]",
18901 items.len()
18902 ),
18903 line,
18904 ));
18905 }
18906 }
18907 Ok(())
18908 }
18909
18910 fn validate_assignment(&self, target: &Expr, value: &Expr, line: usize) -> StrykeResult<()> {
18912 if crate::compat_mode() {
18913 return Ok(());
18914 }
18915 match &target.kind {
18916 ExprKind::HashVar(_) => self.validate_hash_assignment(value, line),
18917 ExprKind::ArrayVar(_) => self.validate_array_assignment(value, line),
18918 ExprKind::ScalarVar(_) => self.validate_scalar_assignment(value, line),
18919 _ => Ok(()),
18920 }
18921 }
18922
18923 fn parse_block_or_bareword_cmp_block(&mut self) -> StrykeResult<Block> {
18927 if matches!(self.peek(), Token::LBrace) {
18928 return self.parse_block();
18929 }
18930 let line = self.peek_line();
18931 if let Token::Ident(ref name) = self.peek().clone() {
18933 if matches!(
18934 self.peek_at(1),
18935 Token::Comma | Token::Semicolon | Token::RBrace | Token::Eof | Token::PipeForward
18936 ) {
18937 let name = name.clone();
18938 self.advance();
18939 let body = Expr {
18940 kind: ExprKind::FuncCall {
18941 name,
18942 args: vec![
18943 Expr {
18944 kind: ExprKind::ScalarVar("a".to_string()),
18945 line,
18946 },
18947 Expr {
18948 kind: ExprKind::ScalarVar("b".to_string()),
18949 line,
18950 },
18951 ],
18952 },
18953 line,
18954 };
18955 return Ok(vec![Statement::new(StmtKind::Expression(body), line)]);
18956 }
18957 }
18958 let expr = self.parse_assign_expr_stop_at_pipe()?;
18960 Ok(vec![Statement::new(StmtKind::Expression(expr), line)])
18961 }
18962
18963 fn parse_fan_optional_progress(
18965 &mut self,
18966 which: &'static str,
18967 ) -> StrykeResult<Option<Box<Expr>>> {
18968 let line = self.peek_line();
18969 if self.eat(&Token::Comma) {
18970 match self.peek() {
18971 Token::Ident(ref kw)
18972 if kw == "progress" && matches!(self.peek_at(1), Token::FatArrow) =>
18973 {
18974 self.advance();
18975 self.expect(&Token::FatArrow)?;
18976 return Ok(Some(Box::new(self.parse_assign_expr()?)));
18977 }
18978 _ => {
18979 return Err(self.syntax_err(
18980 format!("{which}: expected `progress => EXPR` after comma"),
18981 line,
18982 ));
18983 }
18984 }
18985 }
18986 if let Token::Ident(ref kw) = self.peek().clone() {
18987 if kw == "progress" && matches!(self.peek_at(1), Token::FatArrow) {
18988 self.advance();
18989 self.expect(&Token::FatArrow)?;
18990 return Ok(Some(Box::new(self.parse_assign_expr()?)));
18991 }
18992 }
18993 Ok(None)
18994 }
18995
18996 fn parse_assign_expr_list_optional_progress(&mut self) -> StrykeResult<(Expr, Option<Expr>)> {
19003 if self.in_pipe_rhs()
19009 && matches!(
19010 self.peek(),
19011 Token::Semicolon
19012 | Token::RBrace
19013 | Token::RParen
19014 | Token::Eof
19015 | Token::PipeForward
19016 | Token::Comma
19017 )
19018 {
19019 return Ok((self.pipe_placeholder_list(self.peek_line()), None));
19020 }
19021 let mut parts = vec![self.parse_assign_expr_stop_at_pipe()?];
19022 loop {
19023 if !self.eat(&Token::Comma) && !self.eat(&Token::FatArrow) {
19024 break;
19025 }
19026 if matches!(
19027 self.peek(),
19028 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof | Token::PipeForward
19029 ) {
19030 break;
19031 }
19032 if self.peek_is_postfix_stmt_modifier_keyword() {
19033 break;
19034 }
19035 if let Token::Ident(ref kw) = self.peek().clone() {
19036 if kw == "progress" && matches!(self.peek_at(1), Token::FatArrow) {
19037 self.advance();
19038 self.expect(&Token::FatArrow)?;
19039 let prog = self.parse_assign_expr_stop_at_pipe()?;
19040 return Ok((merge_expr_list(parts), Some(prog)));
19041 }
19042 }
19043 parts.push(self.parse_assign_expr_stop_at_pipe()?);
19044 }
19045 Ok((merge_expr_list(parts), None))
19046 }
19047
19048 fn parse_one_arg(&mut self) -> StrykeResult<Expr> {
19049 if matches!(self.peek(), Token::LParen) {
19050 self.advance();
19051 let expr = self.parse_expression()?;
19052 self.expect(&Token::RParen)?;
19053 Ok(expr)
19054 } else {
19055 self.parse_assign_expr_stop_at_pipe()
19056 }
19057 }
19058
19059 fn parse_named_unary_arg(&mut self) -> StrykeResult<Expr> {
19068 if matches!(self.peek(), Token::LParen) {
19069 self.advance();
19070 let expr = self.parse_expression()?;
19071 self.expect(&Token::RParen)?;
19072 Ok(expr)
19073 } else {
19074 self.parse_shift()
19075 }
19076 }
19077
19078 fn parse_one_arg_or_default(&mut self) -> StrykeResult<Expr> {
19079 let prev = self.prev_line();
19090 if self.peek_line() > prev {
19091 return Ok(Expr {
19092 kind: ExprKind::ScalarVar("_".into()),
19093 line: prev,
19094 });
19095 }
19096 if matches!(
19103 self.peek(),
19104 Token::Semicolon
19106 | Token::RBrace
19107 | Token::RParen
19108 | Token::RBracket
19109 | Token::Eof
19110 | Token::Comma
19111 | Token::FatArrow
19112 | Token::PipeForward
19113 | Token::Question
19115 | Token::Colon
19116 | Token::NumEq | Token::NumNe | Token::NumLt | Token::NumGt
19118 | Token::NumLe | Token::NumGe | Token::Spaceship
19119 | Token::StrEq | Token::StrNe | Token::StrLt | Token::StrGt
19120 | Token::StrLe | Token::StrGe | Token::StrCmp
19121 | Token::LogAnd | Token::LogOr | Token::LogNot
19123 | Token::LogAndWord | Token::LogOrWord | Token::LogNotWord
19124 | Token::DefinedOr
19125 | Token::Range | Token::RangeExclusive
19127 | Token::Assign | Token::PlusAssign | Token::MinusAssign
19129 | Token::MulAssign | Token::DivAssign | Token::ModAssign
19130 | Token::PowAssign | Token::DotAssign | Token::AndAssign
19131 | Token::OrAssign | Token::XorAssign | Token::DefinedOrAssign
19132 | Token::ShiftLeftAssign | Token::ShiftRightAssign
19133 | Token::BitAndAssign | Token::BitOrAssign
19134 ) {
19135 return Ok(Expr {
19136 kind: ExprKind::ScalarVar("_".into()),
19137 line: self.peek_line(),
19138 });
19139 }
19140 if matches!(self.peek(), Token::LParen) && matches!(self.peek_at(1), Token::RParen) {
19144 let line = self.peek_line();
19145 self.advance(); self.advance(); return Ok(Expr {
19148 kind: ExprKind::ScalarVar("_".into()),
19149 line,
19150 });
19151 }
19152 self.parse_named_unary_arg()
19157 }
19158
19159 fn parse_one_arg_or_argv(&mut self) -> StrykeResult<Expr> {
19161 let line = self.prev_line(); if matches!(self.peek(), Token::LParen) {
19163 self.advance();
19164 if matches!(self.peek(), Token::RParen) {
19165 self.advance();
19166 return Ok(Expr {
19167 kind: ExprKind::ArrayVar("_".into()),
19168 line: self.peek_line(),
19169 });
19170 }
19171 let expr = self.parse_expression()?;
19172 self.expect(&Token::RParen)?;
19173 return Ok(expr);
19174 }
19175 if matches!(
19177 self.peek(),
19178 Token::Semicolon
19179 | Token::RBrace
19180 | Token::RParen
19181 | Token::Eof
19182 | Token::Comma
19183 | Token::PipeForward
19184 ) || self.peek_line() > line
19185 {
19186 Ok(Expr {
19187 kind: ExprKind::ArrayVar("_".into()),
19188 line,
19189 })
19190 } else {
19191 self.parse_assign_expr()
19192 }
19193 }
19194
19195 fn parse_builtin_args(&mut self) -> StrykeResult<Vec<Expr>> {
19196 if matches!(self.peek(), Token::LParen) {
19197 self.advance();
19198 let args = self.parse_arg_list()?;
19199 self.expect(&Token::RParen)?;
19200 Ok(args)
19201 } else if self.suppress_parenless_call > 0 && matches!(self.peek(), Token::Ident(_)) {
19202 Ok(vec![])
19205 } else {
19206 self.parse_list_until_terminator()
19207 }
19208 }
19209
19210 #[inline]
19214 fn fat_arrow_autoquote(&self, name: &str, line: usize) -> Option<Expr> {
19215 if matches!(self.peek(), Token::FatArrow) {
19216 Some(Expr {
19217 kind: ExprKind::String(name.to_string()),
19218 line,
19219 })
19220 } else {
19221 None
19222 }
19223 }
19224
19225 fn parse_hash_subscript_key(&mut self) -> StrykeResult<Expr> {
19236 let line = self.peek_line();
19237 if let Token::Ident(ref k) = self.peek().clone() {
19238 if matches!(self.peek_at(1), Token::RBrace) && !Self::is_underscore_topic_slot(k) {
19239 let s = k.clone();
19240 self.advance();
19241 return Ok(Expr {
19242 kind: ExprKind::String(s),
19243 line,
19244 });
19245 }
19246 }
19247 if matches!(self.peek_at(1), Token::RBrace) {
19248 if let Some(s) = Self::operator_keyword_to_ident_str(self.peek()) {
19249 self.advance();
19250 return Ok(Expr {
19251 kind: ExprKind::String(s.to_string()),
19252 line,
19253 });
19254 }
19255 }
19256 self.parse_expression()
19257 }
19258
19259 #[inline]
19261 fn peek_is_glob_par_progress_kw(&self) -> bool {
19262 matches!(self.peek(), Token::Ident(ref kw) if kw == "progress")
19263 && matches!(self.peek_at(1), Token::FatArrow)
19264 }
19265
19266 fn parse_pattern_list_until_rparen_or_progress(&mut self) -> StrykeResult<Vec<Expr>> {
19268 let mut args = Vec::new();
19269 loop {
19270 if matches!(self.peek(), Token::RParen | Token::Eof) {
19271 break;
19272 }
19273 if self.peek_is_glob_par_progress_kw() {
19274 break;
19275 }
19276 args.push(self.parse_assign_expr()?);
19277 match self.peek() {
19278 Token::RParen => break,
19279 Token::Comma => {
19280 self.advance();
19281 if matches!(self.peek(), Token::RParen) {
19282 break;
19283 }
19284 if self.peek_is_glob_par_progress_kw() {
19285 break;
19286 }
19287 }
19288 _ => {
19289 return Err(self.syntax_err(
19290 "expected `,`, `)`, or `progress =>` after argument in `glob_par` / `par_sed`",
19291 self.peek_line(),
19292 ));
19293 }
19294 }
19295 }
19296 Ok(args)
19297 }
19298
19299 fn parse_pattern_list_glob_par_bare(&mut self) -> StrykeResult<Vec<Expr>> {
19301 let mut args = Vec::new();
19302 loop {
19303 if matches!(
19304 self.peek(),
19305 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof
19306 ) {
19307 break;
19308 }
19309 if self.peek_is_postfix_stmt_modifier_keyword() {
19310 break;
19311 }
19312 if self.peek_is_glob_par_progress_kw() {
19313 break;
19314 }
19315 args.push(self.parse_assign_expr()?);
19316 if !self.eat(&Token::Comma) {
19317 break;
19318 }
19319 if self.peek_is_glob_par_progress_kw() {
19320 break;
19321 }
19322 }
19323 Ok(args)
19324 }
19325
19326 fn parse_glob_par_or_par_sed_args(&mut self) -> StrykeResult<(Vec<Expr>, Option<Box<Expr>>)> {
19328 if matches!(self.peek(), Token::LParen) {
19329 self.advance();
19330 let args = self.parse_pattern_list_until_rparen_or_progress()?;
19331 let progress = if self.peek_is_glob_par_progress_kw() {
19332 self.advance();
19333 self.expect(&Token::FatArrow)?;
19334 Some(Box::new(self.parse_assign_expr()?))
19335 } else {
19336 None
19337 };
19338 self.expect(&Token::RParen)?;
19339 Ok((args, progress))
19340 } else {
19341 let args = self.parse_pattern_list_glob_par_bare()?;
19342 let progress = if self.peek_is_glob_par_progress_kw() {
19344 self.advance();
19345 self.expect(&Token::FatArrow)?;
19346 Some(Box::new(self.parse_assign_expr()?))
19347 } else {
19348 None
19349 };
19350 Ok((args, progress))
19351 }
19352 }
19353
19354 pub(crate) fn parse_arg_list(&mut self) -> StrykeResult<Vec<Expr>> {
19355 let mut args = Vec::new();
19356 let saved_no_pf = self.no_pipe_forward_depth;
19360 self.no_pipe_forward_depth = 0;
19361 while !matches!(
19362 self.peek(),
19363 Token::RParen | Token::RBracket | Token::RBrace | Token::Eof
19364 ) {
19365 let arg = match self.parse_assign_expr() {
19366 Ok(e) => e,
19367 Err(err) => {
19368 self.no_pipe_forward_depth = saved_no_pf;
19369 return Err(err);
19370 }
19371 };
19372 args.push(arg);
19373 if !self.eat(&Token::Comma) && !self.eat(&Token::FatArrow) {
19374 break;
19375 }
19376 }
19377 self.no_pipe_forward_depth = saved_no_pf;
19378 Ok(args)
19379 }
19380
19381 pub(crate) fn parse_slice_arg_list(&mut self, is_hash: bool) -> StrykeResult<Vec<Expr>> {
19390 let mut args = Vec::new();
19391 let saved_no_pf = self.no_pipe_forward_depth;
19392 self.no_pipe_forward_depth = 0;
19393 while !matches!(
19394 self.peek(),
19395 Token::RParen | Token::RBracket | Token::RBrace | Token::Eof
19396 ) {
19397 let arg = match self.parse_slice_arg(is_hash) {
19398 Ok(e) => e,
19399 Err(err) => {
19400 self.no_pipe_forward_depth = saved_no_pf;
19401 return Err(err);
19402 }
19403 };
19404 args.push(arg);
19405 if !self.eat(&Token::Comma) && !self.eat(&Token::FatArrow) {
19406 break;
19407 }
19408 }
19409 self.no_pipe_forward_depth = saved_no_pf;
19410 Ok(args)
19411 }
19412
19413 fn parse_slice_arg(&mut self, is_hash: bool) -> StrykeResult<Expr> {
19415 let line = self.peek_line();
19416
19417 if matches!(self.peek(), Token::Colon) {
19419 self.advance();
19420 return self.finish_slice_range(None, false, is_hash, line);
19421 }
19422 if matches!(self.peek(), Token::PackageSep) {
19423 self.advance();
19424 return self.finish_slice_range(None, true, is_hash, line);
19425 }
19426
19427 self.suppress_colon_range = self.suppress_colon_range.saturating_add(1);
19430 let result = self.parse_slice_endpoint(is_hash);
19431 self.suppress_colon_range = self.suppress_colon_range.saturating_sub(1);
19432 let from_expr = result?;
19433
19434 if matches!(self.peek(), Token::Colon) {
19436 self.advance();
19437 return self.finish_slice_range(Some(Box::new(from_expr)), false, is_hash, line);
19438 }
19439 if matches!(self.peek(), Token::PackageSep) {
19440 self.advance();
19441 return self.finish_slice_range(Some(Box::new(from_expr)), true, is_hash, line);
19442 }
19443
19444 Ok(from_expr)
19445 }
19446
19447 fn finish_slice_range(
19454 &mut self,
19455 from: Option<Box<Expr>>,
19456 double: bool,
19457 is_hash: bool,
19458 line: usize,
19459 ) -> StrykeResult<Expr> {
19460 let (to, step) = if double {
19461 let step_v = self.parse_slice_optional_endpoint(is_hash)?;
19463 (None, step_v)
19464 } else {
19465 let to_v = self.parse_slice_optional_endpoint(is_hash)?;
19467 let step_v = if matches!(self.peek(), Token::Colon) {
19468 self.advance();
19469 self.parse_slice_optional_endpoint(is_hash)?
19470 } else if matches!(self.peek(), Token::PackageSep) {
19471 return Err(
19472 self.syntax_err("Unexpected `::` after slice TO endpoint".to_string(), line)
19473 );
19474 } else {
19475 None
19476 };
19477 (to_v, step_v)
19478 };
19479
19480 if let (Some(f), Some(t)) = (from.as_ref(), to.as_ref()) {
19483 return Ok(Expr {
19484 kind: ExprKind::Range {
19485 from: f.clone(),
19486 to: t.clone(),
19487 exclusive: false,
19488 step,
19489 },
19490 line,
19491 });
19492 }
19493
19494 Ok(Expr {
19495 kind: ExprKind::SliceRange { from, to, step },
19496 line,
19497 })
19498 }
19499
19500 fn parse_slice_optional_endpoint(&mut self, is_hash: bool) -> StrykeResult<Option<Box<Expr>>> {
19503 if matches!(
19504 self.peek(),
19505 Token::Colon
19506 | Token::PackageSep
19507 | Token::Comma
19508 | Token::RBracket
19509 | Token::RBrace
19510 | Token::Eof
19511 ) {
19512 return Ok(None);
19513 }
19514 self.suppress_colon_range = self.suppress_colon_range.saturating_add(1);
19515 let r = self.parse_slice_endpoint(is_hash);
19516 self.suppress_colon_range = self.suppress_colon_range.saturating_sub(1);
19517 Ok(Some(Box::new(r?)))
19518 }
19519
19520 fn parse_slice_endpoint(&mut self, is_hash: bool) -> StrykeResult<Expr> {
19524 if is_hash {
19525 if let Token::Ident(name) = self.peek().clone() {
19526 if matches!(
19527 self.peek_at(1),
19528 Token::Colon
19529 | Token::PackageSep
19530 | Token::Comma
19531 | Token::RBracket
19532 | Token::RBrace
19533 ) {
19534 let line = self.peek_line();
19535 self.advance();
19536 return Ok(Expr {
19537 kind: ExprKind::String(name),
19538 line,
19539 });
19540 }
19541 }
19542 }
19543 self.parse_assign_expr()
19544 }
19545
19546 fn parse_method_arg_list_no_paren(&mut self) -> StrykeResult<Vec<Expr>> {
19550 let mut args = Vec::new();
19551 let call_line = self.prev_line();
19552 loop {
19553 if args.is_empty() && matches!(self.peek(), Token::LBrace) {
19556 break;
19557 }
19558 if matches!(
19559 self.peek(),
19560 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof | Token::PipeForward
19561 ) {
19562 break;
19563 }
19564 if let Token::Ident(ref kw) = self.peek().clone() {
19565 if matches!(
19566 kw.as_str(),
19567 "if" | "unless" | "while" | "until" | "for" | "foreach"
19568 ) {
19569 break;
19570 }
19571 }
19572 if args.is_empty()
19575 && (self.peek_method_arg_infix_terminator() || matches!(self.peek(), Token::Comma))
19576 {
19577 break;
19578 }
19579 if args.is_empty() && self.peek_line() > call_line {
19582 break;
19583 }
19584 args.push(self.parse_assign_expr()?);
19585 if !self.eat(&Token::Comma) {
19586 break;
19587 }
19588 }
19589 Ok(args)
19590 }
19591
19592 fn peek_method_arg_infix_terminator(&self) -> bool {
19595 matches!(
19596 self.peek(),
19597 Token::Plus
19598 | Token::Minus
19599 | Token::Star
19600 | Token::Slash
19601 | Token::Percent
19602 | Token::Power
19603 | Token::Dot
19604 | Token::X
19605 | Token::NumEq
19606 | Token::NumNe
19607 | Token::NumLt
19608 | Token::NumGt
19609 | Token::NumLe
19610 | Token::NumGe
19611 | Token::Spaceship
19612 | Token::StrEq
19613 | Token::StrNe
19614 | Token::StrLt
19615 | Token::StrGt
19616 | Token::StrLe
19617 | Token::StrGe
19618 | Token::StrCmp
19619 | Token::LogAnd
19620 | Token::LogOr
19621 | Token::LogAndWord
19622 | Token::LogOrWord
19623 | Token::DefinedOr
19624 | Token::BitAnd
19625 | Token::BitOr
19626 | Token::BitXor
19627 | Token::ShiftLeft
19628 | Token::ShiftRight
19629 | Token::Range
19630 | Token::RangeExclusive
19631 | Token::BindMatch
19632 | Token::BindNotMatch
19633 | Token::Arrow
19634 | Token::Question
19636 | Token::Colon
19637 | Token::Assign
19639 | Token::PlusAssign
19640 | Token::MinusAssign
19641 | Token::MulAssign
19642 | Token::DivAssign
19643 | Token::ModAssign
19644 | Token::PowAssign
19645 | Token::DotAssign
19646 | Token::AndAssign
19647 | Token::OrAssign
19648 | Token::XorAssign
19649 | Token::DefinedOrAssign
19650 | Token::ShiftLeftAssign
19651 | Token::ShiftRightAssign
19652 | Token::BitAndAssign
19653 | Token::BitOrAssign
19654 )
19655 }
19656
19657 fn parse_list_until_terminator(&mut self) -> StrykeResult<Vec<Expr>> {
19658 self.parse_list_until_terminator_inner(false)
19659 }
19660
19661 fn parse_list_until_terminator_allow_pipe(&mut self) -> StrykeResult<Vec<Expr>> {
19666 self.parse_list_until_terminator_inner(true)
19667 }
19668
19669 fn parse_list_until_terminator_inner(&mut self, allow_pipe: bool) -> StrykeResult<Vec<Expr>> {
19670 let mut args = Vec::new();
19671 let call_line = self.prev_line();
19676 loop {
19677 let is_terminator = if allow_pipe {
19681 matches!(
19682 self.peek(),
19683 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof
19684 )
19685 } else {
19686 matches!(
19687 self.peek(),
19688 Token::Semicolon
19689 | Token::RBrace
19690 | Token::RParen
19691 | Token::Eof
19692 | Token::PipeForward
19693 )
19694 };
19695 if is_terminator {
19696 break;
19697 }
19698 if let Token::Ident(ref kw) = self.peek().clone() {
19700 if matches!(
19701 kw.as_str(),
19702 "if" | "unless" | "while" | "until" | "for" | "foreach"
19703 ) {
19704 break;
19705 }
19706 }
19707 if args.is_empty() && self.peek_line() > call_line {
19714 break;
19715 }
19716 if allow_pipe {
19720 args.push(self.parse_assign_expr()?);
19721 } else {
19722 args.push(self.parse_assign_expr_stop_at_pipe()?);
19723 }
19724 if !self.eat(&Token::Comma) {
19725 break;
19726 }
19727 }
19728 Ok(args)
19729 }
19730
19731 fn parse_forced_hashref_body(&mut self, line: usize) -> StrykeResult<Expr> {
19738 let saved = self.pos;
19739 if let Ok(pairs) = self.try_parse_hash_ref() {
19740 return Ok(Expr {
19741 kind: ExprKind::HashRef(pairs),
19742 line,
19743 });
19744 }
19745 self.pos = saved;
19747 if matches!(self.peek(), Token::RBrace) {
19748 self.advance();
19749 return Ok(Expr {
19750 kind: ExprKind::HashRef(vec![]),
19751 line,
19752 });
19753 }
19754 let inner = self.parse_expression()?;
19758 self.expect(&Token::RBrace)?;
19759 let sentinel_key = Expr {
19760 kind: ExprKind::String("__HASH_SPREAD__".into()),
19761 line,
19762 };
19763 Ok(Expr {
19764 kind: ExprKind::HashRef(vec![(sentinel_key, inner)]),
19765 line,
19766 })
19767 }
19768
19769 fn try_parse_hash_ref(&mut self) -> StrykeResult<Vec<(Expr, Expr)>> {
19770 let mut pairs = Vec::new();
19771 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
19772 let line = self.peek_line();
19777 let key = if let Token::Ident(ref name) = self.peek().clone() {
19778 if matches!(self.peek_at(1), Token::FatArrow)
19779 && !Self::is_underscore_topic_slot(name)
19780 {
19781 self.advance();
19782 Expr {
19783 kind: ExprKind::String(name.clone()),
19784 line,
19785 }
19786 } else {
19787 self.parse_assign_expr()?
19788 }
19789 } else {
19790 self.parse_assign_expr()?
19791 };
19792 if matches!(self.peek(), Token::RBrace | Token::Comma)
19796 && matches!(
19797 key.kind,
19798 ExprKind::HashVar(_)
19799 | ExprKind::Deref {
19800 kind: Sigil::Hash,
19801 ..
19802 }
19803 )
19804 {
19805 let sentinel_key = Expr {
19809 kind: ExprKind::String("__HASH_SPREAD__".into()),
19810 line,
19811 };
19812 pairs.push((sentinel_key, key));
19813 self.eat(&Token::Comma);
19814 continue;
19815 }
19816 if self.eat(&Token::FatArrow) || self.eat(&Token::Comma) {
19818 let val = self.parse_assign_expr()?;
19819 pairs.push((key, val));
19820 self.eat(&Token::Comma);
19821 } else {
19822 return Err(self.syntax_err("Expected => or , in hash ref", key.line));
19823 }
19824 }
19825 self.expect(&Token::RBrace)?;
19826 Ok(pairs)
19827 }
19828
19829 fn parse_hashref_pairs_until(&mut self, term: &Token) -> StrykeResult<Vec<(Expr, Expr)>> {
19834 let mut pairs = Vec::new();
19835 while !matches!(&self.peek(), t if std::mem::discriminant(*t) == std::mem::discriminant(term))
19836 && !matches!(self.peek(), Token::Eof)
19837 {
19838 let line = self.peek_line();
19839 let key = if let Token::Ident(ref name) = self.peek().clone() {
19840 if matches!(self.peek_at(1), Token::FatArrow)
19841 && !Self::is_underscore_topic_slot(name)
19842 {
19843 self.advance();
19844 Expr {
19845 kind: ExprKind::String(name.clone()),
19846 line,
19847 }
19848 } else {
19849 self.parse_assign_expr()?
19850 }
19851 } else {
19852 self.parse_assign_expr()?
19853 };
19854 if self.eat(&Token::FatArrow) || self.eat(&Token::Comma) {
19855 let val = self.parse_assign_expr()?;
19856 pairs.push((key, val));
19857 self.eat(&Token::Comma);
19858 } else {
19859 return Err(self.syntax_err("Expected => or , in hash ref", key.line));
19860 }
19861 }
19862 Ok(pairs)
19863 }
19864
19865 fn interp_chain_subscripts(
19871 &self,
19872 chars: &[char],
19873 i: &mut usize,
19874 mut base: Expr,
19875 line: usize,
19876 ) -> Expr {
19877 loop {
19878 let (after, requires_subscript) =
19880 if *i + 1 < chars.len() && chars[*i] == '-' && chars[*i + 1] == '>' {
19881 (*i + 2, true)
19882 } else {
19883 (*i, false)
19884 };
19885 if after >= chars.len() {
19886 break;
19887 }
19888 match chars[after] {
19889 '[' => {
19890 *i = after + 1;
19891 let mut idx_str = String::new();
19892 while *i < chars.len() && chars[*i] != ']' {
19893 idx_str.push(chars[*i]);
19894 *i += 1;
19895 }
19896 if *i < chars.len() {
19897 *i += 1;
19898 }
19899 let idx_expr = if let Some(rest) = idx_str.strip_prefix('$') {
19900 Expr {
19901 kind: ExprKind::ScalarVar(rest.to_string()),
19902 line,
19903 }
19904 } else if let Ok(n) = idx_str.parse::<i64>() {
19905 Expr {
19906 kind: ExprKind::Integer(n),
19907 line,
19908 }
19909 } else {
19910 Expr {
19911 kind: ExprKind::String(idx_str),
19912 line,
19913 }
19914 };
19915 base = Expr {
19916 kind: ExprKind::ArrowDeref {
19917 expr: Box::new(base),
19918 index: Box::new(idx_expr),
19919 kind: DerefKind::Array,
19920 },
19921 line,
19922 };
19923 }
19924 '{' => {
19925 *i = after + 1;
19926 let mut key = String::new();
19927 let mut depth = 1usize;
19928 while *i < chars.len() && depth > 0 {
19929 if chars[*i] == '{' {
19930 depth += 1;
19931 } else if chars[*i] == '}' {
19932 depth -= 1;
19933 if depth == 0 {
19934 break;
19935 }
19936 }
19937 key.push(chars[*i]);
19938 *i += 1;
19939 }
19940 if *i < chars.len() {
19941 *i += 1;
19942 }
19943 let key_expr = if let Some(rest) = key.strip_prefix('$') {
19944 Expr {
19945 kind: ExprKind::ScalarVar(rest.to_string()),
19946 line,
19947 }
19948 } else {
19949 Expr {
19950 kind: ExprKind::String(key),
19951 line,
19952 }
19953 };
19954 base = Expr {
19955 kind: ExprKind::ArrowDeref {
19956 expr: Box::new(base),
19957 index: Box::new(key_expr),
19958 kind: DerefKind::Hash,
19959 },
19960 line,
19961 };
19962 }
19963 _ => {
19964 if requires_subscript {
19965 }
19967 break;
19968 }
19969 }
19970 }
19971 base
19972 }
19973
19974 fn no_interop_check_scalar_var_name(&self, name: &str, line: usize) -> StrykeResult<()> {
19978 if crate::no_interop_mode() && (name == "a" || name == "b") {
19979 return Err(self.syntax_err(
19980 format!(
19981 "stryke uses `_` / `_1` (bareword in code) or `$_` / `$_1` \
19982 (sigil inside string interpolation / when whitespace would \
19983 change parsing) instead of `${}` (--no-interop is active)",
19984 name
19985 ),
19986 line,
19987 ));
19988 }
19989 Ok(())
19990 }
19991
19992 fn scan_interp_braces(chars: &[char], start: usize) -> Option<(String, usize)> {
20005 Self::scan_interp_braces_pass(chars, start, true)
20006 .or_else(|| Self::scan_interp_braces_pass(chars, start, false))
20007 }
20008
20009 fn scan_interp_braces_pass(
20010 chars: &[char],
20011 start: usize,
20012 quote_aware: bool,
20013 ) -> Option<(String, usize)> {
20014 let mut depth = 1usize;
20015 let mut quote: Option<char> = None;
20016 let mut esc = false;
20017 let mut i = start;
20018 while i < chars.len() {
20019 let c = chars[i];
20020 if let Some(q) = quote {
20021 if esc {
20022 esc = false;
20023 } else if c == '\\' {
20024 esc = true;
20025 } else if c == q {
20026 quote = None;
20027 }
20028 } else {
20029 match c {
20030 '\'' | '"' if quote_aware => quote = Some(c),
20031 '{' => depth += 1,
20032 '}' => {
20033 depth -= 1;
20034 if depth == 0 {
20035 return Some((chars[start..i].iter().collect(), i));
20036 }
20037 }
20038 _ => {}
20039 }
20040 }
20041 i += 1;
20042 }
20043 None
20044 }
20045
20046 fn parse_interpolated_string(&self, s: &str, line: usize) -> StrykeResult<Expr> {
20047 let mut parts = Vec::new();
20049 let mut literal = String::new();
20050 let chars: Vec<char> = s.chars().collect();
20051 let mut i = 0;
20052
20053 'istr: while i < chars.len() {
20054 if chars[i] == LITERAL_DOLLAR_IN_DQUOTE {
20055 literal.push('$');
20056 i += 1;
20057 continue;
20058 }
20059 if chars[i] == LITERAL_AT_IN_DQUOTE {
20060 literal.push('@');
20061 i += 1;
20062 continue;
20063 }
20064 if chars[i] == '\\' && i + 1 < chars.len() && chars[i + 1] == '$' {
20066 literal.push('\\');
20067 i += 1;
20068 }
20070 if chars[i] == '$' && i + 1 < chars.len() {
20071 if !literal.is_empty() {
20072 parts.push(StringPart::Literal(std::mem::take(&mut literal)));
20073 }
20074 i += 1; while i < chars.len() && chars[i].is_whitespace() {
20077 i += 1;
20078 }
20079 if i >= chars.len() {
20080 return Err(self.syntax_err("Final $ should be \\$ or $name", line));
20081 }
20082 if chars[i] == '#' {
20084 i += 1;
20085 let mut sname = String::from("#");
20086 while i < chars.len()
20087 && (chars[i].is_alphanumeric() || chars[i] == '_' || chars[i] == ':')
20088 {
20089 sname.push(chars[i]);
20090 i += 1;
20091 }
20092 while i + 1 < chars.len() && chars[i] == ':' && chars[i + 1] == ':' {
20093 sname.push_str("::");
20094 i += 2;
20095 while i < chars.len() && (chars[i].is_alphanumeric() || chars[i] == '_') {
20096 sname.push(chars[i]);
20097 i += 1;
20098 }
20099 }
20100 self.no_interop_check_scalar_var_name(&sname, line)?;
20101 parts.push(StringPart::ScalarVar(sname));
20102 continue;
20103 }
20104 if chars[i] == '$' {
20108 let next_c = chars.get(i + 1).copied();
20109 let is_pid = match next_c {
20110 None => true,
20111 Some(c)
20112 if !c.is_ascii_digit() && !matches!(c, 'A'..='Z' | 'a'..='z' | '_') =>
20113 {
20114 true
20115 }
20116 _ => false,
20117 };
20118 if is_pid {
20119 parts.push(StringPart::ScalarVar("$$".to_string()));
20120 i += 1; continue;
20122 }
20123 i += 1; }
20125 if chars[i] == '{' {
20126 i += 1;
20132 let inner = match Self::scan_interp_braces(&chars, i) {
20133 Some((inner, close)) => {
20134 i = close + 1; inner
20136 }
20137 None => {
20138 let rest: String = chars[i..].iter().collect();
20140 i = chars.len();
20141 rest
20142 }
20143 };
20144
20145 let trimmed = inner.trim();
20149 if !crate::compat_mode() && zsh_param_form(trimmed) {
20150 parts.push(StringPart::Expr(build_zpexpand_expr(trimmed, line)));
20158 } else {
20159 let mut base: Expr = if trimmed.starts_with('$')
20160 || trimmed.starts_with('\\')
20161 || trimmed.starts_with('@') || trimmed.starts_with('%') || trimmed.contains(['(', '+', '-', '*', '/', '.', '?', '&', '|'])
20164 {
20165 match parse_expression_from_str(trimmed, "<interp>") {
20169 Ok(e) => Expr {
20170 kind: ExprKind::Deref {
20171 expr: Box::new(e),
20172 kind: Sigil::Scalar,
20173 },
20174 line,
20175 },
20176 Err(_) => Expr {
20177 kind: ExprKind::ScalarVar(inner.clone()),
20178 line,
20179 },
20180 }
20181 } else {
20182 self.no_interop_check_scalar_var_name(&inner, line)?;
20184 Expr {
20185 kind: ExprKind::ScalarVar(inner),
20186 line,
20187 }
20188 };
20189
20190 base = self.interp_chain_subscripts(&chars, &mut i, base, line);
20194 parts.push(StringPart::Expr(base));
20195 }
20196 } else if chars[i] == '^' {
20197 let mut name = String::from("^");
20199 i += 1;
20200 while i < chars.len() && (chars[i].is_alphanumeric() || chars[i] == '_') {
20201 name.push(chars[i]);
20202 i += 1;
20203 }
20204 if i < chars.len() && chars[i] == '{' {
20205 i += 1; let mut key = String::new();
20207 let mut depth = 1;
20208 while i < chars.len() && depth > 0 {
20209 if chars[i] == '{' {
20210 depth += 1;
20211 } else if chars[i] == '}' {
20212 depth -= 1;
20213 if depth == 0 {
20214 break;
20215 }
20216 }
20217 key.push(chars[i]);
20218 i += 1;
20219 }
20220 if i < chars.len() {
20221 i += 1;
20222 }
20223 let key_expr = if let Some(rest) = key.strip_prefix('$') {
20224 Expr {
20225 kind: ExprKind::ScalarVar(rest.to_string()),
20226 line,
20227 }
20228 } else {
20229 Expr {
20230 kind: ExprKind::String(key),
20231 line,
20232 }
20233 };
20234 parts.push(StringPart::Expr(Expr {
20235 kind: ExprKind::HashElement {
20236 hash: name,
20237 key: Box::new(key_expr),
20238 },
20239 line,
20240 }));
20241 } else if i < chars.len() && chars[i] == '[' {
20242 i += 1;
20243 let mut idx_str = String::new();
20244 while i < chars.len() && chars[i] != ']' {
20245 idx_str.push(chars[i]);
20246 i += 1;
20247 }
20248 if i < chars.len() {
20249 i += 1;
20250 }
20251 let idx_expr = if let Some(rest) = idx_str.strip_prefix('$') {
20252 Expr {
20253 kind: ExprKind::ScalarVar(rest.to_string()),
20254 line,
20255 }
20256 } else if let Ok(n) = idx_str.parse::<i64>() {
20257 Expr {
20258 kind: ExprKind::Integer(n),
20259 line,
20260 }
20261 } else {
20262 Expr {
20263 kind: ExprKind::String(idx_str),
20264 line,
20265 }
20266 };
20267 parts.push(StringPart::Expr(Expr {
20268 kind: ExprKind::ArrayElement {
20269 array: name,
20270 index: Box::new(idx_expr),
20271 },
20272 line,
20273 }));
20274 } else {
20275 self.no_interop_check_scalar_var_name(&name, line)?;
20276 parts.push(StringPart::ScalarVar(name));
20277 }
20278 } else if chars[i].is_alphabetic() || chars[i] == '_' {
20279 let mut name = String::new();
20280 while i < chars.len() && (chars[i].is_alphanumeric() || chars[i] == '_') {
20281 name.push(chars[i]);
20282 i += 1;
20283 }
20284 while i + 1 < chars.len() && chars[i] == ':' && chars[i + 1] == ':' {
20289 name.push_str("::");
20290 i += 2;
20291 while i < chars.len() && (chars[i].is_alphanumeric() || chars[i] == '_') {
20292 name.push(chars[i]);
20293 i += 1;
20294 }
20295 }
20296 if name.ends_with("::") && i < chars.len() && !crate::compat_mode() {
20310 if chars[i] == '^'
20311 && i + 1 < chars.len()
20312 && chars[i + 1].is_ascii_alphabetic()
20313 {
20314 name.push('^');
20315 name.push(chars[i + 1]);
20316 i += 2;
20317 } else if "!@$&*+;',\"\\|?/<>.0123456789~%-=()[]{}".contains(chars[i]) {
20318 name.push(chars[i]);
20319 i += 1;
20320 }
20321 }
20322 let is_topic_slot = name == "_"
20327 || (name.len() > 1
20328 && name.starts_with('_')
20329 && name[1..].bytes().all(|b| b.is_ascii_digit()));
20330 if is_topic_slot {
20331 let try_indexed = chars.get(i) == Some(&'<')
20333 && chars.get(i + 1).is_some_and(|c| c.is_ascii_digit());
20334 let mut handled_indexed = false;
20335 if try_indexed {
20336 let mut j = i + 1;
20337 while j < chars.len() && chars[j].is_ascii_digit() {
20338 j += 1;
20339 }
20340 let digits: String = chars[i + 1..j].iter().collect();
20341 if let Ok(n) = digits.parse::<usize>() {
20342 if n >= 1 {
20343 for _ in 0..n {
20344 name.push('<');
20345 }
20346 i = j;
20347 handled_indexed = true;
20348 }
20349 }
20350 }
20351 if !handled_indexed {
20352 while i < chars.len() && chars[i] == '<' {
20353 name.push('<');
20354 i += 1;
20355 }
20356 }
20357 }
20358 self.no_interop_check_scalar_var_name(&name, line)?;
20363 let mut base = if i < chars.len() && chars[i] == '{' {
20368 i += 1; let mut key = String::new();
20371 let mut depth = 1;
20372 while i < chars.len() && depth > 0 {
20373 if chars[i] == '{' {
20374 depth += 1;
20375 } else if chars[i] == '}' {
20376 depth -= 1;
20377 if depth == 0 {
20378 break;
20379 }
20380 }
20381 key.push(chars[i]);
20382 i += 1;
20383 }
20384 if i < chars.len() {
20385 i += 1;
20386 } let key_expr = if let Some(rest) = key.strip_prefix('$') {
20388 Expr {
20389 kind: ExprKind::ScalarVar(rest.to_string()),
20390 line,
20391 }
20392 } else {
20393 Expr {
20394 kind: ExprKind::String(key),
20395 line,
20396 }
20397 };
20398 Expr {
20399 kind: ExprKind::HashElement {
20400 hash: name,
20401 key: Box::new(key_expr),
20402 },
20403 line,
20404 }
20405 } else if i < chars.len() && chars[i] == '[' {
20406 i += 1;
20408 let mut idx_str = String::new();
20409 while i < chars.len() && chars[i] != ']' {
20410 idx_str.push(chars[i]);
20411 i += 1;
20412 }
20413 if i < chars.len() {
20414 i += 1;
20415 }
20416 let idx_expr = if let Some(rest) = idx_str.strip_prefix('$') {
20417 Expr {
20418 kind: ExprKind::ScalarVar(rest.to_string()),
20419 line,
20420 }
20421 } else if let Ok(n) = idx_str.parse::<i64>() {
20422 Expr {
20423 kind: ExprKind::Integer(n),
20424 line,
20425 }
20426 } else {
20427 Expr {
20428 kind: ExprKind::String(idx_str),
20429 line,
20430 }
20431 };
20432 Expr {
20433 kind: ExprKind::ArrayElement {
20434 array: name,
20435 index: Box::new(idx_expr),
20436 },
20437 line,
20438 }
20439 } else {
20440 Expr {
20442 kind: ExprKind::ScalarVar(name),
20443 line,
20444 }
20445 };
20446
20447 base = self.interp_chain_subscripts(&chars, &mut i, base, line);
20451 parts.push(StringPart::Expr(base));
20452 } else if chars[i].is_ascii_digit() {
20453 if chars[i] == '0' {
20455 i += 1;
20456 if i < chars.len() && chars[i].is_ascii_digit() {
20457 return Err(self.syntax_err(
20458 "Numeric variables with more than one digit may not start with '0'",
20459 line,
20460 ));
20461 }
20462 parts.push(StringPart::ScalarVar("0".into()));
20463 } else {
20464 let start = i;
20465 while i < chars.len() && chars[i].is_ascii_digit() {
20466 i += 1;
20467 }
20468 parts.push(StringPart::ScalarVar(chars[start..i].iter().collect()));
20469 }
20470 } else {
20471 let c = chars[i];
20472 let probe = c.to_string();
20473 if VMHelper::is_special_scalar_name_for_get(&probe)
20479 || matches!(c, '\'' | '`' | '&')
20480 {
20481 i += 1;
20482 if i < chars.len() && chars[i] == '{' {
20484 i += 1; let mut key = String::new();
20486 let mut depth = 1;
20487 while i < chars.len() && depth > 0 {
20488 if chars[i] == '{' {
20489 depth += 1;
20490 } else if chars[i] == '}' {
20491 depth -= 1;
20492 if depth == 0 {
20493 break;
20494 }
20495 }
20496 key.push(chars[i]);
20497 i += 1;
20498 }
20499 if i < chars.len() {
20500 i += 1;
20501 } let key_expr = if let Some(rest) = key.strip_prefix('$') {
20503 Expr {
20504 kind: ExprKind::ScalarVar(rest.to_string()),
20505 line,
20506 }
20507 } else {
20508 Expr {
20509 kind: ExprKind::String(key),
20510 line,
20511 }
20512 };
20513 let mut base = Expr {
20514 kind: ExprKind::HashElement {
20515 hash: probe,
20516 key: Box::new(key_expr),
20517 },
20518 line,
20519 };
20520 base = self.interp_chain_subscripts(&chars, &mut i, base, line);
20521 parts.push(StringPart::Expr(base));
20522 } else {
20523 let mut base = Expr {
20525 kind: ExprKind::ScalarVar(probe),
20526 line,
20527 };
20528 base = self.interp_chain_subscripts(&chars, &mut i, base, line);
20529 if matches!(base.kind, ExprKind::ScalarVar(_)) {
20530 if let ExprKind::ScalarVar(name) = base.kind {
20532 self.no_interop_check_scalar_var_name(&name, line)?;
20533 parts.push(StringPart::ScalarVar(name));
20534 }
20535 } else {
20536 parts.push(StringPart::Expr(base));
20537 }
20538 }
20539 } else {
20540 literal.push('$');
20541 literal.push(c);
20542 i += 1;
20543 }
20544 }
20545 } else if chars[i] == '@' && i + 1 < chars.len() {
20546 let next = chars[i + 1];
20547 if next == '$' {
20549 if !literal.is_empty() {
20550 parts.push(StringPart::Literal(std::mem::take(&mut literal)));
20551 }
20552 i += 1; debug_assert_eq!(chars[i], '$');
20554 i += 1; while i < chars.len() && chars[i].is_whitespace() {
20556 i += 1;
20557 }
20558 if i >= chars.len() {
20559 return Err(self.syntax_err(
20560 "Expected variable or block after `@$` in double-quoted string",
20561 line,
20562 ));
20563 }
20564 let inner_expr = if chars[i] == '{' {
20565 i += 1;
20566 let start = i;
20567 let mut depth = 1usize;
20568 while i < chars.len() && depth > 0 {
20569 match chars[i] {
20570 '{' => depth += 1,
20571 '}' => {
20572 depth -= 1;
20573 if depth == 0 {
20574 break;
20575 }
20576 }
20577 _ => {}
20578 }
20579 i += 1;
20580 }
20581 if depth != 0 {
20582 return Err(self.syntax_err(
20583 "Unterminated `${ ... }` after `@` in double-quoted string",
20584 line,
20585 ));
20586 }
20587 let inner: String = chars[start..i].iter().collect();
20588 i += 1; parse_expression_from_str(inner.trim(), "-e")?
20590 } else {
20591 let mut name = String::new();
20592 if chars[i] == '^' {
20593 name.push('^');
20594 i += 1;
20595 while i < chars.len() && (chars[i].is_alphanumeric() || chars[i] == '_')
20596 {
20597 name.push(chars[i]);
20598 i += 1;
20599 }
20600 } else {
20601 while i < chars.len()
20602 && (chars[i].is_alphanumeric()
20603 || chars[i] == '_'
20604 || chars[i] == ':')
20605 {
20606 name.push(chars[i]);
20607 i += 1;
20608 }
20609 while i + 1 < chars.len() && chars[i] == ':' && chars[i + 1] == ':' {
20610 name.push_str("::");
20611 i += 2;
20612 while i < chars.len()
20613 && (chars[i].is_alphanumeric() || chars[i] == '_')
20614 {
20615 name.push(chars[i]);
20616 i += 1;
20617 }
20618 }
20619 }
20620 if name.is_empty() {
20621 return Err(self.syntax_err(
20622 "Expected identifier after `@$` in double-quoted string",
20623 line,
20624 ));
20625 }
20626 Expr {
20627 kind: ExprKind::ScalarVar(name),
20628 line,
20629 }
20630 };
20631 parts.push(StringPart::Expr(Expr {
20632 kind: ExprKind::Deref {
20633 expr: Box::new(inner_expr),
20634 kind: Sigil::Array,
20635 },
20636 line,
20637 }));
20638 continue 'istr;
20639 }
20640 if next == '{' {
20641 if !literal.is_empty() {
20642 parts.push(StringPart::Literal(std::mem::take(&mut literal)));
20643 }
20644 i += 2; let (inner, close) =
20646 Self::scan_interp_braces(&chars, i).ok_or_else(|| {
20647 self.syntax_err("Unterminated @{ ... } in double-quoted string", line)
20648 })?;
20649 i = close + 1; let inner_expr = parse_expression_from_str(inner.trim(), "-e")?;
20651 parts.push(StringPart::Expr(Expr {
20652 kind: ExprKind::Deref {
20653 expr: Box::new(inner_expr),
20654 kind: Sigil::Array,
20655 },
20656 line,
20657 }));
20658 continue 'istr;
20659 }
20660 if !(next.is_alphabetic() || next == '_' || next == '+' || next == '-') {
20661 literal.push(chars[i]);
20662 i += 1;
20663 } else {
20664 if !literal.is_empty() {
20665 parts.push(StringPart::Literal(std::mem::take(&mut literal)));
20666 }
20667 i += 1;
20668 let mut name = String::new();
20669 if i < chars.len() && (chars[i] == '+' || chars[i] == '-') {
20670 name.push(chars[i]);
20671 i += 1;
20672 } else {
20673 while i < chars.len() && (chars[i].is_alphanumeric() || chars[i] == '_') {
20674 name.push(chars[i]);
20675 i += 1;
20676 }
20677 while i + 1 < chars.len() && chars[i] == ':' && chars[i + 1] == ':' {
20678 name.push_str("::");
20679 i += 2;
20680 while i < chars.len() && (chars[i].is_alphanumeric() || chars[i] == '_')
20681 {
20682 name.push(chars[i]);
20683 i += 1;
20684 }
20685 }
20686 }
20687 if i < chars.len() && chars[i] == '[' {
20688 i += 1;
20689 let start_inner = i;
20690 let mut depth = 1usize;
20691 while i < chars.len() && depth > 0 {
20692 match chars[i] {
20693 '[' => depth += 1,
20694 ']' => depth -= 1,
20695 _ => {}
20696 }
20697 if depth == 0 {
20698 let inner: String = chars[start_inner..i].iter().collect();
20699 i += 1; let indices = parse_slice_indices_from_str(inner.trim(), "-e")?;
20701 parts.push(StringPart::Expr(Expr {
20702 kind: ExprKind::ArraySlice {
20703 array: name.clone(),
20704 indices,
20705 },
20706 line,
20707 }));
20708 continue 'istr;
20709 }
20710 i += 1;
20711 }
20712 return Err(self.syntax_err(
20713 "Unterminated [ in array slice inside quoted string",
20714 line,
20715 ));
20716 }
20717 parts.push(StringPart::ArrayVar(name));
20718 }
20719 } else if chars[i] == '#'
20720 && i + 1 < chars.len()
20721 && chars[i + 1] == '{'
20722 && !crate::compat_mode()
20723 {
20724 if !literal.is_empty() {
20726 parts.push(StringPart::Literal(std::mem::take(&mut literal)));
20727 }
20728 i += 2; let inner = match Self::scan_interp_braces(&chars, i) {
20730 Some((inner, close)) => {
20731 i = close + 1; inner
20733 }
20734 None => {
20735 let rest: String = chars[i..].iter().collect();
20737 i = chars.len();
20738 rest
20739 }
20740 };
20741 let expr = parse_block_from_str(inner.trim(), "-e", line)?;
20742 parts.push(StringPart::Expr(expr));
20743 } else {
20744 literal.push(chars[i]);
20745 i += 1;
20746 }
20747 }
20748 if !literal.is_empty() {
20749 parts.push(StringPart::Literal(literal));
20750 }
20751
20752 if parts.len() == 1 {
20753 if let StringPart::Literal(s) = &parts[0] {
20754 return Ok(Expr {
20755 kind: ExprKind::String(s.clone()),
20756 line,
20757 });
20758 }
20759 }
20760 if parts.is_empty() {
20761 return Ok(Expr {
20762 kind: ExprKind::String(String::new()),
20763 line,
20764 });
20765 }
20766
20767 Ok(Expr {
20768 kind: ExprKind::InterpolatedString(parts),
20769 line,
20770 })
20771 }
20772
20773 fn expr_to_overload_key(&self, e: &Expr) -> StrykeResult<String> {
20774 match &e.kind {
20775 ExprKind::String(s) => Ok(s.clone()),
20776 _ => Err(self.syntax_err(
20777 "overload key must be a string literal (e.g. '\"\"' or '+')",
20778 e.line,
20779 )),
20780 }
20781 }
20782
20783 fn expr_to_overload_sub(&mut self, e: &Expr) -> StrykeResult<String> {
20784 match &e.kind {
20785 ExprKind::String(s) => Ok(s.clone()),
20786 ExprKind::Integer(n) => Ok(n.to_string()),
20787 ExprKind::SubroutineRef(s) | ExprKind::SubroutineCodeRef(s) => Ok(s.clone()),
20788 ExprKind::CodeRef { params, body } => {
20792 let id = self.next_overload_anon_id;
20793 self.next_overload_anon_id = self.next_overload_anon_id.saturating_add(1);
20794 let name = format!("__overload_anon_{}", id);
20795 self.pending_synthetic_subs.push(Statement {
20796 label: None,
20797 kind: StmtKind::SubDecl {
20798 name: name.clone(),
20799 params: params.clone(),
20800 body: body.clone(),
20801 prototype: None,
20802 },
20803 line: e.line,
20804 });
20805 Ok(name)
20806 }
20807 _ => Err(self.syntax_err(
20808 "overload handler must be a string literal, number (e.g. fallback => 1), or \\&subname (method in current package)",
20809 e.line,
20810 )),
20811 }
20812 }
20813}
20814
20815fn format_version_float(v: f64) -> String {
20824 let s = format!("{}", v);
20825 if !s.contains('.') && !s.contains('e') && !s.contains('E') {
20832 return format!("{}.0", s);
20833 }
20834 s
20835}
20836
20837fn merge_expr_list(parts: Vec<Expr>) -> Expr {
20838 if parts.len() == 1 {
20839 parts.into_iter().next().unwrap()
20840 } else {
20841 let line = parts.first().map(|e| e.line).unwrap_or(0);
20842 Expr {
20843 kind: ExprKind::List(parts),
20844 line,
20845 }
20846 }
20847}
20848
20849pub fn parse_expression_from_str(s: &str, file: &str) -> StrykeResult<Expr> {
20851 let mut lexer = Lexer::new_with_file(s, file);
20852 let tokens = lexer.tokenize()?;
20853 let mut parser = Parser::new_with_file(tokens, file);
20854 let e = parser.parse_expression()?;
20855 if !parser.at_eof() {
20856 return Err(parser.syntax_err(
20857 "Extra tokens in embedded string expression",
20858 parser.peek_line(),
20859 ));
20860 }
20861 Ok(e)
20862}
20863
20864pub fn parse_block_from_str(s: &str, file: &str, line: usize) -> StrykeResult<Expr> {
20866 let mut lexer = Lexer::new_with_file(s, file);
20867 let tokens = lexer.tokenize()?;
20868 let mut parser = Parser::new_with_file(tokens, file);
20869 let stmts = parser.parse_statements()?;
20870 let inner_line = stmts.first().map(|st| st.line).unwrap_or(line);
20871 let inner = Expr {
20872 kind: ExprKind::CodeRef {
20873 params: vec![],
20874 body: stmts,
20875 },
20876 line: inner_line,
20877 };
20878 Ok(Expr {
20879 kind: ExprKind::Do(Box::new(inner)),
20880 line,
20881 })
20882}
20883
20884pub fn parse_slice_indices_from_str(s: &str, file: &str) -> StrykeResult<Vec<Expr>> {
20887 let mut lexer = Lexer::new_with_file(s, file);
20888 let tokens = lexer.tokenize()?;
20889 let mut parser = Parser::new_with_file(tokens, file);
20890 parser.parse_arg_list()
20891}
20892pub fn parse_format_value_line(line: &str) -> StrykeResult<Vec<Expr>> {
20894 let trimmed = line.trim();
20895 if trimmed.is_empty() {
20896 return Ok(vec![]);
20897 }
20898 let mut lexer = Lexer::new(trimmed);
20899 let tokens = lexer.tokenize()?;
20900 let mut parser = Parser::new(tokens);
20901 let mut exprs = Vec::new();
20902 loop {
20903 if parser.at_eof() {
20904 break;
20905 }
20906 exprs.push(parser.parse_assign_expr()?);
20908 if parser.eat(&Token::Comma) {
20909 continue;
20910 }
20911 if !parser.at_eof() {
20912 return Err(parser.syntax_err("Extra tokens in format value line", parser.peek_line()));
20913 }
20914 break;
20915 }
20916 Ok(exprs)
20917}
20918
20919#[cfg(test)]
20920mod tests {
20921 use super::*;
20922
20923 fn parse_ok(code: &str) -> Program {
20924 let mut lexer = Lexer::new(code);
20925 let tokens = lexer.tokenize().expect("tokenize");
20926 let mut parser = Parser::new(tokens);
20927 parser.parse_program().expect("parse")
20928 }
20929
20930 fn parse_err(code: &str) -> String {
20931 let mut lexer = Lexer::new(code);
20932 let tokens = match lexer.tokenize() {
20933 Ok(t) => t,
20934 Err(e) => return e.message,
20935 };
20936 let mut parser = Parser::new(tokens);
20937 parser.parse_program().unwrap_err().message
20938 }
20939
20940 #[test]
20941 fn parse_empty_program() {
20942 let p = parse_ok("");
20943 assert!(p.statements.is_empty());
20944 }
20945
20946 #[test]
20947 fn parse_semicolons_only() {
20948 let p = parse_ok(";;");
20949 assert!(p.statements.len() <= 3);
20950 }
20951
20952 #[test]
20953 fn parse_simple_scalar_assignment() {
20954 let p = parse_ok("$x = 1");
20955 assert_eq!(p.statements.len(), 1);
20956 }
20957
20958 #[test]
20959 fn parse_simple_array_assignment() {
20960 let p = parse_ok("@arr = (1, 2, 3)");
20961 assert_eq!(p.statements.len(), 1);
20962 }
20963
20964 #[test]
20965 fn parse_simple_hash_assignment() {
20966 let p = parse_ok("%h = (a => 1, b => 2)");
20967 assert_eq!(p.statements.len(), 1);
20968 }
20969
20970 #[test]
20971 fn parse_subroutine_decl() {
20972 let p = parse_ok("fn foo { 1 }");
20973 assert_eq!(p.statements.len(), 1);
20974 match &p.statements[0].kind {
20975 StmtKind::SubDecl { name, .. } => assert_eq!(name, "foo"),
20976 _ => panic!("expected SubDecl"),
20977 }
20978 }
20979
20980 #[test]
20981 fn parse_class_method_expr_body_shorthand() {
20982 let p = parse_ok("class X { fn adg = \"\" }");
20983 match &p.statements[0].kind {
20984 StmtKind::ClassDecl { def } => {
20985 let m = def.method("adg").expect("adg method");
20986 let body = m.body.as_ref().expect("body");
20987 assert_eq!(body.len(), 1);
20988 match &body[0].kind {
20989 StmtKind::Expression(e) => match &e.kind {
20990 ExprKind::String(s) => assert!(s.is_empty()),
20991 _ => panic!("expected string expr, got {:?}", e.kind),
20992 },
20993 _ => panic!("expected expression stmt"),
20994 }
20995 }
20996 _ => panic!("expected ClassDecl"),
20997 }
20998 }
20999
21000 #[test]
21001 fn parse_named_fn_eq_shorthand_with_sig() {
21002 let p = parse_ok("fn add_one($x) = $x + 1");
21003 match &p.statements[0].kind {
21004 StmtKind::SubDecl {
21005 name, params, body, ..
21006 } => {
21007 assert_eq!(name, "add_one");
21008 assert_eq!(params.len(), 1);
21009 assert_eq!(body.len(), 1);
21010 }
21011 _ => panic!("expected SubDecl"),
21012 }
21013 }
21014
21015 #[test]
21016 fn parse_anon_fn_eq_shorthand_with_sig() {
21017 let p = parse_ok("my $f = fn($x) = 23");
21018 match &p.statements[0].kind {
21019 StmtKind::My(decls) => {
21020 let init = decls[0].initializer.as_ref().expect("initializer");
21021 match &init.kind {
21022 ExprKind::CodeRef { params, body } => {
21023 assert_eq!(params.len(), 1);
21024 assert_eq!(body.len(), 1);
21025 }
21026 _ => panic!("expected CodeRef"),
21027 }
21028 }
21029 _ => panic!("expected My"),
21030 }
21031 }
21032
21033 #[test]
21034 fn parse_struct_method_eq_shorthand() {
21035 let p = parse_ok("struct S { fn double($a) = $a * 2 }");
21036 match &p.statements[0].kind {
21037 StmtKind::StructDecl { def } => {
21038 assert_eq!(def.methods.len(), 1);
21039 assert_eq!(def.methods[0].name, "double");
21040 assert_eq!(def.methods[0].body.len(), 1);
21041 }
21042 _ => panic!("expected StructDecl"),
21043 }
21044 }
21045
21046 #[test]
21047 fn parse_trait_method_eq_shorthand() {
21048 let p = parse_ok("trait T { fn k = 0 }");
21049 match &p.statements[0].kind {
21050 StmtKind::TraitDecl { def } => {
21051 let m = def.method("k").expect("k");
21052 let body = m.body.as_ref().expect("default body");
21053 assert_eq!(body.len(), 1);
21054 }
21055 _ => panic!("expected TraitDecl"),
21056 }
21057 }
21058
21059 #[test]
21060 fn parse_fn_eq_shorthand_rejects_top_level_comma() {
21061 let msg = parse_err("fn z = 1, 2");
21062 assert!(
21063 msg.contains("single expression") || msg.contains("comma"),
21064 "{}",
21065 msg
21066 );
21067 }
21068
21069 #[test]
21070 fn parse_subroutine_with_prototype() {
21071 let p = parse_ok("fn foo ($$) { 1 }");
21072 assert_eq!(p.statements.len(), 1);
21073 match &p.statements[0].kind {
21074 StmtKind::SubDecl { prototype, .. } => {
21075 assert!(prototype.is_some());
21076 }
21077 _ => panic!("expected SubDecl"),
21078 }
21079 }
21080
21081 #[test]
21082 fn parse_anonymous_fn() {
21083 let p = parse_ok("my $f = fn { 1 }");
21084 assert_eq!(p.statements.len(), 1);
21085 }
21086
21087 #[test]
21088 fn parse_if_statement() {
21089 let p = parse_ok("if (1) { 2 }");
21090 assert_eq!(p.statements.len(), 1);
21091 matches!(&p.statements[0].kind, StmtKind::If { .. });
21092 }
21093
21094 #[test]
21095 fn parse_if_elsif_else() {
21096 let p = parse_ok("if (0) { 1 } elsif (1) { 2 } else { 3 }");
21097 assert_eq!(p.statements.len(), 1);
21098 }
21099
21100 #[test]
21101 fn parse_unless_statement() {
21102 let p = parse_ok("unless (0) { 1 }");
21103 assert_eq!(p.statements.len(), 1);
21104 }
21105
21106 #[test]
21107 fn parse_while_loop() {
21108 let p = parse_ok("while ($x) { $x-- }");
21109 assert_eq!(p.statements.len(), 1);
21110 }
21111
21112 #[test]
21113 fn parse_until_loop() {
21114 let p = parse_ok("until ($x) { $x++ }");
21115 assert_eq!(p.statements.len(), 1);
21116 }
21117
21118 #[test]
21119 fn parse_for_c_style() {
21120 let p = parse_ok("for (my $i=0; $i<10; $i++) { 1 }");
21121 assert_eq!(p.statements.len(), 1);
21122 }
21123
21124 #[test]
21125 fn parse_foreach_loop() {
21126 let p = parse_ok("foreach my $x (@arr) { 1 }");
21127 assert_eq!(p.statements.len(), 1);
21128 }
21129
21130 #[test]
21131 fn parse_loop_with_label() {
21132 let p = parse_ok("OUTER: for my $i (1..10) { last OUTER }");
21133 assert_eq!(p.statements.len(), 1);
21134 assert_eq!(p.statements[0].label.as_deref(), Some("OUTER"));
21135 }
21136
21137 #[test]
21138 fn parse_begin_block() {
21139 let p = parse_ok("BEGIN { 1 }");
21140 assert_eq!(p.statements.len(), 1);
21141 matches!(&p.statements[0].kind, StmtKind::Begin(_));
21142 }
21143
21144 #[test]
21145 fn parse_end_block() {
21146 let p = parse_ok("END { 1 }");
21147 assert_eq!(p.statements.len(), 1);
21148 matches!(&p.statements[0].kind, StmtKind::End(_));
21149 }
21150
21151 #[test]
21152 fn parse_package_statement() {
21153 let p = parse_ok("package Foo::Bar");
21154 assert_eq!(p.statements.len(), 1);
21155 match &p.statements[0].kind {
21156 StmtKind::Package { name } => assert_eq!(name, "Foo::Bar"),
21157 _ => panic!("expected Package"),
21158 }
21159 }
21160
21161 #[test]
21162 fn parse_use_statement() {
21163 let p = parse_ok("use strict");
21164 assert_eq!(p.statements.len(), 1);
21165 }
21166
21167 #[test]
21170 fn parse_use_module_version_pin() {
21171 let p = parse_ok("use Some::Module 2.13");
21172 assert_eq!(p.statements.len(), 1);
21173 match &p.statements[0].kind {
21174 StmtKind::Use { module, imports, version } => {
21175 assert_eq!(module, "Some::Module");
21176 assert!(imports.is_empty(), "version must not leak into imports");
21177 assert_eq!(version.as_deref(), Some("2.13"));
21178 }
21179 other => panic!("expected Use, got {:?}", other),
21180 }
21181 }
21182
21183 #[test]
21189 fn parse_use_module_version_then_imports() {
21190 let p = parse_ok("use Some::Module 1.0, 'foo', 'bar'");
21191 assert_eq!(p.statements.len(), 1);
21192 match &p.statements[0].kind {
21193 StmtKind::Use { module, imports, version } => {
21194 assert_eq!(module, "Some::Module");
21195 assert_eq!(version.as_deref(), Some("1.0"));
21196 assert!(!imports.is_empty(), "import LIST must reach the imports vec");
21197 }
21198 other => panic!("expected Use, got {:?}", other),
21199 }
21200 }
21201
21202 #[test]
21204 fn parse_use_module_no_version() {
21205 let p = parse_ok("use Foo");
21206 match &p.statements[0].kind {
21207 StmtKind::Use { version, .. } => {
21208 assert_eq!(version.as_deref(), None);
21209 }
21210 other => panic!("expected Use, got {:?}", other),
21211 }
21212 }
21213
21214 #[test]
21222 fn parse_use_module_three_segment_version_rejected() {
21223 let parsed = std::panic::catch_unwind(|| parse_ok("use Foo 1.0.0"));
21224 assert!(
21225 parsed.is_err(),
21226 "3-segment version pins are NOT supported today; if this passes \
21227 the parser was extended — update the test (and probably the \
21228 resolver's pin-arm too)"
21229 );
21230 }
21231
21232 #[test]
21239 fn parse_use_module_string_literal_is_import_not_version() {
21240 let p = parse_ok(r#"use Foo "1.0.0""#);
21241 match &p.statements[0].kind {
21242 StmtKind::Use { version, imports, .. } => {
21243 assert_eq!(version.as_deref(), None, "string literal must not become version pin");
21244 assert!(!imports.is_empty(), "string literal must reach the import list");
21245 }
21246 other => panic!("expected Use, got {:?}", other),
21247 }
21248 }
21249
21250 #[test]
21251 fn parse_no_statement() {
21252 let p = parse_ok("no warnings");
21253 assert_eq!(p.statements.len(), 1);
21254 }
21255
21256 #[test]
21257 fn parse_require_bareword() {
21258 let p = parse_ok("require Foo::Bar");
21259 assert_eq!(p.statements.len(), 1);
21260 }
21261
21262 #[test]
21263 fn parse_require_string() {
21264 let p = parse_ok(r#"require "foo.pl""#);
21265 assert_eq!(p.statements.len(), 1);
21266 }
21267
21268 #[test]
21269 fn parse_eval_block() {
21270 let p = parse_ok("eval { 1 }");
21271 assert_eq!(p.statements.len(), 1);
21272 }
21273
21274 #[test]
21275 fn parse_eval_string() {
21276 let p = parse_ok(r#"eval "1 + 2""#);
21277 assert_eq!(p.statements.len(), 1);
21278 }
21279
21280 #[test]
21281 fn parse_qw_word_list() {
21282 let p = parse_ok("my @a = qw(foo bar baz)");
21283 assert_eq!(p.statements.len(), 1);
21284 }
21285
21286 #[test]
21287 fn parse_q_string() {
21288 let p = parse_ok("my $s = q{hello}");
21289 assert_eq!(p.statements.len(), 1);
21290 }
21291
21292 #[test]
21293 fn parse_qq_string() {
21294 let p = parse_ok(r#"my $s = qq(hello $x)"#);
21295 assert_eq!(p.statements.len(), 1);
21296 }
21297
21298 #[test]
21299 fn parse_regex_match() {
21300 let p = parse_ok(r#"$x =~ /foo/"#);
21301 assert_eq!(p.statements.len(), 1);
21302 }
21303
21304 #[test]
21305 fn parse_regex_substitution() {
21306 let p = parse_ok(r#"$x =~ s/foo/bar/g"#);
21307 assert_eq!(p.statements.len(), 1);
21308 }
21309
21310 #[test]
21311 fn parse_transliterate() {
21312 let p = parse_ok(r#"$x =~ tr/a-z/A-Z/"#);
21313 assert_eq!(p.statements.len(), 1);
21314 }
21315
21316 #[test]
21317 fn parse_ternary_operator() {
21318 let p = parse_ok("my $x = $a ? 1 : 2");
21319 assert_eq!(p.statements.len(), 1);
21320 }
21321
21322 #[test]
21323 fn parse_arrow_method_call() {
21324 let p = parse_ok("$obj->method()");
21325 assert_eq!(p.statements.len(), 1);
21326 }
21327
21328 #[test]
21329 fn parse_arrow_deref_hash() {
21330 let p = parse_ok("$r->{key}");
21331 assert_eq!(p.statements.len(), 1);
21332 }
21333
21334 #[test]
21335 fn parse_arrow_deref_array() {
21336 let p = parse_ok("$r->[0]");
21337 assert_eq!(p.statements.len(), 1);
21338 }
21339
21340 #[test]
21341 fn parse_chained_arrow_deref() {
21342 let p = parse_ok("$r->{a}[0]{b}");
21343 assert_eq!(p.statements.len(), 1);
21344 }
21345
21346 #[test]
21347 fn parse_my_multiple_vars() {
21348 let p = parse_ok("my ($a, $b, $c) = (1, 2, 3)");
21349 assert_eq!(p.statements.len(), 1);
21350 }
21351
21352 #[test]
21353 fn parse_our_scalar() {
21354 let p = parse_ok("our $VERSION = '1.0'");
21355 assert_eq!(p.statements.len(), 1);
21356 }
21357
21358 #[test]
21359 fn parse_local_scalar() {
21360 let p = parse_ok("local $/ = undef");
21361 assert_eq!(p.statements.len(), 1);
21362 }
21363
21364 #[test]
21365 fn parse_state_variable() {
21366 let p = parse_ok("fn Test::counter { state $n = 0; $n++ }");
21367 assert_eq!(p.statements.len(), 1);
21368 }
21369
21370 #[test]
21371 fn parse_postfix_if() {
21372 let p = parse_ok("print 1 if $x");
21373 assert_eq!(p.statements.len(), 1);
21374 }
21375
21376 #[test]
21377 fn parse_postfix_unless() {
21378 let p = parse_ok("die 'error' unless $ok");
21379 assert_eq!(p.statements.len(), 1);
21380 }
21381
21382 #[test]
21383 fn parse_postfix_while() {
21384 let p = parse_ok("$x++ while $x < 10");
21385 assert_eq!(p.statements.len(), 1);
21386 }
21387
21388 #[test]
21389 fn parse_postfix_for() {
21390 let p = parse_ok("print for @arr");
21391 assert_eq!(p.statements.len(), 1);
21392 }
21393
21394 #[test]
21395 fn parse_last_next_redo() {
21396 let p = parse_ok("for (@a) { next if $_ < 0; last if $_ > 10 }");
21397 assert_eq!(p.statements.len(), 1);
21398 }
21399
21400 #[test]
21401 fn parse_return_statement() {
21402 let p = parse_ok("fn foo { return 42 }");
21403 assert_eq!(p.statements.len(), 1);
21404 }
21405
21406 #[test]
21407 fn parse_wantarray() {
21408 let p = parse_ok("fn foo { wantarray ? @a : $a }");
21409 assert_eq!(p.statements.len(), 1);
21410 }
21411
21412 #[test]
21413 fn parse_caller_builtin() {
21414 let p = parse_ok("my @c = caller");
21415 assert_eq!(p.statements.len(), 1);
21416 }
21417
21418 #[test]
21419 fn parse_ref_to_array() {
21420 let p = parse_ok("my $r = \\@arr");
21421 assert_eq!(p.statements.len(), 1);
21422 }
21423
21424 #[test]
21425 fn parse_ref_to_hash() {
21426 let p = parse_ok("my $r = \\%hash");
21427 assert_eq!(p.statements.len(), 1);
21428 }
21429
21430 #[test]
21431 fn parse_ref_to_scalar() {
21432 let p = parse_ok("my $r = \\$x");
21433 assert_eq!(p.statements.len(), 1);
21434 }
21435
21436 #[test]
21437 fn parse_deref_scalar() {
21438 let p = parse_ok("my $v = $$r");
21439 assert_eq!(p.statements.len(), 1);
21440 }
21441
21442 #[test]
21443 fn parse_deref_array() {
21444 let p = parse_ok("my @a = @$r");
21445 assert_eq!(p.statements.len(), 1);
21446 }
21447
21448 #[test]
21449 fn parse_deref_hash() {
21450 let p = parse_ok("my %h = %$r");
21451 assert_eq!(p.statements.len(), 1);
21452 }
21453
21454 #[test]
21455 fn parse_blessed_ref() {
21456 let p = parse_ok("bless $r, 'Foo'");
21457 assert_eq!(p.statements.len(), 1);
21458 }
21459
21460 #[test]
21461 fn parse_heredoc_basic() {
21462 let p = parse_ok("my $s = <<END;\nfoo\nEND");
21463 assert_eq!(p.statements.len(), 1);
21464 }
21465
21466 #[test]
21467 fn parse_heredoc_quoted() {
21468 let p = parse_ok("my $s = <<'END';\nfoo\nEND");
21469 assert_eq!(p.statements.len(), 1);
21470 }
21471
21472 #[test]
21473 fn parse_do_block() {
21474 let p = parse_ok("my $x = do { 1 + 2 }");
21475 assert_eq!(p.statements.len(), 1);
21476 }
21477
21478 #[test]
21479 fn parse_do_file() {
21480 let p = parse_ok(r#"do "foo.pl""#);
21481 assert_eq!(p.statements.len(), 1);
21482 }
21483
21484 #[test]
21485 fn parse_map_expression() {
21486 let p = parse_ok("my @b = map { $_ * 2 } @a");
21487 assert_eq!(p.statements.len(), 1);
21488 }
21489
21490 #[test]
21493 fn parse_dist_thread_on_scalar_empty_list_source() {
21494 let p = parse_ok("~d> on $c () map { _ * 2 }");
21495 assert_eq!(p.statements.len(), 1);
21496 let StmtKind::Expression(root) = &p.statements[0].kind else {
21497 panic!("expected Expression statement");
21498 };
21499 let ExprKind::DistReduceExpr { cluster, list, .. } = &root.kind else {
21500 panic!("expected DistReduceExpr, got {:?}", root.kind);
21501 };
21502 assert!(
21503 matches!(cluster.kind, ExprKind::ScalarVar(ref s) if s == "c"),
21504 "expected cluster $c, got {:?}",
21505 cluster.kind
21506 );
21507 assert!(
21508 matches!(list.kind, ExprKind::List(ref v) if v.is_empty()),
21509 "expected empty list source, got {:?}",
21510 list.kind
21511 );
21512 }
21513
21514 #[test]
21515 fn parse_grep_expression() {
21516 let p = parse_ok("my @b = grep { $_ > 0 } @a");
21517 assert_eq!(p.statements.len(), 1);
21518 }
21519
21520 #[test]
21521 fn parse_sort_expression() {
21522 let p = parse_ok("my @b = sort { $a <=> $b } @a");
21523 assert_eq!(p.statements.len(), 1);
21524 }
21525
21526 #[test]
21527 fn pipe_sort_does_not_swallow_next_my_decl() {
21528 let p = parse_ok("my @s = @data |> sort\nmy $j = join(\",\", @s)");
21532 assert_eq!(
21533 p.statements.len(),
21534 2,
21535 "expected 2 stmts (sort + join decl), got {}: {:?}",
21536 p.statements.len(),
21537 p.statements
21538 .iter()
21539 .map(|s| format!("{:?}", s.kind).chars().take(60).collect::<String>())
21540 .collect::<Vec<_>>(),
21541 );
21542 }
21543
21544 #[test]
21545 fn pipe_sort_multiline_pipeline_preserves_next_decl() {
21546 let p = parse_ok(
21550 "my @bk = @{$inv->by_cat(\"bakery\")} |> maps { _->label() } |> sort\nmy $j = join(\"|\", @bk)",
21551 );
21552 assert_eq!(p.statements.len(), 2);
21553 }
21554
21555 #[test]
21556 fn parse_pipe_forward() {
21557 let p = parse_ok("@a |> map { $_ * 2 }");
21558 assert_eq!(p.statements.len(), 1);
21559 }
21560
21561 #[test]
21562 fn parse_expression_from_str_simple() {
21563 let e = parse_expression_from_str("$x + 1", "-e").unwrap();
21564 assert!(matches!(e.kind, ExprKind::BinOp { .. }));
21565 }
21566
21567 #[test]
21568 fn parse_expression_from_str_extra_tokens_error() {
21569 let err = parse_expression_from_str("$x; $y", "-e").unwrap_err();
21570 assert!(err.message.contains("Extra tokens"));
21571 }
21572
21573 #[test]
21574 fn parse_slice_indices_from_str_basic() {
21575 let indices = parse_slice_indices_from_str("0, 1, 2", "-e").unwrap();
21576 assert_eq!(indices.len(), 3);
21577 }
21578
21579 #[test]
21580 fn parse_format_value_line_empty() {
21581 let exprs = parse_format_value_line("").unwrap();
21582 assert!(exprs.is_empty());
21583 }
21584
21585 #[test]
21586 fn parse_format_value_line_single() {
21587 let exprs = parse_format_value_line("$x").unwrap();
21588 assert_eq!(exprs.len(), 1);
21589 }
21590
21591 #[test]
21592 fn parse_format_value_line_multiple() {
21593 let exprs = parse_format_value_line("$a, $b, $c").unwrap();
21594 assert_eq!(exprs.len(), 3);
21595 }
21596
21597 #[test]
21598 fn parse_unclosed_brace_error() {
21599 let err = parse_err("fn foo {");
21600 assert!(!err.is_empty());
21601 }
21602
21603 #[test]
21604 fn parse_unclosed_paren_error() {
21605 let err = parse_err("print (1, 2");
21606 assert!(!err.is_empty());
21607 }
21608
21609 #[test]
21610 fn parse_invalid_statement_error() {
21611 let err = parse_err("???");
21612 assert!(!err.is_empty());
21613 }
21614
21615 #[test]
21616 fn merge_expr_list_single() {
21617 let e = Expr {
21618 kind: ExprKind::Integer(1),
21619 line: 1,
21620 };
21621 let merged = merge_expr_list(vec![e.clone()]);
21622 matches!(merged.kind, ExprKind::Integer(1));
21623 }
21624
21625 #[test]
21626 fn merge_expr_list_multiple() {
21627 let e1 = Expr {
21628 kind: ExprKind::Integer(1),
21629 line: 1,
21630 };
21631 let e2 = Expr {
21632 kind: ExprKind::Integer(2),
21633 line: 1,
21634 };
21635 let merged = merge_expr_list(vec![e1, e2]);
21636 matches!(merged.kind, ExprKind::List(_));
21637 }
21638
21639 struct NoInteropGuard {
21648 saved: Option<bool>,
21649 }
21650 impl NoInteropGuard {
21651 fn on() -> Self {
21652 let saved = crate::no_interop_mode_tls();
21653 crate::set_no_interop_mode_tls(Some(true));
21654 Self { saved }
21655 }
21656 }
21657 impl Drop for NoInteropGuard {
21658 fn drop(&mut self) {
21659 crate::set_no_interop_mode_tls(self.saved);
21660 }
21661 }
21662
21663 #[test]
21664 fn no_interop_rejects_sub_keyword() {
21665 let _g = NoInteropGuard::on();
21666 let err = parse_err("sub foo { 1 }");
21667 assert!(
21668 err.contains("--no-interop") && err.contains("fn"),
21669 "sub rejected with fn hint: got {err:?}"
21670 );
21671 }
21672
21673 #[test]
21674 fn no_interop_rejects_say() {
21675 let _g = NoInteropGuard::on();
21676 let err = parse_err("say 1");
21677 assert!(
21678 err.contains("--no-interop") && err.contains("`p`"),
21679 "say rejected with p hint: got {err:?}"
21680 );
21681 }
21682
21683 #[test]
21684 fn no_interop_rejects_scalar_keyword() {
21685 let _g = NoInteropGuard::on();
21686 let err = parse_err("my $n = scalar @x");
21687 assert!(
21688 err.contains("--no-interop") && (err.contains("len") || err.contains("cnt")),
21689 "scalar rejected with len/cnt hint: got {err:?}"
21690 );
21691 }
21692
21693 #[test]
21694 fn no_interop_rejects_reverse() {
21695 let _g = NoInteropGuard::on();
21696 let err = parse_err("my @y = reverse @x");
21697 assert!(
21698 err.contains("--no-interop") && err.contains("rev"),
21699 "reverse rejected with rev hint: got {err:?}"
21700 );
21701 }
21702
21703 #[test]
21708 fn no_interop_accepts_stryke_idioms() {
21709 let _g = NoInteropGuard::on();
21710 parse_ok("fn foo { 1 }");
21713 parse_ok("p 1");
21714 parse_ok("my @x = (1, 2, 3); my $n = len(@x)");
21715 parse_ok("my @x = (1, 2, 3); my @y = rev(@x)");
21716 }
21717
21718 #[test]
21723 fn default_mode_still_accepts_perl5_forms() {
21724 parse_ok("sub foo { 1 }");
21726 parse_ok("say 1");
21727 }
21728
21729 #[test]
21734 fn no_interop_rejects_bare_dollar_a_dollar_b() {
21735 let _g = NoInteropGuard::on();
21736 let err = parse_err("my $x = $a + $b");
21738 assert!(
21739 err.contains("--no-interop") || err.contains("$_0") || err.contains("$_1"),
21740 "$a/$b rejected with positional hint: got {err:?}"
21741 );
21742 }
21743
21744 #[test]
21748 fn no_interop_rejects_dollar_a_inside_sort_block() {
21749 let _g = NoInteropGuard::on();
21750 let err = parse_err("my @s = sort { $a <=> $b } (3, 1, 2)");
21751 assert!(
21752 err.contains("--no-interop") || err.contains("$_0") || err.contains("$_1"),
21753 "$a in sort block rejected: got {err:?}"
21754 );
21755 }
21756
21757 #[test]
21760 fn no_interop_accepts_positional_underscore_in_sort_block() {
21761 let _g = NoInteropGuard::on();
21762 parse_ok("my @s = sort { $_0 <=> $_1 } (3, 1, 2)");
21763 }
21764
21765 #[test]
21770 fn colon_range_parses_in_for_loop() {
21771 let _g = NoInteropGuard::on();
21772 parse_ok("for my $i (1:10) { p $i }");
21773 parse_ok("my @r = 0:99");
21774 parse_ok("my @r = -5:5");
21775 }
21776
21777 #[test]
21779 fn postfix_statement_modifiers_parse() {
21780 let _g = NoInteropGuard::on();
21781 parse_ok("p _ for (1, 2, 3)");
21782 parse_ok("p 1 if 1");
21783 parse_ok("p 0 unless 0");
21784 }
21785
21786 #[test]
21789 fn pipe_forward_accepts_both_function_and_block_rhs() {
21790 let _g = NoInteropGuard::on();
21791 parse_ok("my $r = 1:10 |> sum");
21792 parse_ok("my @r = 1:10 |> maps { _ * 2 }");
21793 parse_ok("my @r = 1:10 |> grep { _ % 2 == 0 } |> maps { _ + 1 }");
21794 }
21795
21796 #[test]
21798 fn bareword_positional_underscore_n_parses_in_blocks() {
21799 let _g = NoInteropGuard::on();
21800 parse_ok("my @s = sort { _0 <=> _1 } (3, 1, 2)");
21802 parse_ok("my @r = maps { _0 * 2 } (1, 2, 3)");
21804 }
21805
21806 #[test]
21810 fn no_interop_accepts_struct_decl() {
21811 let _g = NoInteropGuard::on();
21812 parse_ok("struct Point { x => Int, y => Int }");
21813 }
21814
21815 #[test]
21816 fn no_interop_accepts_enum_decl() {
21817 let _g = NoInteropGuard::on();
21818 parse_ok("enum Color { Red, Green, Blue }");
21819 parse_ok("enum Maybe { Just => Int, Nothing }");
21823 }
21824
21825 #[test]
21826 fn no_interop_accepts_class_decl_with_methods() {
21827 let _g = NoInteropGuard::on();
21828 parse_ok(
21829 "class Rect {\n width: Float\n height: Float\n\n fn area { $self->width * $self->height }\n}",
21830 );
21831 }
21832
21833 #[test]
21834 fn no_interop_accepts_trait_decl() {
21835 let _g = NoInteropGuard::on();
21836 parse_ok("trait Greeter { fn greet; fn loudly { p \"GREET\" } }");
21837 }
21838
21839 #[test]
21844 fn defined_or_assign_compound_operators_parse() {
21845 let _g = NoInteropGuard::on();
21846 parse_ok("my $h = {}; $h->{x} ||= []");
21847 parse_ok("my $v; $v //= 0");
21848 }
21849
21850 #[test]
21853 fn explicit_hashref_literal_parses() {
21854 let _g = NoInteropGuard::on();
21855 parse_ok("my $row = +{ region => \"north\", qty => 10 }");
21856 parse_ok("my @rows = (+{ a => 1 }, +{ a => 2 })");
21857 }
21858
21859 #[test]
21862 fn eval_block_and_dollar_at_parse() {
21863 let _g = NoInteropGuard::on();
21864 parse_ok("my $r = eval { 1 + 2 }; p $@ if $@");
21865 parse_ok("eval { die \"boom\" }; p $@");
21866 }
21867
21868 #[test]
21871 fn try_catch_parses() {
21872 let _g = NoInteropGuard::on();
21873 parse_ok("try { die \"boom\" } catch ($e) { p $e }");
21874 }
21875
21876 #[test]
21879 fn file_test_operators_parse() {
21880 let _g = NoInteropGuard::on();
21881 parse_ok("p 1 if -d \"/tmp\"");
21882 parse_ok("p 2 if -f \"/etc/hosts\"");
21883 parse_ok("p 3 if -e $0");
21884 parse_ok("my $sz = -s \"/etc/hosts\"");
21885 }
21886
21887 #[test]
21891 fn thread_macros_parse() {
21892 let _g = NoInteropGuard::on();
21893 parse_ok("my $r = ~> 5 +1 *2");
21894 parse_ok("my @r = ~> (1,2,3) maps { _ * 2 }");
21895 }
21896
21897 #[test]
21901 fn hash_destructure_sub_signature_parses() {
21902 let _g = NoInteropGuard::on();
21903 parse_ok("fn handle({ name => $name, qty => $qty }) { p \"$name x $qty\" }");
21904 }
21905
21906 #[test]
21909 fn anonymous_fn_parses() {
21910 let _g = NoInteropGuard::on();
21911 parse_ok("my $f = fn { _0 * 2 }");
21912 parse_ok("my @doubled = maps { _0 * 2 } (1, 2, 3)");
21913 }
21914
21915 #[test]
21917 fn ternary_and_chained_ternary_parse() {
21918 let _g = NoInteropGuard::on();
21919 parse_ok("my $r = $x > 0 ? \"pos\" : \"neg\"");
21920 parse_ok("my $r = $x > 0 ? \"pos\" : $x < 0 ? \"neg\" : \"zero\"");
21921 }
21922
21923 #[test]
21926 fn array_slice_with_negative_indices_parses() {
21927 let _g = NoInteropGuard::on();
21928 parse_ok("my @arr = (1,2,3,4,5); my @tail = @arr[-3:-1]");
21929 parse_ok("my @arr = (1,2,3); my @last_two = @arr[-2:]");
21930 }
21931
21932 #[test]
21935 fn state_variable_declaration_parses() {
21936 let _g = NoInteropGuard::on();
21937 parse_ok("fn my_counter { state $n = 0; $n++; $n }");
21940 parse_ok("fn my_memo($k) { state %cache; $cache{$k} //= compute($k) }");
21941 }
21942
21943 #[test]
21946 fn our_declaration_parses() {
21947 let _g = NoInteropGuard::on();
21948 parse_ok("our $VERSION = 1.0");
21949 parse_ok("our @EXPORT = (1, 2, 3)");
21950 }
21951
21952 #[test]
21955 fn regex_binding_operators_parse() {
21956 let _g = NoInteropGuard::on();
21957 parse_ok("p 1 if $s =~ /^\\d+$/");
21958 parse_ok("p 0 unless $s !~ /[A-Z]/");
21959 parse_ok("my @m = $s =~ /(\\w+)/g");
21960 }
21961
21962 #[test]
21966 fn nested_data_structure_literals_parse() {
21967 let _g = NoInteropGuard::on();
21968 parse_ok("my %h = (a => [1, 2, 3], b => [4, 5])");
21969 parse_ok("my @rows = (+{ x => 1 }, +{ x => 2 })");
21970 parse_ok("my %grid = (cells => [+{ row => 1 }, +{ row => 2 }])");
21971 }
21972
21973 #[test]
21976 fn anonymous_fn_with_explicit_params_parses() {
21977 let _g = NoInteropGuard::on();
21978 parse_ok("my $add = fn ($x, $y) { $x + $y }");
21979 parse_ok("my $h = fn ($v, %opts) { p $v; p %opts }");
21980 }
21981
21982 #[test]
21985 fn package_declaration_parses() {
21986 let _g = NoInteropGuard::on();
21987 parse_ok("package Foo; my $x = 1");
21988 parse_ok("package Foo::Bar::Baz; our $VERSION = 0.01");
21989 }
21990
21991 #[test]
21995 fn loop_control_keywords_parse() {
21996 let _g = NoInteropGuard::on();
21997 parse_ok("for my $i (1:10) { next if $i % 2; p $i }");
21998 parse_ok("while (1) { last if $done }");
21999 parse_ok("my $rerun = 0; for (1:5) { if ($rerun) { redo } }");
22000 }
22001
22002 #[test]
22005 fn labelled_loops_parse() {
22006 let _g = NoInteropGuard::on();
22007 parse_ok("OUTER: for my $i (1:10) { last OUTER if $i > 5 }");
22008 parse_ok("OUTER: for my $i (1:3) { INNER: for my $j (1:3) { next OUTER if $j > $i } }");
22009 }
22010
22011 #[test]
22015 fn string_repeat_x_operator_parses() {
22016 let _g = NoInteropGuard::on();
22017 parse_ok("my $sep = \"-\" x 40");
22018 parse_ok("my @zeros = (0) x 100");
22019 parse_ok("my $bar = \"#\" x $count");
22020 }
22021
22022 #[test]
22025 fn chomp_chop_parse() {
22026 let _g = NoInteropGuard::on();
22027 parse_ok("chomp(my $line = <STDIN>)");
22028 parse_ok("my $s = \"hi\\n\"; chomp $s");
22029 parse_ok("my $t = \"hi\"; chop $t");
22030 }
22031
22032 #[test]
22035 fn substitution_operator_parses() {
22036 let _g = NoInteropGuard::on();
22037 parse_ok("my $s = \"abc\"; $s =~ s/b/X/");
22038 parse_ok("my $s = \"AaBb\"; $s =~ s/[a-z]//g");
22039 parse_ok("my $s = \"Hello\"; $s =~ s/(.)/\\1\\1/g");
22040 }
22041
22042 #[test]
22045 fn sprintf_parses_with_format_specs() {
22046 let _g = NoInteropGuard::on();
22047 parse_ok("my $row = sprintf(\"%-10s %5d\", \"foo\", 42)");
22048 parse_ok("p sprintf(\"%.3f ms\", 1.234)");
22049 parse_ok("p sprintf(\"%04x\", 255)");
22050 }
22051
22052 #[test]
22055 fn diamond_stdin_reads_parse() {
22056 let _g = NoInteropGuard::on();
22057 parse_ok("my $line = <STDIN>");
22058 parse_ok("my @lines = <STDIN>");
22059 parse_ok("while (my $line = <STDIN>) { p $line }");
22060 }
22061
22062 #[test]
22065 fn chained_method_calls_parse() {
22066 let _g = NoInteropGuard::on();
22067 parse_ok("$obj->foo->bar");
22068 parse_ok("my $r = $row->{region}");
22069 parse_ok("$obj->set(1)->get");
22070 }
22071
22072 #[test]
22075 fn hash_deref_forms_parse() {
22076 let _g = NoInteropGuard::on();
22077 parse_ok("my $h = +{a=>1}; my %copy = %$h");
22078 parse_ok("my $h = +{a=>1}; my @k = keys %$h");
22079 parse_ok("my $h = +{a=>1}; p $h->{a}");
22080 parse_ok("my $h = +{a=>1, b=>2}; p len(keys %{$h})");
22081 }
22082
22083 #[test]
22086 fn die_unless_assertion_parses() {
22087 let _g = NoInteropGuard::on();
22088 parse_ok("die \"x must be 1\" unless 1 == 1");
22089 parse_ok("die \"empty list\" if len(@x) == 0");
22090 parse_ok("my $x = 1; die \"hi\" unless $x");
22091 }
22092
22093 #[test]
22096 fn qw_literal_parses() {
22097 let _g = NoInteropGuard::on();
22098 parse_ok("my @w = qw(red green blue)");
22099 parse_ok("for my $name (qw(Alice Bob Carol)) { p $name }");
22100 }
22101
22102 #[test]
22105 fn array_slice_with_explicit_indices_parses() {
22106 let _g = NoInteropGuard::on();
22107 parse_ok("my @a = (10, 20, 30, 40); my @s = @a[0, 2]");
22108 parse_ok("my @a = (10, 20, 30); my @s = @a[2, 0, 1]");
22109 }
22110
22111 #[test]
22114 fn hash_slice_with_keys_parses() {
22115 let _g = NoInteropGuard::on();
22116 parse_ok("my %h = (a=>1, b=>2, c=>3); my @v = @h{'a','c'}");
22117 }
22118
22119 #[test]
22123 fn bitwise_operators_parse() {
22124 let _g = NoInteropGuard::on();
22125 parse_ok("my $x = 0xAA; my $y = $x & 0x0F");
22126 parse_ok("my $x = 1; my $y = $x | 2 | 4");
22127 parse_ok("my $x = 0xFF; my $y = $x ^ 0x80");
22128 parse_ok("my $x = 0xAA; my $y = ~$x & 0xff");
22129 parse_ok("my $x = 1; my $y = $x << 4");
22130 parse_ok("my $x = 0xF0; my $y = $x >> 2");
22131 }
22132
22133 #[test]
22136 fn numeric_literal_prefixes_parse() {
22137 let _g = NoInteropGuard::on();
22138 parse_ok("my $hex = 0xCAFEBABE");
22139 parse_ok("my $bin = 0b1101");
22140 parse_ok("my $hex8 = 0xff & 0x0f");
22141 }
22142
22143 #[test]
22146 fn negative_array_index_parses() {
22147 let _g = NoInteropGuard::on();
22148 parse_ok("my @arr = (1, 2, 3); p $arr[-1]");
22151 parse_ok("my @stack = (10, 20, 30); p $stack[-1] if len(@stack) > 0");
22152 }
22153
22154 #[test]
22157 fn loop_control_standalone_parses() {
22158 let _g = NoInteropGuard::on();
22159 parse_ok("while (1) { last }");
22160 parse_ok("for my $i (1:10) { next if $i == 3 }");
22161 parse_ok("fn ret { return 42 }");
22162 }
22163
22164 #[test]
22168 fn shift_pop_on_array_parses() {
22169 let _g = NoInteropGuard::on();
22170 parse_ok("my @a = (1, 2, 3); my $first = shift @a");
22171 parse_ok("my @a = (1, 2, 3); my $last = pop @a");
22172 parse_ok("fn drop_first(@xs) { shift @xs; @xs }");
22173 }
22174
22175 #[test]
22178 fn special_internal_names_parse() {
22179 let _g = NoInteropGuard::on();
22180 parse_ok("p __FILE__");
22181 parse_ok("p __LINE__");
22182 parse_ok("p __PACKAGE__");
22183 parse_ok("p __FILE__ . \":\" . __LINE__");
22184 }
22185
22186 #[test]
22189 fn deeply_nested_ternary_parses() {
22190 let _g = NoInteropGuard::on();
22191 parse_ok("my $g = ($p eq \"a\") ? 1 : ($p eq \"b\") ? 2 : ($p eq \"c\") ? 3 : 4");
22192 }
22193
22194 #[test]
22198 fn array_of_hashref_chained_subscript_parses() {
22199 let _g = NoInteropGuard::on();
22200 parse_ok("my @rows = (+{name=>'a',sc=>1}); p $rows[0]->{name}");
22201 parse_ok("my @rows = (+{n=>10},+{n=>20}); p $rows[-1]->{n}");
22202 }
22203
22204 #[test]
22207 fn nested_for_loops_parse() {
22208 let _g = NoInteropGuard::on();
22209 parse_ok("for my $r (0:5) { for my $c (0:5) { p \"$r,$c\" } }");
22210 }
22211
22212 #[test]
22216 fn dot_assign_string_append_parses() {
22217 let _g = NoInteropGuard::on();
22218 parse_ok("my $s = \"a\"; $s .= \"b\"");
22219 parse_ok("my $out = \"\"; $out .= \"x\" for (1:3)");
22220 }
22221
22222 #[test]
22225 fn unshift_parses() {
22226 let _g = NoInteropGuard::on();
22227 parse_ok("my @path = (3, 4); unshift @path, 2; unshift @path, 1");
22228 parse_ok("my @q; unshift @q, $_ for (1:5)");
22229 }
22230
22231 #[test]
22234 fn defined_or_fallback_parses() {
22235 let _g = NoInteropGuard::on();
22236 parse_ok("my $x; my $y = $x // 0");
22237 parse_ok("my %h = (a=>1); my $v = $h{missing} // -1");
22238 parse_ok("p 1 if defined $foo");
22239 }
22240
22241 #[test]
22244 fn rev_over_range_parses() {
22245 let _g = NoInteropGuard::on();
22246 parse_ok("for my $i (rev 0:9) { p $i }");
22247 parse_ok("my @r = rev (1, 2, 3, 4)");
22248 }
22249
22250 #[test]
22253 fn array_deref_forms_parse() {
22254 let _g = NoInteropGuard::on();
22255 parse_ok("my $r = [1, 2, 3]; my @copy = @$r");
22256 parse_ok("my $r = [1, 2, 3]; my @copy = @{$r}");
22257 parse_ok("my @rs = ([1], [2, 3]); my @flat; push @flat, @$_ for @rs");
22258 }
22259
22260 #[test]
22263 fn hashref_subscript_alt_forms_parse() {
22264 let _g = NoInteropGuard::on();
22265 parse_ok("my $h = +{a=>1}; p $h->{a}");
22266 parse_ok("my $h = +{a=>1}; p ${$h}{a}");
22267 }
22268
22269 #[test]
22272 fn numeric_builtins_parse() {
22273 let _g = NoInteropGuard::on();
22274 parse_ok("my $x = abs(-5)");
22275 parse_ok("my $f = int(3.7)");
22276 parse_ok("my $s = sqrt(2)");
22277 parse_ok("my $n = int($x * 100 + 0.5) / 100");
22278 }
22279
22280 #[test]
22284 fn local_declaration_parses() {
22285 let _g = NoInteropGuard::on();
22286 parse_ok("our $g = 1; (fn { local $g = 99; p $g })->()");
22288 }
22289
22290 #[test]
22294 fn srand_rand_parse() {
22295 let _g = NoInteropGuard::on();
22296 parse_ok("srand(42); my $r = rand(6)");
22297 parse_ok("srand(); my $r = int(rand(100))");
22298 }
22299
22300 #[test]
22303 fn substr_parses() {
22304 let _g = NoInteropGuard::on();
22305 parse_ok("my $s = \"hello\"; p substr($s, 0, 1)");
22306 parse_ok("my $s = \"hello\"; p substr($s, 1)");
22307 parse_ok("my $s = \"hello\"; p substr($s, -2)");
22308 }
22309
22310 #[test]
22313 fn underscore_separators_in_numbers_parse() {
22314 let _g = NoInteropGuard::on();
22315 parse_ok("my $n = 1_000_000");
22316 parse_ok("my $r = 1_000_000 / 365");
22317 parse_ok("my $hex = 0xff_ff");
22318 }
22319
22320 #[test]
22323 fn arrayref_of_arrayref_access_parses() {
22324 let _g = NoInteropGuard::on();
22325 parse_ok("my $grid = [[1, 2], [3, 4]]");
22326 parse_ok("my $grid = [[1, 2], [3, 4]]; p $grid->[0]->[1]");
22327 parse_ok("my $grid = [[1, 2], [3, 4]]; p $grid->[1][0]");
22328 }
22329
22330 #[test]
22333 fn arrow_chain_assignment_parses() {
22334 let _g = NoInteropGuard::on();
22335 parse_ok("my $g = [[0, 0]]; $g->[0]->[1] = 99");
22336 parse_ok("my $g = [[0, 0]]; $g->[0][1] = 99");
22337 }
22338
22339 #[test]
22342 fn tuple_destructure_from_arrayref_parses() {
22343 let _g = NoInteropGuard::on();
22344 parse_ok("my $e = [10, 20]; my ($lhs, $rhs) = @$e");
22345 parse_ok("my $e = [\"x\", 1]; my ($name, $weight) = ($e->[0], $e->[1])");
22346 }
22347
22348 #[test]
22351 fn next_last_unless_postfix_parses() {
22352 let _g = NoInteropGuard::on();
22353 parse_ok("for my $i (1:10) { next unless $i % 2 == 0; p $i }");
22354 parse_ok("while (1) { last unless $live }");
22355 }
22356
22357 #[test]
22360 fn keys_on_braced_hash_deref_parses() {
22361 let _g = NoInteropGuard::on();
22362 parse_ok("my $h = +{a=>1}; my @k = keys %{$h}");
22363 parse_ok("my $h = +{a=>+{b=>1}}; my @k = keys %{$h->{a}}");
22364 }
22365
22366 #[test]
22370 fn namespaced_fn_decl_parses() {
22371 let _g = NoInteropGuard::on();
22372 parse_ok("fn Module::method($x) { $x * 2 }");
22373 parse_ok("fn Foo::Bar::helper { 42 }");
22374 parse_ok("fn Demo::run { p \"running\" }");
22375 }
22376
22377 #[test]
22380 fn namespaced_fn_call_parses() {
22381 let _g = NoInteropGuard::on();
22382 parse_ok("fn Module::add($x, $y) { $x + $y } p Module::add(2, 3)");
22383 parse_ok("fn Foo::Bar::baz { 1 } fn Demo::main { Foo::Bar::baz() + Foo::Bar::baz() }");
22386 }
22387
22388 #[test]
22391 fn index_builtin_parses() {
22392 let _g = NoInteropGuard::on();
22393 parse_ok("my $i = index(\"hello world\", \"world\")");
22394 parse_ok("my $i = index($s, $pat, 0)");
22395 }
22396
22397 #[test]
22401 fn rev_on_array_literal_parses() {
22402 let _g = NoInteropGuard::on();
22403 parse_ok("my @r = rev (1, 2, 3, 4)");
22404 parse_ok("for my $x (rev (\"a\", \"b\", \"c\")) { p $x }");
22405 }
22406
22407 #[test]
22412 fn numeric_and_string_comparison_in_one_expr_parses() {
22413 let _g = NoInteropGuard::on();
22414 parse_ok("my $r = $_0 <=> $_1 || $name cmp $other");
22415 parse_ok("p 1 if $x == $y && $name eq \"foo\"");
22416 }
22417
22418 #[test]
22424 fn bareword_positional_in_sort_reduce_blocks_parses() {
22425 let _g = NoInteropGuard::on();
22426 parse_ok("my @s = sort { _0 <=> _1 } (3, 1, 2)");
22428 parse_ok("my $r = (1, 2, 3) |> reduce { _0 + _1 }");
22430 parse_ok("my $s = (\"a\", \"b\") |> reduce { _0 . _1 }");
22432 }
22433
22434 #[test]
22437 fn bareword_topic_in_maps_grep_parses() {
22438 let _g = NoInteropGuard::on();
22439 parse_ok("my @r = (1, 2, 3) |> maps { _ * 2 }");
22440 parse_ok("my @r = (1, 2, 3, 4) |> grep { _ % 2 == 0 }");
22441 parse_ok("my @r = 1:100 |> pmap { _ ** 3 }");
22442 parse_ok("my @r = 1:100 |> pgrep { _ % 7 == 0 }");
22443 }
22444
22445 #[test]
22449 fn bareword_vs_sigil_in_string_interp_parses() {
22450 let _g = NoInteropGuard::on();
22451 parse_ok("my @r = (5, 10) |> maps { _ + 1 }");
22453 parse_ok("p \"first=$_0 second=$_1\"");
22457 parse_ok("p \"got: $_\"");
22458 }
22459
22460 #[test]
22463 fn bareword_topic_in_postfix_for_parses() {
22464 let _g = NoInteropGuard::on();
22465 parse_ok("my $n = 0; $n += _ for (1, 2, 3, 4)");
22466 parse_ok("my %h; $h{_}++ for (\"a\", \"b\", \"a\")");
22467 }
22468
22469 #[test]
22472 fn bareword_topic_as_hash_key_parses() {
22473 let _g = NoInteropGuard::on();
22474 parse_ok("my %h; $h{_}++ for (\"a\", \"b\", \"a\")");
22475 parse_ok("my @arr = (1, 2, 3); my %seen; $seen{$arr[_]}++ for (0, 1, 2)");
22476 }
22477
22478 #[test]
22481 fn bareword_topic_inside_subscript_chain_parses() {
22482 let _g = NoInteropGuard::on();
22483 parse_ok(
22484 "my @c = (\"a\", \"b\", \"c\"); my %seen = (a => 1, b => 2, c => 1); \
22485 my @hits = grep { $seen{$c[_]} == 1 } (0, 1, 2)",
22486 );
22487 }
22488
22489 #[test]
22492 fn ref_builtin_parses() {
22493 let _g = NoInteropGuard::on();
22494 parse_ok("my $x = [1, 2]; p ref($x)");
22495 parse_ok("my $r = +{a => 1}; p 1 if ref($r) eq \"HASH\"");
22496 }
22497
22498 #[test]
22502 fn expression_bodied_recursive_fn_parses() {
22503 let _g = NoInteropGuard::on();
22504 parse_ok("fn N::gcd = _1 == 0 ? _0 : N::gcd(_1, _0 % _1)");
22505 parse_ok("fn N::lcm = _0 * _1 / N::gcd(_0, _1)");
22506 }
22507
22508 #[test]
22511 fn expression_bodied_pipe_reduce_parses() {
22512 let _g = NoInteropGuard::on();
22513 parse_ok(
22514 "fn N::gcd = _1 == 0 ? _0 : N::gcd(_1, _0 % _1); \
22515 fn N::gcd_list = @{_} |> reduce { N::gcd(_0, _1) }",
22516 );
22517 }
22518
22519 #[test]
22522 fn reduce_fold_with_hashref_accumulator_parses() {
22523 let _g = NoInteropGuard::on();
22524 parse_ok(
22525 "my @xs = (3, 2, 3); \
22526 my $st = (+{cur => 0, best => -100}, @xs) |> reduce { \
22527 +{ cur => _1, best => _0->{best} } \
22528 }",
22529 );
22530 }
22531
22532 #[test]
22535 fn array_deref_slice_with_variable_bounds_parses() {
22536 let _g = NoInteropGuard::on();
22537 parse_ok(
22538 "my @a = (10, 20, 30, 40, 50); my $r = \\@a; \
22539 my $lo = 1; my $hi = 3; \
22540 my @s = @$r[$lo:$hi]",
22541 );
22542 }
22543
22544 #[test]
22547 fn min_max_over_array_deref_slice_parses() {
22548 let _g = NoInteropGuard::on();
22549 parse_ok(
22550 "my @a = (1, 3, 2, 5); my $r = \\@a; \
22551 my $lo = 0; my $hi = 2; \
22552 my $m1 = min(@$r[$lo:$hi]); \
22553 my $m2 = max(@$r[$lo:$hi])",
22554 );
22555 }
22556
22557 #[test]
22561 fn flat_maps_with_recursive_call_parses() {
22562 let _g = NoInteropGuard::on();
22563 parse_ok(
22564 "fn Flat::flatten($r) = @$r |> flat_maps { \
22565 ref(_) eq \"ARRAY\" ? Flat::flatten(_) : (_) \
22566 }",
22567 );
22568 }
22569
22570 #[test]
22573 fn nested_map_with_outer_lexical_capture_parses() {
22574 let _g = NoInteropGuard::on();
22575 parse_ok(
22576 "my @lists = ([1,2,3], [10,20,30]); \
22577 my @r = 0:2 |> maps { my $i = _; [map { _->[$i] } @lists] }",
22578 );
22579 }
22580
22581 #[test]
22584 fn array_deref_of_bareword_topic_parses() {
22585 let _g = NoInteropGuard::on();
22586 parse_ok("my @lists = ([1,2,3], [10,20,30]); p min(map { len(@{_}) } @lists)");
22587 }
22588
22589 #[test]
22594 fn thread_macro_accepts_glob_rand_srand_stages() {
22595 parse_ok("my @r = ~> \"/tmp/*\" glob sort");
22596 parse_ok("my $i = ~> 100 rand int");
22597 parse_ok("~> 42 srand");
22598 }
22599
22600 #[test]
22604 fn recursive_fn_with_arrayref_assignment_parses() {
22605 let _g = NoInteropGuard::on();
22606 parse_ok(
22607 "fn UF::find($uf, $x) { \
22608 return $x if $uf->{parent}[$x] == $x; \
22609 $uf->{parent}[$x] = UF::find($uf, $uf->{parent}[$x]); \
22610 $uf->{parent}[$x] \
22611 }",
22612 );
22613 }
22614
22615 #[test]
22618 fn hashref_init_with_range_and_repeat_parses() {
22619 let _g = NoInteropGuard::on();
22620 parse_ok("fn UF::new($n) = +{ parent => [0:$n - 1], rank => [(0) x $n], count => $n }");
22621 }
22622
22623 #[test]
22626 fn postfix_for_arrayref_deref_parses() {
22627 let _g = NoInteropGuard::on();
22628 parse_ok("my @words = (\"a\", \"b\"); my $r = \\@words; my @out; push @out, $_ for @$r");
22629 }
22630
22631 #[test]
22634 fn tuple_swap_destructure_parses() {
22635 let _g = NoInteropGuard::on();
22636 parse_ok("my $ra = 1; my $rb = 2; ($ra, $rb) = ($rb, $ra)");
22637 }
22638
22639 #[test]
22644 fn explicit_2d_array_row_init_parses() {
22645 let _g = NoInteropGuard::on();
22646 parse_ok(
22647 "my @d; my $m = 3; my $n = 4; \
22648 for my $i (0:$m) { $d[$i] = [(0) x ($n + 1)] }",
22649 );
22650 }
22651
22652 #[test]
22655 fn min_with_three_args_parses() {
22656 let _g = NoInteropGuard::on();
22657 parse_ok("my $x = min(1, 2, 3)");
22658 parse_ok("my @d; $d[0][0] = 5; my $r = min($d[0][0] + 1, $d[0][0] + 1, $d[0][0] + 0)");
22659 }
22660
22661 #[test]
22664 fn string_slice_with_len_bound_parses() {
22665 let _g = NoInteropGuard::on();
22666 parse_ok(
22667 "my $w = \"apple\"; my $pre = \"app\"; \
22668 my $ok = len($w) >= len($pre) && $w[0:len($pre) - 1] eq $pre",
22669 );
22670 }
22671
22672 #[test]
22675 fn sort_block_with_arrow_deref_topic_parses() {
22676 let _g = NoInteropGuard::on();
22677 parse_ok(
22678 "my @edges = ([0,1,4], [2,3,1], [1,2,2]); \
22679 my @sorted = sort { _0->[2] <=> _1->[2] } @edges",
22680 );
22681 }
22682
22683 #[test]
22686 fn cstyle_for_with_postdecrement_parses() {
22687 let _g = NoInteropGuard::on();
22688 parse_ok(
22689 "my @arr = (1, 2, 3, 4, 5); \
22690 my $n = len(@arr); \
22691 for (my $i = $n - 1; $i > 0; $i--) { p $arr[$i] }",
22692 );
22693 }
22694
22695 #[test]
22698 fn tuple_swap_arrayref_index_parses() {
22699 let _g = NoInteropGuard::on();
22700 parse_ok(
22701 "my @arr = (1, 2, 3); my $r = \\@arr; my $i = 0; my $j = 2; \
22702 ($r->[$i], $r->[$j]) = ($r->[$j], $r->[$i])",
22703 );
22704 }
22705
22706 #[test]
22709 fn recursive_backtracking_arrayref_mutation_parses() {
22710 let _g = NoInteropGuard::on();
22711 parse_ok(
22712 "fn Q::go($n, $cols, $count_ref) { \
22713 my $r = len(@$cols); \
22714 if ($r == $n) { $$count_ref++; return } \
22715 for my $c (0:$n - 1) { \
22716 push @$cols, $c; \
22717 Q::go($n, $cols, $count_ref); \
22718 pop @$cols \
22719 } \
22720 }",
22721 );
22722 }
22723
22724 #[test]
22727 fn nested_hashref_chain_parses() {
22728 let _g = NoInteropGuard::on();
22729 parse_ok(
22730 "my $c = +{ nodes => +{ a => +{ val => 1, prev => undef, next => \"b\" } } }; \
22731 my $p = $c->{nodes}{a}{prev}; \
22732 my $n = $c->{nodes}{a}{next}",
22733 );
22734 }
22735
22736 #[test]
22739 fn scalar_ref_postincrement_parses() {
22740 let _g = NoInteropGuard::on();
22741 parse_ok("my $n = 0; my $ref = \\$n; $$ref++; p $n");
22742 }
22743
22744 #[test]
22747 fn hash_of_hash_autoviv_increment_parses() {
22748 let _g = NoInteropGuard::on();
22749 parse_ok(
22750 "my %table; \
22751 my $prev = \"the\"; my $next = \"quick\"; \
22752 $table{$prev} //= +{}; \
22753 $table{$prev}{$next}++",
22754 );
22755 }
22756
22757 #[test]
22760 fn cstyle_for_with_literal_bounds_parses() {
22761 let _g = NoInteropGuard::on();
22762 parse_ok("my $sum = 0; for (my $i = 0; $i < 10; $i++) { $sum += $i }");
22763 }
22764
22765 #[test]
22771 fn recursive_expression_body_with_ternary_parses() {
22772 let _g = NoInteropGuard::on();
22773 parse_ok("fn J::s($n, $k) = $n == 1 ? 0 : (J::s($n - 1, $k) + $k) % $n");
22774 }
22775
22776 #[test]
22780 fn namespaced_quote_like_tail_segments_parse() {
22781 let _g = NoInteropGuard::on();
22782 parse_ok("fn Foo::s($x) = $x + 1");
22783 parse_ok("fn Foo::m($x) = $x * 2");
22784 parse_ok("fn Foo::q($x) = $x");
22785 parse_ok("fn Foo::qq($x) = $x");
22786 parse_ok("fn Foo::qx($x) = $x");
22787 parse_ok("fn Foo::qr($x) = $x");
22788 parse_ok("fn Foo::tr($x) = $x");
22789 parse_ok("fn Foo::y($x) = $x");
22790 }
22791
22792 #[test]
22795 fn splice_single_remove_parses() {
22796 let _g = NoInteropGuard::on();
22797 parse_ok("my @circle = 0:5; splice @circle, 2, 1");
22798 }
22799
22800 #[test]
22802 fn atan2_call_parses() {
22803 let _g = NoInteropGuard::on();
22804 parse_ok("fn MC::true_pi = atan2(0, -1)");
22805 parse_ok("my $pi = atan2(0, -1)");
22806 }
22807
22808 #[test]
22811 fn flat_2d_array_indexing_parses() {
22812 let _g = NoInteropGuard::on();
22813 parse_ok(
22814 "my @board = (0) x 81; \
22815 my $r = 3; my $c = 5; \
22816 $board[$r * 9 + $c] = 7; \
22817 my $v = $board[$r * 9 + $c]",
22818 );
22819 }
22820
22821 #[test]
22827 fn par_top_level_prefix_form_parses() {
22828 let _g = NoInteropGuard::on();
22829 parse_ok("my @r = par { _ * 2 } (1, 2, 3, 4)");
22830 parse_ok("par { p _ } @big");
22831 }
22832
22833 #[test]
22837 fn dp_max_step_chained_subscript_parses() {
22838 let _g = NoInteropGuard::on();
22839 parse_ok(
22840 "my @d; for my $i (0:3) { $d[$i] = [(0) x 4] } \
22841 $d[1][1] = max($d[0][1], $d[1][0]); \
22842 my $r = $d[1][1]",
22843 );
22844 }
22845
22846 #[test]
22848 fn rolling_hash_arithmetic_parses() {
22849 let _g = NoInteropGuard::on();
22850 parse_ok(
22851 "my $h = 0; my $base = 257; my $mod = 1000000007; my $high = 256; \
22852 my $drop = 65; my $add = 90; \
22853 $h = (($h - $drop * $high) * $base + $add) % $mod; \
22854 $h = ($h + $mod) % $mod",
22855 );
22856 }
22857
22858 #[test]
22861 fn triple_nested_2d_via_k_parses() {
22862 let _g = NoInteropGuard::on();
22863 parse_ok(
22864 "my @d; for my $i (0:3) { $d[$i] = [(0) x 4] } \
22865 for my $k (0:3) { for my $i (0:3) { for my $j (0:3) { \
22866 $d[$i][$j] = $d[$i][$k] + $d[$k][$j] \
22867 if $d[$i][$k] + $d[$k][$j] < $d[$i][$j] \
22868 } } }",
22869 );
22870 }
22871
22872 #[test]
22875 fn dp_array_repeat_init_parses() {
22876 let _g = NoInteropGuard::on();
22877 parse_ok("my $amount = 11; my $INF = 1e18; my @dp = ($INF) x ($amount + 1); $dp[0] = 0");
22878 }
22879
22880 #[test]
22882 fn rev_split_join_chain_parses() {
22883 let _g = NoInteropGuard::on();
22884 parse_ok("my $s = \"abc\"; my $r = join(\"\", rev split //, $s)");
22885 }
22886
22887 #[test]
22890 fn cstyle_for_decrement_with_arrayref_swap_parses() {
22891 let _g = NoInteropGuard::on();
22892 parse_ok(
22893 "my @arr = (1, 2, 3); my $r = \\@arr; \
22894 for (my $end = len(@arr) - 1; $end > 0; $end--) { \
22895 ($r->[0], $r->[$end]) = ($r->[$end], $r->[0]) \
22896 }",
22897 );
22898 }
22899
22900 #[test]
22903 fn shift_in_while_loop_parses() {
22904 let _g = NoInteropGuard::on();
22905 parse_ok(
22906 "my @q = (0, 1, 2); my @out; \
22907 while (len(@q) > 0) { my $u = shift @q; push @out, $u }",
22908 );
22909 }
22910
22911 #[test]
22914 fn mod_pow_squaring_loop_parses() {
22915 let _g = NoInteropGuard::on();
22916 parse_ok(
22917 "fn MR::mod_pow($base, $exp, $m) { \
22918 my $result = 1; my $bs = $base % $m; my $e = $exp; \
22919 while ($e > 0) { \
22920 $result = ($result * $bs) % $m if $e % 2 == 1; \
22921 $e = int($e / 2); \
22922 $bs = ($bs * $bs) % $m \
22923 } \
22924 $result \
22925 }",
22926 );
22927 }
22928
22929 #[test]
22932 fn dp_traceback_walk_parses() {
22933 let _g = NoInteropGuard::on();
22934 parse_ok(
22935 "my @xs = (1, 2, 3); my $n = 3; my $target = 4; \
22936 my @dp; for my $i (0:$n) { $dp[$i] = [(0) x ($target + 1)] } \
22937 my @out; my $j = $target; \
22938 for (my $i = $n; $i > 0; $i--) { \
22939 my $v = $xs[$i - 1]; \
22940 if ($j >= $v && $dp[$i - 1][$j - $v] == 1) { \
22941 unshift @out, $v; $j -= $v \
22942 } \
22943 }",
22944 );
22945 }
22946
22947 #[test]
22950 fn binary_search_in_for_loop_parses() {
22951 let _g = NoInteropGuard::on();
22952 parse_ok(
22953 "my @xs = (3, 1, 4, 1, 5); my @tails; \
22954 for my $x (@xs) { \
22955 my $lo = 0; my $hi = len @tails; \
22956 while ($lo < $hi) { \
22957 my $mid = int(($lo + $hi) / 2); \
22958 if ($tails[$mid] < $x) { $lo = $mid + 1 } else { $hi = $mid } \
22959 } \
22960 if ($lo == len @tails) { push @tails, $x } else { $tails[$lo] = $x } \
22961 }",
22962 );
22963 }
22964
22965 #[test]
22968 fn edge_relaxation_destructure_parses() {
22969 let _g = NoInteropGuard::on();
22970 parse_ok(
22971 "my @edges = ([0, 1, 5], [1, 2, -3]); \
22972 my @dist = (0, 1e18, 1e18); my $INF = 1e18; \
22973 for my $e (@edges) { \
22974 my ($u, $w, $cost) = @$e; \
22975 next if $dist[$u] >= $INF; \
22976 $dist[$w] = $dist[$u] + $cost if $dist[$u] + $cost < $dist[$w] \
22977 }",
22978 );
22979 }
22980
22981 #[test]
22984 fn bitwise_and_with_negation_parses() {
22985 let _g = NoInteropGuard::on();
22986 parse_ok("fn Fenwick::lsb($x) = $x & -$x");
22987 parse_ok("my $k = 12; my $lo_bit = $k & -$k");
22988 }
22989
22990 #[test]
22993 fn counting_sort_cumulative_loop_parses() {
22994 let _g = NoInteropGuard::on();
22995 parse_ok(
22996 "my @count = (3, 1, 2, 4); my $running = 0; \
22997 for my $v (0:3) { \
22998 my $c = $count[$v]; \
22999 $count[$v] = $running; \
23000 $running += $c \
23001 }",
23002 );
23003 }
23004
23005 #[test]
23009 fn recursive_tree_walk_with_hashref_parses() {
23010 let _g = NoInteropGuard::on();
23011 parse_ok(
23012 "fn Huff::walk($node, $prefix, $codes) { \
23013 return unless defined $node; \
23014 if (exists $node->{sym}) { \
23015 $codes->{$node->{sym}} = $prefix eq \"\" ? \"0\" : $prefix; \
23016 return \
23017 } \
23018 Huff::walk($node->{left}, $prefix . \"0\", $codes); \
23019 Huff::walk($node->{right}, $prefix . \"1\", $codes) \
23020 }",
23021 );
23022 }
23023
23024 #[test]
23027 fn z_array_window_arithmetic_parses() {
23028 let _g = NoInteropGuard::on();
23029 parse_ok(
23030 "my @c = (\"a\", \"b\", \"a\"); my $n = 3; \
23031 my @z = (0) x $n; $z[0] = $n; \
23032 my $l = 0; my $r = 0; my $i = 1; \
23033 if ($i < $r) { \
23034 my $inside = $r - $i < $z[$i - $l] ? $r - $i : $z[$i - $l]; \
23035 $z[$i] = $inside \
23036 } \
23037 while ($i + $z[$i] < $n && $c[$z[$i]] eq $c[$i + $z[$i]]) { $z[$i]++ }",
23038 );
23039 }
23040
23041 #[test]
23044 fn bfs_with_parent_link_node_parses() {
23045 let _g = NoInteropGuard::on();
23046 parse_ok(
23047 "my @open = (+{ r => 0, c => 0, g => 0, parent => undef }); \
23048 while (len(@open) > 0) { \
23049 my $cur = shift @open; \
23050 for my $d ([-1, 0], [1, 0], [0, -1], [0, 1]) { \
23051 my ($dr, $dc) = @$d; \
23052 push @open, +{ \
23053 r => $cur->{r} + $dr, c => $cur->{c} + $dc, \
23054 g => $cur->{g} + 1, parent => $cur \
23055 } \
23056 } \
23057 last \
23058 }",
23059 );
23060 }
23061
23062 #[test]
23065 fn cross_product_sort_comparator_parses() {
23066 let _g = NoInteropGuard::on();
23067 parse_ok(
23068 "fn Hull::cross($p1, $p2, $p3) = \
23069 ($p2->[0] - $p1->[0]) * ($p3->[1] - $p1->[1]) - \
23070 ($p2->[1] - $p1->[1]) * ($p3->[0] - $p1->[0]); \
23071 my $pivot = [0, 0]; my @pts = ([1, 1], [2, 0]); \
23072 my @sorted = sort { \
23073 my $c = Hull::cross($pivot, _0, _1); \
23074 $c == 0 ? 0 : ($c < 0 ? 1 : -1) \
23075 } @pts",
23076 );
23077 }
23078
23079 #[test]
23082 fn lomuto_partition_loop_parses() {
23083 let _g = NoInteropGuard::on();
23084 parse_ok(
23085 "fn QS::partition($arr, $lo, $hi) { \
23086 my $pivot = $arr->[$hi]; \
23087 my $i = $lo - 1; \
23088 for my $j ($lo:$hi - 1) { \
23089 if ($arr->[$j] <= $pivot) { \
23090 $i++; \
23091 ($arr->[$i], $arr->[$j]) = ($arr->[$j], $arr->[$i]) \
23092 } \
23093 } \
23094 ($arr->[$i + 1], $arr->[$hi]) = ($arr->[$hi], $arr->[$i + 1]); \
23095 $i + 1 \
23096 }",
23097 );
23098 }
23099
23100 #[test]
23103 fn tortoise_hare_diff_loop_parses() {
23104 let _g = NoInteropGuard::on();
23105 parse_ok(
23106 "my $x = 2; my $y = 2; my $n = 35; my $d = 1; \
23107 while ($d == 1) { \
23108 $x = ($x * $x + 1) % $n; \
23109 $y = ($y * $y + 1) % $n; \
23110 $y = ($y * $y + 1) % $n; \
23111 my $diff = $x > $y ? $x - $y : $y - $x; \
23112 $d = $diff \
23113 }",
23114 );
23115 }
23116
23117 #[test]
23120 fn ext_gcd_recursive_destructure_parses() {
23121 let _g = NoInteropGuard::on();
23122 parse_ok(
23123 "fn Mod::ext_gcd($va, $vb) { \
23124 return [$va, 1, 0] if $vb == 0; \
23125 my $r = Mod::ext_gcd($vb, $va % $vb); \
23126 my ($g, $x1, $y1) = @$r; \
23127 [$g, $y1, $x1 - int($va / $vb) * $y1] \
23128 }",
23129 );
23130 }
23131
23132 #[test]
23135 fn convolution_recurrence_dp_parses() {
23136 let _g = NoInteropGuard::on();
23137 parse_ok(
23138 "my @c = (1); my $n = 5; \
23139 for my $i (1:$n) { \
23140 my $sum = 0; \
23141 for my $j (0:$i - 1) { $sum += $c[$j] * $c[$i - 1 - $j] } \
23142 push @c, $sum \
23143 }",
23144 );
23145 }
23146
23147 #[test]
23152 fn tarjan_scc_shared_state_parses() {
23153 let _g = NoInteropGuard::on();
23154 parse_ok(
23155 "fn SCC::strong_connect($s, $v) { \
23156 $s->{idx_of}{$v} = $s->{index}; \
23157 $s->{low_of}{$v} = $s->{index}; \
23158 $s->{index}++; \
23159 push @{$s->{stack}}, $v; \
23160 $s->{on_stack}{$v} = 1; \
23161 for my $w (@{$s->{adj}{$v}}) { \
23162 if (!exists $s->{idx_of}{$w}) { \
23163 SCC::strong_connect($s, $w); \
23164 $s->{low_of}{$v} = $s->{low_of}{$w} if $s->{low_of}{$w} < $s->{low_of}{$v} \
23165 } \
23166 } \
23167 }",
23168 );
23169 }
23170
23171 #[test]
23174 fn heaps_algorithm_recursive_parses() {
23175 let _g = NoInteropGuard::on();
23176 parse_ok(
23177 "fn Perm::heaps_inner($arr, $n, $out) { \
23178 if ($n == 1) { my @snap = @$arr; push @$out, \\@snap; return } \
23179 for my $i (0:$n - 1) { \
23180 Perm::heaps_inner($arr, $n - 1, $out); \
23181 if ($n % 2 == 0) { \
23182 ($arr->[$i], $arr->[$n - 1]) = ($arr->[$n - 1], $arr->[$i]) \
23183 } else { \
23184 ($arr->[0], $arr->[$n - 1]) = ($arr->[$n - 1], $arr->[0]) \
23185 } \
23186 } \
23187 }",
23188 );
23189 }
23190
23191 #[test]
23199 fn format_as_hash_key_parses() {
23200 parse_ok("my %opts; $opts{format} = \"csv\"");
23201 parse_ok("my %opts = (format => \"csv\", level => 9)");
23202 parse_ok("my $h = +{ format => \"csv\" }");
23203 parse_ok("my @keys = ($h->{format}, $h->{level})");
23204 }
23205
23206 #[test]
23208 fn format_as_method_call_parses() {
23209 parse_ok("class Foo { val: Str; fn format($self) { \"x\" } } my $f = Foo(val => \"y\"); my $s = $f->format()");
23210 }
23211
23212 #[test]
23214 fn format_as_namespaced_tail_parses() {
23215 parse_ok("fn Foo::format($x) = $x . \"!\"");
23216 parse_ok("fn Foo::format($x) = $x . \"!\"; my $r = Foo::format(\"hi\")");
23217 }
23218
23219 #[test]
23226 fn arrow_hash_compound_assign_parses_all_ops() {
23227 let _g = NoInteropGuard::on();
23228 parse_ok("my $h = +{n=>10}; $h->{n} -= 1");
23229 parse_ok("my $h = +{n=>10}; $h->{n} += 1");
23230 parse_ok("my $h = +{n=>10}; $h->{n} *= 2");
23231 parse_ok("my $h = +{n=>10}; $h->{n} /= 2");
23232 parse_ok("my $h = +{n=>10}; $h->{n} %= 3");
23233 parse_ok("my $h = +{n=>\"x\"}; $h->{n} .= \"y\"");
23234 }
23235
23236 #[test]
23240 fn arrow_hash_compound_assign_value_chains_parses() {
23241 let _g = NoInteropGuard::on();
23242 parse_ok("my $h = +{n=>10}; my $v = $h->{n} -= 1");
23243 parse_ok("my $h = +{n=>10}; my @list = ($h->{n} += 5, $h->{n} += 5)");
23244 parse_ok("my $h = +{n=>10}; my $double = ($h->{n} -= 1) * 2");
23245 }
23246}
23247
23248fn zsh_param_form(body: &str) -> bool {
23258 let b = body.as_bytes();
23259 if b.is_empty() || b[0] == b'$' || b[0] == b'\\' {
23260 return false;
23261 }
23262 if b[0] == b'(' {
23263 if let Some(rp) = body.find(')') {
23265 return body[rp + 1..]
23266 .bytes()
23267 .next()
23268 .map_or(false, |c| c.is_ascii_alphanumeric() || c == b'_' || c == b'$');
23269 }
23270 return false;
23271 }
23272 if b[0] == b'#' {
23273 return body[1..]
23275 .bytes()
23276 .next()
23277 .map_or(false, |c| c.is_ascii_alphanumeric() || c == b'_');
23278 }
23279 let mut i = 0;
23280 while i < b.len() && (b[i].is_ascii_alphanumeric() || b[i] == b'_') {
23281 i += 1;
23282 }
23283 if i == 0 {
23284 return false;
23285 }
23286 match b.get(i) {
23287 Some(b':') => b.get(i + 1) != Some(&b':'),
23290 Some(b'#') | Some(b'%') | Some(b'/') | Some(b'[') => true,
23292 _ => false,
23293 }
23294}
23295
23296fn build_zpexpand_expr(body: &str, line: usize) -> Expr {
23300 let mut args = vec![Expr {
23301 kind: ExprKind::String(format!("${{{}}}", body)),
23302 line,
23303 }];
23304 let arrays = subscripted_names(body);
23307 for name in zsh_referenced_params(body) {
23308 args.push(Expr {
23309 kind: ExprKind::String(name.clone()),
23310 line,
23311 });
23312 let val = if arrays.contains(&name) {
23316 ExprKind::ScalarRef(Box::new(Expr {
23317 kind: ExprKind::ArrayVar(name),
23318 line,
23319 }))
23320 } else {
23321 ExprKind::ScalarVar(name)
23322 };
23323 args.push(Expr { kind: val, line });
23324 }
23325 Expr {
23326 kind: ExprKind::FuncCall {
23327 name: "zpexpand".to_string(),
23328 args,
23329 },
23330 line,
23331 }
23332}
23333
23334fn subscripted_names(body: &str) -> Vec<String> {
23337 let b = body.as_bytes();
23338 let mut out = Vec::new();
23339 let mut i = 0;
23340 while i < b.len() {
23341 if b[i].is_ascii_alphabetic() || b[i] == b'_' {
23342 let s = i;
23343 while i < b.len() && (b[i].is_ascii_alphanumeric() || b[i] == b'_') {
23344 i += 1;
23345 }
23346 if b.get(i) == Some(&b'[') {
23347 out.push(body[s..i].to_string());
23348 }
23349 } else {
23350 i += 1;
23351 }
23352 }
23353 out
23354}
23355
23356fn zsh_referenced_params(inner: &str) -> Vec<String> {
23359 let b = inner.as_bytes();
23360 let mut out = Vec::new();
23361 let mut i = 0;
23362 if b.first() == Some(&b'(') {
23363 if let Some(rp) = inner.find(')') {
23364 i = rp + 1;
23365 }
23366 }
23367 while i < b.len() && b[i] == b'#' {
23368 i += 1;
23369 }
23370 let s = i;
23371 while i < b.len() && (b[i].is_ascii_alphanumeric() || b[i] == b'_') {
23372 i += 1;
23373 }
23374 if i > s {
23375 out.push(inner[s..i].to_string());
23376 }
23377 while i < b.len() {
23378 if b[i] == b'$' && i + 1 < b.len() && (b[i + 1].is_ascii_alphabetic() || b[i + 1] == b'_') {
23379 let ds = i + 1;
23380 let mut de = ds;
23381 while de < b.len() && (b[de].is_ascii_alphanumeric() || b[de] == b'_') {
23382 de += 1;
23383 }
23384 out.push(inner[ds..de].to_string());
23385 i = de;
23386 } else {
23387 i += 1;
23388 }
23389 }
23390 out.sort();
23391 out.dedup();
23392 out
23393}