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 OurSync(Vec<VarDecl>),
171 Block(Block),
173 StmtGroup(Block),
175 Begin(Block),
177 End(Block),
179 UnitCheck(Block),
181 Check(Block),
183 Init(Block),
185 Empty,
187 Goto {
189 target: Box<Expr>,
190 },
191 Continue(Block),
193 StructDecl {
195 def: StructDef,
196 },
197 EnumDecl {
199 def: EnumDef,
200 },
201 ClassDecl {
203 def: ClassDef,
204 },
205 TraitDecl {
207 def: TraitDef,
208 },
209 EvalTimeout {
211 timeout: Expr,
212 body: Block,
213 },
214 TryCatch {
217 try_block: Block,
218 catch_var: String,
219 catch_block: Block,
220 finally_block: Option<Block>,
221 },
222 Given {
224 topic: Expr,
225 body: Block,
226 },
227 When {
229 cond: Expr,
230 body: Block,
231 },
232 DefaultCase {
234 body: Block,
235 },
236 Tie {
238 target: TieTarget,
239 class: Expr,
240 args: Vec<Expr>,
241 },
242 FormatDecl {
244 name: String,
245 lines: Vec<String>,
246 },
247 AdviceDecl {
250 kind: AdviceKind,
251 pattern: String,
252 body: Block,
253 },
254}
255
256#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
258pub enum AdviceKind {
259 Before,
261 After,
263 Around,
265}
266
267#[derive(Debug, Clone, Serialize, Deserialize)]
269pub enum TieTarget {
270 Hash(String),
271 Array(String),
272 Scalar(String),
273}
274
275#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
277pub enum PerlTypeName {
278 Int,
279 Str,
280 Float,
281 Bool,
282 Array,
283 Hash,
284 Ref,
285 Struct(String),
287 Enum(String),
289 Any,
291}
292
293#[derive(Debug, Clone, Serialize, Deserialize)]
295pub struct StructField {
296 pub name: String,
297 pub ty: PerlTypeName,
298 #[serde(skip_serializing_if = "Option::is_none")]
300 pub default: Option<Expr>,
301}
302
303#[derive(Debug, Clone, Serialize, Deserialize)]
305pub struct StructMethod {
306 pub name: String,
307 pub params: Vec<SubSigParam>,
308 pub body: Block,
309}
310
311#[derive(Debug, Clone, Serialize, Deserialize)]
313pub struct EnumVariant {
314 pub name: String,
315 pub ty: Option<PerlTypeName>,
317}
318
319#[derive(Debug, Clone, Serialize, Deserialize)]
321pub struct EnumDef {
322 pub name: String,
323 pub variants: Vec<EnumVariant>,
324}
325
326impl EnumDef {
327 #[inline]
328 pub fn variant_index(&self, name: &str) -> Option<usize> {
329 self.variants.iter().position(|v| v.name == name)
330 }
331
332 #[inline]
333 pub fn variant(&self, name: &str) -> Option<&EnumVariant> {
334 self.variants.iter().find(|v| v.name == name)
335 }
336}
337
338#[derive(Debug, Clone, Serialize, Deserialize)]
340pub struct StructDef {
341 pub name: String,
342 pub fields: Vec<StructField>,
343 #[serde(default, skip_serializing_if = "Vec::is_empty")]
345 pub methods: Vec<StructMethod>,
346}
347
348#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
350pub enum Visibility {
351 #[default]
352 Public,
353 Private,
354 Protected,
355}
356
357#[derive(Debug, Clone, Serialize, Deserialize)]
359pub struct ClassField {
360 pub name: String,
361 pub ty: PerlTypeName,
362 pub visibility: Visibility,
363 #[serde(skip_serializing_if = "Option::is_none")]
364 pub default: Option<Expr>,
365}
366
367#[derive(Debug, Clone, Serialize, Deserialize)]
369pub struct ClassMethod {
370 pub name: String,
371 pub params: Vec<SubSigParam>,
372 pub body: Option<Block>,
373 pub visibility: Visibility,
374 pub is_static: bool,
375 #[serde(default, skip_serializing_if = "is_false")]
376 pub is_final: bool,
377}
378
379#[derive(Debug, Clone, Serialize, Deserialize)]
381pub struct TraitDef {
382 pub name: String,
383 pub methods: Vec<ClassMethod>,
384}
385
386impl TraitDef {
387 #[inline]
388 pub fn method(&self, name: &str) -> Option<&ClassMethod> {
389 self.methods.iter().find(|m| m.name == name)
390 }
391
392 #[inline]
393 pub fn required_methods(&self) -> impl Iterator<Item = &ClassMethod> {
394 self.methods.iter().filter(|m| m.body.is_none())
395 }
396}
397
398#[derive(Debug, Clone, Serialize, Deserialize)]
400pub struct ClassStaticField {
401 pub name: String,
402 pub ty: PerlTypeName,
403 pub visibility: Visibility,
404 #[serde(skip_serializing_if = "Option::is_none")]
405 pub default: Option<Expr>,
406}
407
408#[derive(Debug, Clone, Serialize, Deserialize)]
410pub struct ClassDef {
411 pub name: String,
412 #[serde(default, skip_serializing_if = "is_false")]
413 pub is_abstract: bool,
414 #[serde(default, skip_serializing_if = "is_false")]
415 pub is_final: bool,
416 #[serde(default, skip_serializing_if = "Vec::is_empty")]
417 pub extends: Vec<String>,
418 #[serde(default, skip_serializing_if = "Vec::is_empty")]
419 pub implements: Vec<String>,
420 pub fields: Vec<ClassField>,
421 pub methods: Vec<ClassMethod>,
422 #[serde(default, skip_serializing_if = "Vec::is_empty")]
423 pub static_fields: Vec<ClassStaticField>,
424}
425
426fn is_false(v: &bool) -> bool {
427 !*v
428}
429
430impl ClassDef {
431 #[inline]
432 pub fn field_index(&self, name: &str) -> Option<usize> {
433 self.fields.iter().position(|f| f.name == name)
434 }
435
436 #[inline]
437 pub fn field(&self, name: &str) -> Option<&ClassField> {
438 self.fields.iter().find(|f| f.name == name)
439 }
440
441 #[inline]
442 pub fn method(&self, name: &str) -> Option<&ClassMethod> {
443 self.methods.iter().find(|m| m.name == name)
444 }
445
446 #[inline]
447 pub fn static_methods(&self) -> impl Iterator<Item = &ClassMethod> {
448 self.methods.iter().filter(|m| m.is_static)
449 }
450
451 #[inline]
452 pub fn instance_methods(&self) -> impl Iterator<Item = &ClassMethod> {
453 self.methods.iter().filter(|m| !m.is_static)
454 }
455}
456
457impl StructDef {
458 #[inline]
459 pub fn field_index(&self, name: &str) -> Option<usize> {
460 self.fields.iter().position(|f| f.name == name)
461 }
462
463 #[inline]
465 pub fn field_type(&self, name: &str) -> Option<&PerlTypeName> {
466 self.fields.iter().find(|f| f.name == name).map(|f| &f.ty)
467 }
468
469 #[inline]
471 pub fn method(&self, name: &str) -> Option<&StructMethod> {
472 self.methods.iter().find(|m| m.name == name)
473 }
474}
475
476impl PerlTypeName {
477 #[inline]
479 pub fn from_byte(b: u8) -> Option<Self> {
480 match b {
481 0 => Some(Self::Int),
482 1 => Some(Self::Str),
483 2 => Some(Self::Float),
484 3 => Some(Self::Bool),
485 4 => Some(Self::Array),
486 5 => Some(Self::Hash),
487 6 => Some(Self::Ref),
488 7 => Some(Self::Any),
489 _ => None,
490 }
491 }
492
493 #[inline]
495 pub fn as_byte(&self) -> Option<u8> {
496 match self {
497 Self::Int => Some(0),
498 Self::Str => Some(1),
499 Self::Float => Some(2),
500 Self::Bool => Some(3),
501 Self::Array => Some(4),
502 Self::Hash => Some(5),
503 Self::Ref => Some(6),
504 Self::Any => Some(7),
505 Self::Struct(_) | Self::Enum(_) => None,
506 }
507 }
508
509 pub fn display_name(&self) -> String {
511 match self {
512 Self::Int => "Int".to_string(),
513 Self::Str => "Str".to_string(),
514 Self::Float => "Float".to_string(),
515 Self::Bool => "Bool".to_string(),
516 Self::Array => "Array".to_string(),
517 Self::Hash => "Hash".to_string(),
518 Self::Ref => "Ref".to_string(),
519 Self::Any => "Any".to_string(),
520 Self::Struct(name) => name.clone(),
521 Self::Enum(name) => name.clone(),
522 }
523 }
524
525 pub fn check_value(&self, v: &crate::value::PerlValue) -> Result<(), String> {
527 match self {
528 Self::Int => {
529 if v.is_integer_like() {
530 Ok(())
531 } else {
532 Err(format!("expected Int (INTEGER), got {}", v.type_name()))
533 }
534 }
535 Self::Str => {
536 if v.is_string_like() {
537 Ok(())
538 } else {
539 Err(format!("expected Str (STRING), got {}", v.type_name()))
540 }
541 }
542 Self::Float => {
543 if v.is_integer_like() || v.is_float_like() {
544 Ok(())
545 } else {
546 Err(format!(
547 "expected Float (INTEGER or FLOAT), got {}",
548 v.type_name()
549 ))
550 }
551 }
552 Self::Bool => Ok(()),
553 Self::Array => {
554 if v.as_array_vec().is_some() || v.as_array_ref().is_some() {
555 Ok(())
556 } else {
557 Err(format!("expected Array, got {}", v.type_name()))
558 }
559 }
560 Self::Hash => {
561 if v.as_hash_map().is_some() || v.as_hash_ref().is_some() {
562 Ok(())
563 } else {
564 Err(format!("expected Hash, got {}", v.type_name()))
565 }
566 }
567 Self::Ref => {
568 if v.as_scalar_ref().is_some()
569 || v.as_array_ref().is_some()
570 || v.as_hash_ref().is_some()
571 || v.as_code_ref().is_some()
572 {
573 Ok(())
574 } else {
575 Err(format!("expected Ref, got {}", v.type_name()))
576 }
577 }
578 Self::Struct(name) => {
579 if v.is_undef() {
581 return Ok(());
582 }
583 if let Some(s) = v.as_struct_inst() {
584 if s.def.name == *name {
585 Ok(())
586 } else {
587 Err(format!(
588 "expected struct {}, got struct {}",
589 name, s.def.name
590 ))
591 }
592 } else if let Some(e) = v.as_enum_inst() {
593 if e.def.name == *name {
594 Ok(())
595 } else {
596 Err(format!("expected {}, got enum {}", name, e.def.name))
597 }
598 } else if let Some(c) = v.as_class_inst() {
599 if c.isa(name) {
601 Ok(())
602 } else {
603 Err(format!("expected {}, got {}", name, c.def.name))
604 }
605 } else {
606 Err(format!("expected {}, got {}", name, v.type_name()))
607 }
608 }
609 Self::Enum(name) => {
610 if v.is_undef() {
612 return Ok(());
613 }
614 if let Some(e) = v.as_enum_inst() {
615 if e.def.name == *name {
616 Ok(())
617 } else {
618 Err(format!("expected enum {}, got enum {}", name, e.def.name))
619 }
620 } else {
621 Err(format!("expected enum {}, got {}", name, v.type_name()))
622 }
623 }
624 Self::Any => Ok(()),
625 }
626 }
627}
628
629#[derive(Debug, Clone, Serialize, Deserialize)]
630pub struct VarDecl {
631 pub sigil: Sigil,
632 pub name: String,
633 pub initializer: Option<Expr>,
634 pub frozen: bool,
636 pub type_annotation: Option<PerlTypeName>,
638}
639
640#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
641pub enum Sigil {
642 Scalar,
643 Array,
644 Hash,
645 Typeglob,
647}
648
649pub type Block = Vec<Statement>;
650
651#[derive(Debug, Clone, Serialize, Deserialize)]
653pub enum SortComparator {
654 Block(Block),
655 Code(Box<Expr>),
656}
657
658#[derive(Debug, Clone, Serialize, Deserialize)]
662pub struct MatchArm {
663 pub pattern: MatchPattern,
664 #[serde(skip_serializing_if = "Option::is_none")]
666 pub guard: Option<Box<Expr>>,
667 pub body: Expr,
668}
669
670#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
672pub enum RetryBackoff {
673 None,
675 Linear,
677 Exponential,
679}
680
681#[derive(Debug, Clone, Serialize, Deserialize)]
683pub enum MatchPattern {
684 Any,
686 Regex { pattern: String, flags: String },
689 Value(Box<Expr>),
691 Array(Vec<MatchArrayElem>),
693 Hash(Vec<MatchHashPair>),
695 OptionSome(String),
698}
699
700#[derive(Debug, Clone, Serialize, Deserialize)]
701pub enum MatchArrayElem {
702 Expr(Expr),
703 CaptureScalar(String),
706 Rest,
708 RestBind(String),
710}
711
712#[derive(Debug, Clone, Serialize, Deserialize)]
713pub enum MatchHashPair {
714 KeyOnly { key: Expr },
716 Capture { key: Expr, name: String },
718}
719
720#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
721pub enum MagicConstKind {
722 File,
724 Line,
726 Sub,
728}
729
730#[derive(Debug, Clone, Serialize, Deserialize)]
731pub struct Expr {
732 pub kind: ExprKind,
733 pub line: usize,
734}
735
736#[derive(Debug, Clone, Serialize, Deserialize)]
737pub enum ExprKind {
738 Integer(i64),
740 Float(f64),
741 String(String),
742 Bareword(String),
745 Regex(String, String),
746 QW(Vec<String>),
747 Undef,
748 MagicConst(MagicConstKind),
750
751 InterpolatedString(Vec<StringPart>),
753
754 ScalarVar(String),
756 ArrayVar(String),
757 HashVar(String),
758 ArrayElement {
759 array: String,
760 index: Box<Expr>,
761 },
762 HashElement {
763 hash: String,
764 key: Box<Expr>,
765 },
766 ArraySlice {
767 array: String,
768 indices: Vec<Expr>,
769 },
770 HashSlice {
771 hash: String,
772 keys: Vec<Expr>,
773 },
774 HashKvSlice {
777 hash: String,
778 keys: Vec<Expr>,
779 },
780 HashSliceDeref {
782 container: Box<Expr>,
783 keys: Vec<Expr>,
784 },
785 AnonymousListSlice {
787 source: Box<Expr>,
788 indices: Vec<Expr>,
789 },
790
791 ScalarRef(Box<Expr>),
793 ArrayRef(Vec<Expr>),
794 HashRef(Vec<(Expr, Expr)>),
795 CodeRef {
796 params: Vec<SubSigParam>,
797 body: Block,
798 },
799 SubroutineRef(String),
801 SubroutineCodeRef(String),
803 DynamicSubCodeRef(Box<Expr>),
805 Deref {
806 expr: Box<Expr>,
807 kind: Sigil,
808 },
809 ArrowDeref {
810 expr: Box<Expr>,
811 index: Box<Expr>,
812 kind: DerefKind,
813 },
814
815 BinOp {
817 left: Box<Expr>,
818 op: BinOp,
819 right: Box<Expr>,
820 },
821 UnaryOp {
822 op: UnaryOp,
823 expr: Box<Expr>,
824 },
825 PostfixOp {
826 expr: Box<Expr>,
827 op: PostfixOp,
828 },
829 Assign {
830 target: Box<Expr>,
831 value: Box<Expr>,
832 },
833 CompoundAssign {
834 target: Box<Expr>,
835 op: BinOp,
836 value: Box<Expr>,
837 },
838 Ternary {
839 condition: Box<Expr>,
840 then_expr: Box<Expr>,
841 else_expr: Box<Expr>,
842 },
843
844 Repeat {
853 expr: Box<Expr>,
854 count: Box<Expr>,
855 list_repeat: bool,
856 },
857
858 Range {
861 from: Box<Expr>,
862 to: Box<Expr>,
863 #[serde(default)]
864 exclusive: bool,
865 #[serde(default)]
866 step: Option<Box<Expr>>,
867 },
868
869 SliceRange {
874 #[serde(default)]
875 from: Option<Box<Expr>>,
876 #[serde(default)]
877 to: Option<Box<Expr>>,
878 #[serde(default)]
879 step: Option<Box<Expr>>,
880 },
881
882 MyExpr {
888 keyword: String, decls: Vec<VarDecl>,
890 },
891
892 FuncCall {
894 name: String,
895 args: Vec<Expr>,
896 },
897
898 MethodCall {
900 object: Box<Expr>,
901 method: String,
902 args: Vec<Expr>,
903 #[serde(default)]
905 super_call: bool,
906 },
907 IndirectCall {
910 target: Box<Expr>,
911 args: Vec<Expr>,
912 #[serde(default)]
913 ampersand: bool,
914 #[serde(default)]
916 pass_caller_arglist: bool,
917 },
918 Typeglob(String),
920 TypeglobExpr(Box<Expr>),
922
923 Print {
925 handle: Option<String>,
926 args: Vec<Expr>,
927 },
928 Say {
929 handle: Option<String>,
930 args: Vec<Expr>,
931 },
932 Printf {
933 handle: Option<String>,
934 args: Vec<Expr>,
935 },
936 Die(Vec<Expr>),
937 Warn(Vec<Expr>),
938
939 Match {
941 expr: Box<Expr>,
942 pattern: String,
943 flags: String,
944 scalar_g: bool,
946 #[serde(default = "default_delim")]
947 delim: char,
948 },
949 Substitution {
950 expr: Box<Expr>,
951 pattern: String,
952 replacement: String,
953 flags: String,
954 #[serde(default = "default_delim")]
955 delim: char,
956 },
957 Transliterate {
958 expr: Box<Expr>,
959 from: String,
960 to: String,
961 flags: String,
962 #[serde(default = "default_delim")]
963 delim: char,
964 },
965
966 MapExpr {
968 block: Block,
969 list: Box<Expr>,
970 flatten_array_refs: bool,
972 #[serde(default)]
974 stream: bool,
975 },
976 MapExprComma {
978 expr: Box<Expr>,
979 list: Box<Expr>,
980 flatten_array_refs: bool,
981 #[serde(default)]
982 stream: bool,
983 },
984 GrepExpr {
985 block: Block,
986 list: Box<Expr>,
987 #[serde(default)]
988 keyword: GrepBuiltinKeyword,
989 },
990 GrepExprComma {
992 expr: Box<Expr>,
993 list: Box<Expr>,
994 #[serde(default)]
995 keyword: GrepBuiltinKeyword,
996 },
997 SortExpr {
999 cmp: Option<SortComparator>,
1000 list: Box<Expr>,
1001 },
1002 ReverseExpr(Box<Expr>),
1003 Rev(Box<Expr>),
1005 JoinExpr {
1006 separator: Box<Expr>,
1007 list: Box<Expr>,
1008 },
1009 SplitExpr {
1010 pattern: Box<Expr>,
1011 string: Box<Expr>,
1012 limit: Option<Box<Expr>>,
1013 },
1014 ForEachExpr {
1017 block: Block,
1018 list: Box<Expr>,
1019 },
1020
1021 PMapExpr {
1023 block: Block,
1024 list: Box<Expr>,
1025 progress: Option<Box<Expr>>,
1027 flat_outputs: bool,
1030 #[serde(default, skip_serializing_if = "Option::is_none")]
1032 on_cluster: Option<Box<Expr>>,
1033 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
1036 stream: bool,
1037 },
1038 PMapChunkedExpr {
1040 chunk_size: Box<Expr>,
1041 block: Block,
1042 list: Box<Expr>,
1043 progress: Option<Box<Expr>>,
1044 },
1045 PGrepExpr {
1046 block: Block,
1047 list: Box<Expr>,
1048 progress: Option<Box<Expr>>,
1050 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
1052 stream: bool,
1053 },
1054 PForExpr {
1056 block: Block,
1057 list: Box<Expr>,
1058 progress: Option<Box<Expr>>,
1059 },
1060 ParLinesExpr {
1062 path: Box<Expr>,
1063 callback: Box<Expr>,
1064 progress: Option<Box<Expr>>,
1065 },
1066 ParWalkExpr {
1068 path: Box<Expr>,
1069 callback: Box<Expr>,
1070 progress: Option<Box<Expr>>,
1071 },
1072 PwatchExpr {
1074 path: Box<Expr>,
1075 callback: Box<Expr>,
1076 },
1077 PSortExpr {
1079 cmp: Option<Block>,
1080 list: Box<Expr>,
1081 progress: Option<Box<Expr>>,
1082 },
1083 ReduceExpr {
1086 block: Block,
1087 list: Box<Expr>,
1088 },
1089 PReduceExpr {
1092 block: Block,
1093 list: Box<Expr>,
1094 progress: Option<Box<Expr>>,
1096 },
1097 PReduceInitExpr {
1102 init: Box<Expr>,
1103 block: Block,
1104 list: Box<Expr>,
1105 progress: Option<Box<Expr>>,
1106 },
1107 PMapReduceExpr {
1109 map_block: Block,
1110 reduce_block: Block,
1111 list: Box<Expr>,
1112 progress: Option<Box<Expr>>,
1113 },
1114 PcacheExpr {
1116 block: Block,
1117 list: Box<Expr>,
1118 progress: Option<Box<Expr>>,
1119 },
1120 PselectExpr {
1122 receivers: Vec<Expr>,
1123 timeout: Option<Box<Expr>>,
1124 },
1125 FanExpr {
1130 count: Option<Box<Expr>>,
1131 block: Block,
1132 progress: Option<Box<Expr>>,
1133 capture: bool,
1134 },
1135
1136 AsyncBlock {
1138 body: Block,
1139 },
1140 SpawnBlock {
1142 body: Block,
1143 },
1144 Trace {
1146 body: Block,
1147 },
1148 Timer {
1150 body: Block,
1151 },
1152 Bench {
1154 body: Block,
1155 times: Box<Expr>,
1156 },
1157 Spinner {
1159 message: Box<Expr>,
1160 body: Block,
1161 },
1162 Await(Box<Expr>),
1164 Slurp(Box<Expr>),
1166 Capture(Box<Expr>),
1168 Qx(Box<Expr>),
1170 FetchUrl(Box<Expr>),
1172
1173 Pchannel {
1175 capacity: Option<Box<Expr>>,
1176 },
1177
1178 Push {
1180 array: Box<Expr>,
1181 values: Vec<Expr>,
1182 },
1183 Pop(Box<Expr>),
1184 Shift(Box<Expr>),
1185 Unshift {
1186 array: Box<Expr>,
1187 values: Vec<Expr>,
1188 },
1189 Splice {
1190 array: Box<Expr>,
1191 offset: Option<Box<Expr>>,
1192 length: Option<Box<Expr>>,
1193 replacement: Vec<Expr>,
1194 },
1195 Delete(Box<Expr>),
1196 Exists(Box<Expr>),
1197 Keys(Box<Expr>),
1198 Values(Box<Expr>),
1199 Each(Box<Expr>),
1200
1201 Chomp(Box<Expr>),
1203 Chop(Box<Expr>),
1204 Length(Box<Expr>),
1205 Substr {
1206 string: Box<Expr>,
1207 offset: Box<Expr>,
1208 length: Option<Box<Expr>>,
1209 replacement: Option<Box<Expr>>,
1210 },
1211 Index {
1212 string: Box<Expr>,
1213 substr: Box<Expr>,
1214 position: Option<Box<Expr>>,
1215 },
1216 Rindex {
1217 string: Box<Expr>,
1218 substr: Box<Expr>,
1219 position: Option<Box<Expr>>,
1220 },
1221 Sprintf {
1222 format: Box<Expr>,
1223 args: Vec<Expr>,
1224 },
1225
1226 Abs(Box<Expr>),
1228 Int(Box<Expr>),
1229 Sqrt(Box<Expr>),
1230 Sin(Box<Expr>),
1231 Cos(Box<Expr>),
1232 Atan2 {
1233 y: Box<Expr>,
1234 x: Box<Expr>,
1235 },
1236 Exp(Box<Expr>),
1237 Log(Box<Expr>),
1238 Rand(Option<Box<Expr>>),
1240 Srand(Option<Box<Expr>>),
1242 Hex(Box<Expr>),
1243 Oct(Box<Expr>),
1244
1245 Lc(Box<Expr>),
1247 Uc(Box<Expr>),
1248 Lcfirst(Box<Expr>),
1249 Ucfirst(Box<Expr>),
1250
1251 Fc(Box<Expr>),
1253 Crypt {
1255 plaintext: Box<Expr>,
1256 salt: Box<Expr>,
1257 },
1258 Pos(Option<Box<Expr>>),
1260 Study(Box<Expr>),
1262
1263 Defined(Box<Expr>),
1265 Ref(Box<Expr>),
1266 ScalarContext(Box<Expr>),
1267
1268 Chr(Box<Expr>),
1270 Ord(Box<Expr>),
1271
1272 OpenMyHandle {
1275 name: String,
1276 },
1277 Open {
1278 handle: Box<Expr>,
1279 mode: Box<Expr>,
1280 file: Option<Box<Expr>>,
1281 },
1282 Close(Box<Expr>),
1283 ReadLine(Option<String>),
1284 Eof(Option<Box<Expr>>),
1285
1286 Opendir {
1287 handle: Box<Expr>,
1288 path: Box<Expr>,
1289 },
1290 Readdir(Box<Expr>),
1291 Closedir(Box<Expr>),
1292 Rewinddir(Box<Expr>),
1293 Telldir(Box<Expr>),
1294 Seekdir {
1295 handle: Box<Expr>,
1296 position: Box<Expr>,
1297 },
1298
1299 FileTest {
1301 op: char,
1302 expr: Box<Expr>,
1303 },
1304
1305 System(Vec<Expr>),
1307 Exec(Vec<Expr>),
1308 Eval(Box<Expr>),
1309 Do(Box<Expr>),
1310 Require(Box<Expr>),
1311 Exit(Option<Box<Expr>>),
1312 Chdir(Box<Expr>),
1313 Mkdir {
1314 path: Box<Expr>,
1315 mode: Option<Box<Expr>>,
1316 },
1317 Unlink(Vec<Expr>),
1318 Rename {
1319 old: Box<Expr>,
1320 new: Box<Expr>,
1321 },
1322 Chmod(Vec<Expr>),
1324 Chown(Vec<Expr>),
1326
1327 Stat(Box<Expr>),
1328 Lstat(Box<Expr>),
1329 Link {
1330 old: Box<Expr>,
1331 new: Box<Expr>,
1332 },
1333 Symlink {
1334 old: Box<Expr>,
1335 new: Box<Expr>,
1336 },
1337 Readlink(Box<Expr>),
1338 Files(Vec<Expr>),
1340 Filesf(Vec<Expr>),
1342 FilesfRecursive(Vec<Expr>),
1344 Dirs(Vec<Expr>),
1346 DirsRecursive(Vec<Expr>),
1348 SymLinks(Vec<Expr>),
1350 Sockets(Vec<Expr>),
1352 Pipes(Vec<Expr>),
1354 BlockDevices(Vec<Expr>),
1356 CharDevices(Vec<Expr>),
1358 Executables(Vec<Expr>),
1360 Glob(Vec<Expr>),
1361 GlobPar {
1364 args: Vec<Expr>,
1365 progress: Option<Box<Expr>>,
1366 },
1367 ParSed {
1369 args: Vec<Expr>,
1370 progress: Option<Box<Expr>>,
1371 },
1372
1373 Bless {
1375 ref_expr: Box<Expr>,
1376 class: Option<Box<Expr>>,
1377 },
1378
1379 Caller(Option<Box<Expr>>),
1381
1382 Wantarray,
1384
1385 List(Vec<Expr>),
1387
1388 PostfixIf {
1390 expr: Box<Expr>,
1391 condition: Box<Expr>,
1392 },
1393 PostfixUnless {
1394 expr: Box<Expr>,
1395 condition: Box<Expr>,
1396 },
1397 PostfixWhile {
1398 expr: Box<Expr>,
1399 condition: Box<Expr>,
1400 },
1401 PostfixUntil {
1402 expr: Box<Expr>,
1403 condition: Box<Expr>,
1404 },
1405 PostfixForeach {
1406 expr: Box<Expr>,
1407 list: Box<Expr>,
1408 },
1409
1410 RetryBlock {
1412 body: Block,
1413 times: Box<Expr>,
1414 backoff: RetryBackoff,
1415 },
1416 RateLimitBlock {
1419 slot: u32,
1420 max: Box<Expr>,
1421 window: Box<Expr>,
1422 body: Block,
1423 },
1424 EveryBlock {
1426 interval: Box<Expr>,
1427 body: Block,
1428 },
1429 GenBlock {
1431 body: Block,
1432 },
1433 Yield(Box<Expr>),
1435
1436 AlgebraicMatch {
1438 subject: Box<Expr>,
1439 arms: Vec<MatchArm>,
1440 },
1441}
1442
1443#[derive(Debug, Clone, Serialize, Deserialize)]
1444pub enum StringPart {
1445 Literal(String),
1446 ScalarVar(String),
1447 ArrayVar(String),
1448 Expr(Expr),
1449}
1450
1451#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
1452pub enum DerefKind {
1453 Array,
1454 Hash,
1455 Call,
1456}
1457
1458#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
1459pub enum BinOp {
1460 Add,
1461 Sub,
1462 Mul,
1463 Div,
1464 Mod,
1465 Pow,
1466 Concat,
1467 NumEq,
1468 NumNe,
1469 NumLt,
1470 NumGt,
1471 NumLe,
1472 NumGe,
1473 Spaceship,
1474 StrEq,
1475 StrNe,
1476 StrLt,
1477 StrGt,
1478 StrLe,
1479 StrGe,
1480 StrCmp,
1481 LogAnd,
1482 LogOr,
1483 DefinedOr,
1484 BitAnd,
1485 BitOr,
1486 BitXor,
1487 ShiftLeft,
1488 ShiftRight,
1489 LogAndWord,
1490 LogOrWord,
1491 BindMatch,
1492 BindNotMatch,
1493}
1494
1495#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
1496pub enum UnaryOp {
1497 Negate,
1498 LogNot,
1499 BitNot,
1500 LogNotWord,
1501 PreIncrement,
1502 PreDecrement,
1503 Ref,
1504}
1505
1506#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
1507pub enum PostfixOp {
1508 Increment,
1509 Decrement,
1510}
1511
1512#[cfg(test)]
1513mod tests {
1514 use super::*;
1515
1516 #[test]
1517 fn binop_deref_kind_distinct() {
1518 assert_ne!(BinOp::Add, BinOp::Sub);
1519 assert_eq!(DerefKind::Call, DerefKind::Call);
1520 }
1521
1522 #[test]
1523 fn sigil_variants_exhaustive_in_tests() {
1524 let all = [Sigil::Scalar, Sigil::Array, Sigil::Hash];
1525 assert_eq!(all.len(), 3);
1526 }
1527
1528 #[test]
1529 fn program_empty_roundtrip_clone() {
1530 let p = Program { statements: vec![] };
1531 assert!(p.clone().statements.is_empty());
1532 }
1533
1534 #[test]
1535 fn program_serializes_to_json() {
1536 let p = crate::parse("1+2;").expect("parse");
1537 let s = serde_json::to_string(&p).expect("json");
1538 assert!(s.contains("\"statements\""));
1539 assert!(s.contains("BinOp"));
1540 }
1541}