1use serde::{Deserialize, Serialize};
5
6fn default_delim() -> char {
7 '/'
8}
9
10#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct Program {
12 pub statements: Vec<Statement>,
13}
14
15#[derive(Debug, Clone, Serialize, Deserialize)]
16pub struct Statement {
17 pub label: Option<String>,
19 pub kind: StmtKind,
20 pub line: usize,
21}
22
23impl Statement {
24 pub fn new(kind: StmtKind, line: usize) -> Self {
25 Self {
26 label: None,
27 kind,
28 line,
29 }
30 }
31}
32
33#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
36#[serde(rename_all = "snake_case")]
37#[derive(Default)]
38pub enum GrepBuiltinKeyword {
39 #[default]
40 Grep,
41 Greps,
42 Filter,
43 FindAll,
44}
45
46impl GrepBuiltinKeyword {
47 pub const fn as_str(self) -> &'static str {
48 match self {
49 Self::Grep => "grep",
50 Self::Greps => "greps",
51 Self::Filter => "filter",
52 Self::FindAll => "find_all",
53 }
54 }
55
56 pub const fn is_stream(self) -> bool {
58 !matches!(self, Self::Grep)
59 }
60}
61
62#[derive(Debug, Clone, Serialize, Deserialize)]
64pub enum SubSigParam {
65 Scalar(String, Option<PerlTypeName>, Option<Box<Expr>>),
68 Array(String, Option<Box<Expr>>),
70 Hash(String, Option<Box<Expr>>),
72 ArrayDestruct(Vec<MatchArrayElem>),
74 HashDestruct(Vec<(String, String)>),
76}
77
78#[derive(Debug, Clone, Serialize, Deserialize)]
79pub enum StmtKind {
80 Expression(Expr),
81 If {
82 condition: Expr,
83 body: Block,
84 elsifs: Vec<(Expr, Block)>,
85 else_block: Option<Block>,
86 },
87 Unless {
88 condition: Expr,
89 body: Block,
90 else_block: Option<Block>,
91 },
92 While {
93 condition: Expr,
94 body: Block,
95 label: Option<String>,
96 continue_block: Option<Block>,
98 },
99 Until {
100 condition: Expr,
101 body: Block,
102 label: Option<String>,
103 continue_block: Option<Block>,
104 },
105 DoWhile {
106 body: Block,
107 condition: Expr,
108 },
109 For {
110 init: Option<Box<Statement>>,
111 condition: Option<Expr>,
112 step: Option<Expr>,
113 body: Block,
114 label: Option<String>,
115 continue_block: Option<Block>,
116 },
117 Foreach {
118 var: String,
119 list: Expr,
120 body: Block,
121 label: Option<String>,
122 continue_block: Option<Block>,
123 },
124 SubDecl {
125 name: String,
126 params: Vec<SubSigParam>,
127 body: Block,
128 prototype: Option<String>,
131 },
132 Package {
133 name: String,
134 },
135 Use {
136 module: String,
137 imports: Vec<Expr>,
138 },
139 UsePerlVersion {
141 version: f64,
142 },
143 UseOverload {
145 pairs: Vec<(String, String)>,
146 },
147 No {
148 module: String,
149 imports: Vec<Expr>,
150 },
151 Return(Option<Expr>),
152 Last(Option<String>),
153 Next(Option<String>),
154 Redo(Option<String>),
155 My(Vec<VarDecl>),
156 Our(Vec<VarDecl>),
157 Local(Vec<VarDecl>),
158 State(Vec<VarDecl>),
160 LocalExpr {
162 target: Expr,
163 initializer: Option<Expr>,
164 },
165 MySync(Vec<VarDecl>),
167 Block(Block),
169 StmtGroup(Block),
171 Begin(Block),
173 End(Block),
175 UnitCheck(Block),
177 Check(Block),
179 Init(Block),
181 Empty,
183 Goto {
185 target: Box<Expr>,
186 },
187 Continue(Block),
189 StructDecl {
191 def: StructDef,
192 },
193 EnumDecl {
195 def: EnumDef,
196 },
197 ClassDecl {
199 def: ClassDef,
200 },
201 TraitDecl {
203 def: TraitDef,
204 },
205 EvalTimeout {
207 timeout: Expr,
208 body: Block,
209 },
210 TryCatch {
213 try_block: Block,
214 catch_var: String,
215 catch_block: Block,
216 finally_block: Option<Block>,
217 },
218 Given {
220 topic: Expr,
221 body: Block,
222 },
223 When {
225 cond: Expr,
226 body: Block,
227 },
228 DefaultCase {
230 body: Block,
231 },
232 Tie {
234 target: TieTarget,
235 class: Expr,
236 args: Vec<Expr>,
237 },
238 FormatDecl {
240 name: String,
241 lines: Vec<String>,
242 },
243 AdviceDecl {
246 kind: AdviceKind,
247 pattern: String,
248 body: Block,
249 },
250}
251
252#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
254pub enum AdviceKind {
255 Before,
257 After,
259 Around,
261}
262
263#[derive(Debug, Clone, Serialize, Deserialize)]
265pub enum TieTarget {
266 Hash(String),
267 Array(String),
268 Scalar(String),
269}
270
271#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
273pub enum PerlTypeName {
274 Int,
275 Str,
276 Float,
277 Bool,
278 Array,
279 Hash,
280 Ref,
281 Struct(String),
283 Enum(String),
285 Any,
287}
288
289#[derive(Debug, Clone, Serialize, Deserialize)]
291pub struct StructField {
292 pub name: String,
293 pub ty: PerlTypeName,
294 #[serde(skip_serializing_if = "Option::is_none")]
296 pub default: Option<Expr>,
297}
298
299#[derive(Debug, Clone, Serialize, Deserialize)]
301pub struct StructMethod {
302 pub name: String,
303 pub params: Vec<SubSigParam>,
304 pub body: Block,
305}
306
307#[derive(Debug, Clone, Serialize, Deserialize)]
309pub struct EnumVariant {
310 pub name: String,
311 pub ty: Option<PerlTypeName>,
313}
314
315#[derive(Debug, Clone, Serialize, Deserialize)]
317pub struct EnumDef {
318 pub name: String,
319 pub variants: Vec<EnumVariant>,
320}
321
322impl EnumDef {
323 #[inline]
324 pub fn variant_index(&self, name: &str) -> Option<usize> {
325 self.variants.iter().position(|v| v.name == name)
326 }
327
328 #[inline]
329 pub fn variant(&self, name: &str) -> Option<&EnumVariant> {
330 self.variants.iter().find(|v| v.name == name)
331 }
332}
333
334#[derive(Debug, Clone, Serialize, Deserialize)]
336pub struct StructDef {
337 pub name: String,
338 pub fields: Vec<StructField>,
339 #[serde(default, skip_serializing_if = "Vec::is_empty")]
341 pub methods: Vec<StructMethod>,
342}
343
344#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
346pub enum Visibility {
347 #[default]
348 Public,
349 Private,
350 Protected,
351}
352
353#[derive(Debug, Clone, Serialize, Deserialize)]
355pub struct ClassField {
356 pub name: String,
357 pub ty: PerlTypeName,
358 pub visibility: Visibility,
359 #[serde(skip_serializing_if = "Option::is_none")]
360 pub default: Option<Expr>,
361}
362
363#[derive(Debug, Clone, Serialize, Deserialize)]
365pub struct ClassMethod {
366 pub name: String,
367 pub params: Vec<SubSigParam>,
368 pub body: Option<Block>,
369 pub visibility: Visibility,
370 pub is_static: bool,
371 #[serde(default, skip_serializing_if = "is_false")]
372 pub is_final: bool,
373}
374
375#[derive(Debug, Clone, Serialize, Deserialize)]
377pub struct TraitDef {
378 pub name: String,
379 pub methods: Vec<ClassMethod>,
380}
381
382impl TraitDef {
383 #[inline]
384 pub fn method(&self, name: &str) -> Option<&ClassMethod> {
385 self.methods.iter().find(|m| m.name == name)
386 }
387
388 #[inline]
389 pub fn required_methods(&self) -> impl Iterator<Item = &ClassMethod> {
390 self.methods.iter().filter(|m| m.body.is_none())
391 }
392}
393
394#[derive(Debug, Clone, Serialize, Deserialize)]
396pub struct ClassStaticField {
397 pub name: String,
398 pub ty: PerlTypeName,
399 pub visibility: Visibility,
400 #[serde(skip_serializing_if = "Option::is_none")]
401 pub default: Option<Expr>,
402}
403
404#[derive(Debug, Clone, Serialize, Deserialize)]
406pub struct ClassDef {
407 pub name: String,
408 #[serde(default, skip_serializing_if = "is_false")]
409 pub is_abstract: bool,
410 #[serde(default, skip_serializing_if = "is_false")]
411 pub is_final: bool,
412 #[serde(default, skip_serializing_if = "Vec::is_empty")]
413 pub extends: Vec<String>,
414 #[serde(default, skip_serializing_if = "Vec::is_empty")]
415 pub implements: Vec<String>,
416 pub fields: Vec<ClassField>,
417 pub methods: Vec<ClassMethod>,
418 #[serde(default, skip_serializing_if = "Vec::is_empty")]
419 pub static_fields: Vec<ClassStaticField>,
420}
421
422fn is_false(v: &bool) -> bool {
423 !*v
424}
425
426impl ClassDef {
427 #[inline]
428 pub fn field_index(&self, name: &str) -> Option<usize> {
429 self.fields.iter().position(|f| f.name == name)
430 }
431
432 #[inline]
433 pub fn field(&self, name: &str) -> Option<&ClassField> {
434 self.fields.iter().find(|f| f.name == name)
435 }
436
437 #[inline]
438 pub fn method(&self, name: &str) -> Option<&ClassMethod> {
439 self.methods.iter().find(|m| m.name == name)
440 }
441
442 #[inline]
443 pub fn static_methods(&self) -> impl Iterator<Item = &ClassMethod> {
444 self.methods.iter().filter(|m| m.is_static)
445 }
446
447 #[inline]
448 pub fn instance_methods(&self) -> impl Iterator<Item = &ClassMethod> {
449 self.methods.iter().filter(|m| !m.is_static)
450 }
451}
452
453impl StructDef {
454 #[inline]
455 pub fn field_index(&self, name: &str) -> Option<usize> {
456 self.fields.iter().position(|f| f.name == name)
457 }
458
459 #[inline]
461 pub fn field_type(&self, name: &str) -> Option<&PerlTypeName> {
462 self.fields.iter().find(|f| f.name == name).map(|f| &f.ty)
463 }
464
465 #[inline]
467 pub fn method(&self, name: &str) -> Option<&StructMethod> {
468 self.methods.iter().find(|m| m.name == name)
469 }
470}
471
472impl PerlTypeName {
473 #[inline]
475 pub fn from_byte(b: u8) -> Option<Self> {
476 match b {
477 0 => Some(Self::Int),
478 1 => Some(Self::Str),
479 2 => Some(Self::Float),
480 3 => Some(Self::Bool),
481 4 => Some(Self::Array),
482 5 => Some(Self::Hash),
483 6 => Some(Self::Ref),
484 7 => Some(Self::Any),
485 _ => None,
486 }
487 }
488
489 #[inline]
491 pub fn as_byte(&self) -> Option<u8> {
492 match self {
493 Self::Int => Some(0),
494 Self::Str => Some(1),
495 Self::Float => Some(2),
496 Self::Bool => Some(3),
497 Self::Array => Some(4),
498 Self::Hash => Some(5),
499 Self::Ref => Some(6),
500 Self::Any => Some(7),
501 Self::Struct(_) | Self::Enum(_) => None,
502 }
503 }
504
505 pub fn display_name(&self) -> String {
507 match self {
508 Self::Int => "Int".to_string(),
509 Self::Str => "Str".to_string(),
510 Self::Float => "Float".to_string(),
511 Self::Bool => "Bool".to_string(),
512 Self::Array => "Array".to_string(),
513 Self::Hash => "Hash".to_string(),
514 Self::Ref => "Ref".to_string(),
515 Self::Any => "Any".to_string(),
516 Self::Struct(name) => name.clone(),
517 Self::Enum(name) => name.clone(),
518 }
519 }
520
521 pub fn check_value(&self, v: &crate::value::PerlValue) -> Result<(), String> {
523 match self {
524 Self::Int => {
525 if v.is_integer_like() {
526 Ok(())
527 } else {
528 Err(format!("expected Int (INTEGER), got {}", v.type_name()))
529 }
530 }
531 Self::Str => {
532 if v.is_string_like() {
533 Ok(())
534 } else {
535 Err(format!("expected Str (STRING), got {}", v.type_name()))
536 }
537 }
538 Self::Float => {
539 if v.is_integer_like() || v.is_float_like() {
540 Ok(())
541 } else {
542 Err(format!(
543 "expected Float (INTEGER or FLOAT), got {}",
544 v.type_name()
545 ))
546 }
547 }
548 Self::Bool => Ok(()),
549 Self::Array => {
550 if v.as_array_vec().is_some() || v.as_array_ref().is_some() {
551 Ok(())
552 } else {
553 Err(format!("expected Array, got {}", v.type_name()))
554 }
555 }
556 Self::Hash => {
557 if v.as_hash_map().is_some() || v.as_hash_ref().is_some() {
558 Ok(())
559 } else {
560 Err(format!("expected Hash, got {}", v.type_name()))
561 }
562 }
563 Self::Ref => {
564 if v.as_scalar_ref().is_some()
565 || v.as_array_ref().is_some()
566 || v.as_hash_ref().is_some()
567 || v.as_code_ref().is_some()
568 {
569 Ok(())
570 } else {
571 Err(format!("expected Ref, got {}", v.type_name()))
572 }
573 }
574 Self::Struct(name) => {
575 if v.is_undef() {
577 return Ok(());
578 }
579 if let Some(s) = v.as_struct_inst() {
580 if s.def.name == *name {
581 Ok(())
582 } else {
583 Err(format!(
584 "expected struct {}, got struct {}",
585 name, s.def.name
586 ))
587 }
588 } else if let Some(e) = v.as_enum_inst() {
589 if e.def.name == *name {
590 Ok(())
591 } else {
592 Err(format!("expected {}, got enum {}", name, e.def.name))
593 }
594 } else if let Some(c) = v.as_class_inst() {
595 if c.isa(name) {
597 Ok(())
598 } else {
599 Err(format!("expected {}, got {}", name, c.def.name))
600 }
601 } else {
602 Err(format!("expected {}, got {}", name, v.type_name()))
603 }
604 }
605 Self::Enum(name) => {
606 if v.is_undef() {
608 return Ok(());
609 }
610 if let Some(e) = v.as_enum_inst() {
611 if e.def.name == *name {
612 Ok(())
613 } else {
614 Err(format!("expected enum {}, got enum {}", name, e.def.name))
615 }
616 } else {
617 Err(format!("expected enum {}, got {}", name, v.type_name()))
618 }
619 }
620 Self::Any => Ok(()),
621 }
622 }
623}
624
625#[derive(Debug, Clone, Serialize, Deserialize)]
626pub struct VarDecl {
627 pub sigil: Sigil,
628 pub name: String,
629 pub initializer: Option<Expr>,
630 pub frozen: bool,
632 pub type_annotation: Option<PerlTypeName>,
634}
635
636#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
637pub enum Sigil {
638 Scalar,
639 Array,
640 Hash,
641 Typeglob,
643}
644
645pub type Block = Vec<Statement>;
646
647#[derive(Debug, Clone, Serialize, Deserialize)]
649pub enum SortComparator {
650 Block(Block),
651 Code(Box<Expr>),
652}
653
654#[derive(Debug, Clone, Serialize, Deserialize)]
658pub struct MatchArm {
659 pub pattern: MatchPattern,
660 #[serde(skip_serializing_if = "Option::is_none")]
662 pub guard: Option<Box<Expr>>,
663 pub body: Expr,
664}
665
666#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
668pub enum RetryBackoff {
669 None,
671 Linear,
673 Exponential,
675}
676
677#[derive(Debug, Clone, Serialize, Deserialize)]
679pub enum MatchPattern {
680 Any,
682 Regex { pattern: String, flags: String },
685 Value(Box<Expr>),
687 Array(Vec<MatchArrayElem>),
689 Hash(Vec<MatchHashPair>),
691 OptionSome(String),
694}
695
696#[derive(Debug, Clone, Serialize, Deserialize)]
697pub enum MatchArrayElem {
698 Expr(Expr),
699 CaptureScalar(String),
702 Rest,
704 RestBind(String),
706}
707
708#[derive(Debug, Clone, Serialize, Deserialize)]
709pub enum MatchHashPair {
710 KeyOnly { key: Expr },
712 Capture { key: Expr, name: String },
714}
715
716#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
717pub enum MagicConstKind {
718 File,
720 Line,
722 Sub,
724}
725
726#[derive(Debug, Clone, Serialize, Deserialize)]
727pub struct Expr {
728 pub kind: ExprKind,
729 pub line: usize,
730}
731
732#[derive(Debug, Clone, Serialize, Deserialize)]
733pub enum ExprKind {
734 Integer(i64),
736 Float(f64),
737 String(String),
738 Bareword(String),
741 Regex(String, String),
742 QW(Vec<String>),
743 Undef,
744 MagicConst(MagicConstKind),
746
747 InterpolatedString(Vec<StringPart>),
749
750 ScalarVar(String),
752 ArrayVar(String),
753 HashVar(String),
754 ArrayElement {
755 array: String,
756 index: Box<Expr>,
757 },
758 HashElement {
759 hash: String,
760 key: Box<Expr>,
761 },
762 ArraySlice {
763 array: String,
764 indices: Vec<Expr>,
765 },
766 HashSlice {
767 hash: String,
768 keys: Vec<Expr>,
769 },
770 HashKvSlice {
773 hash: String,
774 keys: Vec<Expr>,
775 },
776 HashSliceDeref {
778 container: Box<Expr>,
779 keys: Vec<Expr>,
780 },
781 AnonymousListSlice {
783 source: Box<Expr>,
784 indices: Vec<Expr>,
785 },
786
787 ScalarRef(Box<Expr>),
789 ArrayRef(Vec<Expr>),
790 HashRef(Vec<(Expr, Expr)>),
791 CodeRef {
792 params: Vec<SubSigParam>,
793 body: Block,
794 },
795 SubroutineRef(String),
797 SubroutineCodeRef(String),
799 DynamicSubCodeRef(Box<Expr>),
801 Deref {
802 expr: Box<Expr>,
803 kind: Sigil,
804 },
805 ArrowDeref {
806 expr: Box<Expr>,
807 index: Box<Expr>,
808 kind: DerefKind,
809 },
810
811 BinOp {
813 left: Box<Expr>,
814 op: BinOp,
815 right: Box<Expr>,
816 },
817 UnaryOp {
818 op: UnaryOp,
819 expr: Box<Expr>,
820 },
821 PostfixOp {
822 expr: Box<Expr>,
823 op: PostfixOp,
824 },
825 Assign {
826 target: Box<Expr>,
827 value: Box<Expr>,
828 },
829 CompoundAssign {
830 target: Box<Expr>,
831 op: BinOp,
832 value: Box<Expr>,
833 },
834 Ternary {
835 condition: Box<Expr>,
836 then_expr: Box<Expr>,
837 else_expr: Box<Expr>,
838 },
839
840 Repeat {
849 expr: Box<Expr>,
850 count: Box<Expr>,
851 list_repeat: bool,
852 },
853
854 Range {
857 from: Box<Expr>,
858 to: Box<Expr>,
859 #[serde(default)]
860 exclusive: bool,
861 #[serde(default)]
862 step: Option<Box<Expr>>,
863 },
864
865 SliceRange {
870 #[serde(default)]
871 from: Option<Box<Expr>>,
872 #[serde(default)]
873 to: Option<Box<Expr>>,
874 #[serde(default)]
875 step: Option<Box<Expr>>,
876 },
877
878 MyExpr {
884 keyword: String, decls: Vec<VarDecl>,
886 },
887
888 FuncCall {
890 name: String,
891 args: Vec<Expr>,
892 },
893
894 MethodCall {
896 object: Box<Expr>,
897 method: String,
898 args: Vec<Expr>,
899 #[serde(default)]
901 super_call: bool,
902 },
903 IndirectCall {
906 target: Box<Expr>,
907 args: Vec<Expr>,
908 #[serde(default)]
909 ampersand: bool,
910 #[serde(default)]
912 pass_caller_arglist: bool,
913 },
914 Typeglob(String),
916 TypeglobExpr(Box<Expr>),
918
919 Print {
921 handle: Option<String>,
922 args: Vec<Expr>,
923 },
924 Say {
925 handle: Option<String>,
926 args: Vec<Expr>,
927 },
928 Printf {
929 handle: Option<String>,
930 args: Vec<Expr>,
931 },
932 Die(Vec<Expr>),
933 Warn(Vec<Expr>),
934
935 Match {
937 expr: Box<Expr>,
938 pattern: String,
939 flags: String,
940 scalar_g: bool,
942 #[serde(default = "default_delim")]
943 delim: char,
944 },
945 Substitution {
946 expr: Box<Expr>,
947 pattern: String,
948 replacement: String,
949 flags: String,
950 #[serde(default = "default_delim")]
951 delim: char,
952 },
953 Transliterate {
954 expr: Box<Expr>,
955 from: String,
956 to: String,
957 flags: String,
958 #[serde(default = "default_delim")]
959 delim: char,
960 },
961
962 MapExpr {
964 block: Block,
965 list: Box<Expr>,
966 flatten_array_refs: bool,
968 #[serde(default)]
970 stream: bool,
971 },
972 MapExprComma {
974 expr: Box<Expr>,
975 list: Box<Expr>,
976 flatten_array_refs: bool,
977 #[serde(default)]
978 stream: bool,
979 },
980 GrepExpr {
981 block: Block,
982 list: Box<Expr>,
983 #[serde(default)]
984 keyword: GrepBuiltinKeyword,
985 },
986 GrepExprComma {
988 expr: Box<Expr>,
989 list: Box<Expr>,
990 #[serde(default)]
991 keyword: GrepBuiltinKeyword,
992 },
993 SortExpr {
995 cmp: Option<SortComparator>,
996 list: Box<Expr>,
997 },
998 ReverseExpr(Box<Expr>),
999 Rev(Box<Expr>),
1001 JoinExpr {
1002 separator: Box<Expr>,
1003 list: Box<Expr>,
1004 },
1005 SplitExpr {
1006 pattern: Box<Expr>,
1007 string: Box<Expr>,
1008 limit: Option<Box<Expr>>,
1009 },
1010 ForEachExpr {
1013 block: Block,
1014 list: Box<Expr>,
1015 },
1016
1017 PMapExpr {
1019 block: Block,
1020 list: Box<Expr>,
1021 progress: Option<Box<Expr>>,
1023 flat_outputs: bool,
1026 #[serde(default, skip_serializing_if = "Option::is_none")]
1028 on_cluster: Option<Box<Expr>>,
1029 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
1032 stream: bool,
1033 },
1034 PMapChunkedExpr {
1036 chunk_size: Box<Expr>,
1037 block: Block,
1038 list: Box<Expr>,
1039 progress: Option<Box<Expr>>,
1040 },
1041 PGrepExpr {
1042 block: Block,
1043 list: Box<Expr>,
1044 progress: Option<Box<Expr>>,
1046 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
1048 stream: bool,
1049 },
1050 PForExpr {
1052 block: Block,
1053 list: Box<Expr>,
1054 progress: Option<Box<Expr>>,
1055 },
1056 ParLinesExpr {
1058 path: Box<Expr>,
1059 callback: Box<Expr>,
1060 progress: Option<Box<Expr>>,
1061 },
1062 ParWalkExpr {
1064 path: Box<Expr>,
1065 callback: Box<Expr>,
1066 progress: Option<Box<Expr>>,
1067 },
1068 PwatchExpr {
1070 path: Box<Expr>,
1071 callback: Box<Expr>,
1072 },
1073 PSortExpr {
1075 cmp: Option<Block>,
1076 list: Box<Expr>,
1077 progress: Option<Box<Expr>>,
1078 },
1079 ReduceExpr {
1082 block: Block,
1083 list: Box<Expr>,
1084 },
1085 PReduceExpr {
1088 block: Block,
1089 list: Box<Expr>,
1090 progress: Option<Box<Expr>>,
1092 },
1093 PReduceInitExpr {
1098 init: Box<Expr>,
1099 block: Block,
1100 list: Box<Expr>,
1101 progress: Option<Box<Expr>>,
1102 },
1103 PMapReduceExpr {
1105 map_block: Block,
1106 reduce_block: Block,
1107 list: Box<Expr>,
1108 progress: Option<Box<Expr>>,
1109 },
1110 PcacheExpr {
1112 block: Block,
1113 list: Box<Expr>,
1114 progress: Option<Box<Expr>>,
1115 },
1116 PselectExpr {
1118 receivers: Vec<Expr>,
1119 timeout: Option<Box<Expr>>,
1120 },
1121 FanExpr {
1126 count: Option<Box<Expr>>,
1127 block: Block,
1128 progress: Option<Box<Expr>>,
1129 capture: bool,
1130 },
1131
1132 AsyncBlock {
1134 body: Block,
1135 },
1136 SpawnBlock {
1138 body: Block,
1139 },
1140 Trace {
1142 body: Block,
1143 },
1144 Timer {
1146 body: Block,
1147 },
1148 Bench {
1150 body: Block,
1151 times: Box<Expr>,
1152 },
1153 Spinner {
1155 message: Box<Expr>,
1156 body: Block,
1157 },
1158 Await(Box<Expr>),
1160 Slurp(Box<Expr>),
1162 Capture(Box<Expr>),
1164 Qx(Box<Expr>),
1166 FetchUrl(Box<Expr>),
1168
1169 Pchannel {
1171 capacity: Option<Box<Expr>>,
1172 },
1173
1174 Push {
1176 array: Box<Expr>,
1177 values: Vec<Expr>,
1178 },
1179 Pop(Box<Expr>),
1180 Shift(Box<Expr>),
1181 Unshift {
1182 array: Box<Expr>,
1183 values: Vec<Expr>,
1184 },
1185 Splice {
1186 array: Box<Expr>,
1187 offset: Option<Box<Expr>>,
1188 length: Option<Box<Expr>>,
1189 replacement: Vec<Expr>,
1190 },
1191 Delete(Box<Expr>),
1192 Exists(Box<Expr>),
1193 Keys(Box<Expr>),
1194 Values(Box<Expr>),
1195 Each(Box<Expr>),
1196
1197 Chomp(Box<Expr>),
1199 Chop(Box<Expr>),
1200 Length(Box<Expr>),
1201 Substr {
1202 string: Box<Expr>,
1203 offset: Box<Expr>,
1204 length: Option<Box<Expr>>,
1205 replacement: Option<Box<Expr>>,
1206 },
1207 Index {
1208 string: Box<Expr>,
1209 substr: Box<Expr>,
1210 position: Option<Box<Expr>>,
1211 },
1212 Rindex {
1213 string: Box<Expr>,
1214 substr: Box<Expr>,
1215 position: Option<Box<Expr>>,
1216 },
1217 Sprintf {
1218 format: Box<Expr>,
1219 args: Vec<Expr>,
1220 },
1221
1222 Abs(Box<Expr>),
1224 Int(Box<Expr>),
1225 Sqrt(Box<Expr>),
1226 Sin(Box<Expr>),
1227 Cos(Box<Expr>),
1228 Atan2 {
1229 y: Box<Expr>,
1230 x: Box<Expr>,
1231 },
1232 Exp(Box<Expr>),
1233 Log(Box<Expr>),
1234 Rand(Option<Box<Expr>>),
1236 Srand(Option<Box<Expr>>),
1238 Hex(Box<Expr>),
1239 Oct(Box<Expr>),
1240
1241 Lc(Box<Expr>),
1243 Uc(Box<Expr>),
1244 Lcfirst(Box<Expr>),
1245 Ucfirst(Box<Expr>),
1246
1247 Fc(Box<Expr>),
1249 Crypt {
1251 plaintext: Box<Expr>,
1252 salt: Box<Expr>,
1253 },
1254 Pos(Option<Box<Expr>>),
1256 Study(Box<Expr>),
1258
1259 Defined(Box<Expr>),
1261 Ref(Box<Expr>),
1262 ScalarContext(Box<Expr>),
1263
1264 Chr(Box<Expr>),
1266 Ord(Box<Expr>),
1267
1268 OpenMyHandle {
1271 name: String,
1272 },
1273 Open {
1274 handle: Box<Expr>,
1275 mode: Box<Expr>,
1276 file: Option<Box<Expr>>,
1277 },
1278 Close(Box<Expr>),
1279 ReadLine(Option<String>),
1280 Eof(Option<Box<Expr>>),
1281
1282 Opendir {
1283 handle: Box<Expr>,
1284 path: Box<Expr>,
1285 },
1286 Readdir(Box<Expr>),
1287 Closedir(Box<Expr>),
1288 Rewinddir(Box<Expr>),
1289 Telldir(Box<Expr>),
1290 Seekdir {
1291 handle: Box<Expr>,
1292 position: Box<Expr>,
1293 },
1294
1295 FileTest {
1297 op: char,
1298 expr: Box<Expr>,
1299 },
1300
1301 System(Vec<Expr>),
1303 Exec(Vec<Expr>),
1304 Eval(Box<Expr>),
1305 Do(Box<Expr>),
1306 Require(Box<Expr>),
1307 Exit(Option<Box<Expr>>),
1308 Chdir(Box<Expr>),
1309 Mkdir {
1310 path: Box<Expr>,
1311 mode: Option<Box<Expr>>,
1312 },
1313 Unlink(Vec<Expr>),
1314 Rename {
1315 old: Box<Expr>,
1316 new: Box<Expr>,
1317 },
1318 Chmod(Vec<Expr>),
1320 Chown(Vec<Expr>),
1322
1323 Stat(Box<Expr>),
1324 Lstat(Box<Expr>),
1325 Link {
1326 old: Box<Expr>,
1327 new: Box<Expr>,
1328 },
1329 Symlink {
1330 old: Box<Expr>,
1331 new: Box<Expr>,
1332 },
1333 Readlink(Box<Expr>),
1334 Files(Vec<Expr>),
1336 Filesf(Vec<Expr>),
1338 FilesfRecursive(Vec<Expr>),
1340 Dirs(Vec<Expr>),
1342 DirsRecursive(Vec<Expr>),
1344 SymLinks(Vec<Expr>),
1346 Sockets(Vec<Expr>),
1348 Pipes(Vec<Expr>),
1350 BlockDevices(Vec<Expr>),
1352 CharDevices(Vec<Expr>),
1354 Executables(Vec<Expr>),
1356 Glob(Vec<Expr>),
1357 GlobPar {
1360 args: Vec<Expr>,
1361 progress: Option<Box<Expr>>,
1362 },
1363 ParSed {
1365 args: Vec<Expr>,
1366 progress: Option<Box<Expr>>,
1367 },
1368
1369 Bless {
1371 ref_expr: Box<Expr>,
1372 class: Option<Box<Expr>>,
1373 },
1374
1375 Caller(Option<Box<Expr>>),
1377
1378 Wantarray,
1380
1381 List(Vec<Expr>),
1383
1384 PostfixIf {
1386 expr: Box<Expr>,
1387 condition: Box<Expr>,
1388 },
1389 PostfixUnless {
1390 expr: Box<Expr>,
1391 condition: Box<Expr>,
1392 },
1393 PostfixWhile {
1394 expr: Box<Expr>,
1395 condition: Box<Expr>,
1396 },
1397 PostfixUntil {
1398 expr: Box<Expr>,
1399 condition: Box<Expr>,
1400 },
1401 PostfixForeach {
1402 expr: Box<Expr>,
1403 list: Box<Expr>,
1404 },
1405
1406 RetryBlock {
1408 body: Block,
1409 times: Box<Expr>,
1410 backoff: RetryBackoff,
1411 },
1412 RateLimitBlock {
1415 slot: u32,
1416 max: Box<Expr>,
1417 window: Box<Expr>,
1418 body: Block,
1419 },
1420 EveryBlock {
1422 interval: Box<Expr>,
1423 body: Block,
1424 },
1425 GenBlock {
1427 body: Block,
1428 },
1429 Yield(Box<Expr>),
1431
1432 AlgebraicMatch {
1434 subject: Box<Expr>,
1435 arms: Vec<MatchArm>,
1436 },
1437}
1438
1439#[derive(Debug, Clone, Serialize, Deserialize)]
1440pub enum StringPart {
1441 Literal(String),
1442 ScalarVar(String),
1443 ArrayVar(String),
1444 Expr(Expr),
1445}
1446
1447#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
1448pub enum DerefKind {
1449 Array,
1450 Hash,
1451 Call,
1452}
1453
1454#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
1455pub enum BinOp {
1456 Add,
1457 Sub,
1458 Mul,
1459 Div,
1460 Mod,
1461 Pow,
1462 Concat,
1463 NumEq,
1464 NumNe,
1465 NumLt,
1466 NumGt,
1467 NumLe,
1468 NumGe,
1469 Spaceship,
1470 StrEq,
1471 StrNe,
1472 StrLt,
1473 StrGt,
1474 StrLe,
1475 StrGe,
1476 StrCmp,
1477 LogAnd,
1478 LogOr,
1479 DefinedOr,
1480 BitAnd,
1481 BitOr,
1482 BitXor,
1483 ShiftLeft,
1484 ShiftRight,
1485 LogAndWord,
1486 LogOrWord,
1487 BindMatch,
1488 BindNotMatch,
1489}
1490
1491#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
1492pub enum UnaryOp {
1493 Negate,
1494 LogNot,
1495 BitNot,
1496 LogNotWord,
1497 PreIncrement,
1498 PreDecrement,
1499 Ref,
1500}
1501
1502#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
1503pub enum PostfixOp {
1504 Increment,
1505 Decrement,
1506}
1507
1508#[cfg(test)]
1509mod tests {
1510 use super::*;
1511
1512 #[test]
1513 fn binop_deref_kind_distinct() {
1514 assert_ne!(BinOp::Add, BinOp::Sub);
1515 assert_eq!(DerefKind::Call, DerefKind::Call);
1516 }
1517
1518 #[test]
1519 fn sigil_variants_exhaustive_in_tests() {
1520 let all = [Sigil::Scalar, Sigil::Array, Sigil::Hash];
1521 assert_eq!(all.len(), 3);
1522 }
1523
1524 #[test]
1525 fn program_empty_roundtrip_clone() {
1526 let p = Program { statements: vec![] };
1527 assert!(p.clone().statements.is_empty());
1528 }
1529
1530 #[test]
1531 fn program_serializes_to_json() {
1532 let p = crate::parse("1+2;").expect("parse");
1533 let s = serde_json::to_string(&p).expect("json");
1534 assert!(s.contains("\"statements\""));
1535 assert!(s.contains("BinOp"));
1536 }
1537}