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}
244
245#[derive(Debug, Clone, Serialize, Deserialize)]
247pub enum TieTarget {
248 Hash(String),
249 Array(String),
250 Scalar(String),
251}
252
253#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
255pub enum PerlTypeName {
256 Int,
257 Str,
258 Float,
259 Bool,
260 Array,
261 Hash,
262 Ref,
263 Struct(String),
265 Enum(String),
267 Any,
269}
270
271#[derive(Debug, Clone, Serialize, Deserialize)]
273pub struct StructField {
274 pub name: String,
275 pub ty: PerlTypeName,
276 #[serde(skip_serializing_if = "Option::is_none")]
278 pub default: Option<Expr>,
279}
280
281#[derive(Debug, Clone, Serialize, Deserialize)]
283pub struct StructMethod {
284 pub name: String,
285 pub params: Vec<SubSigParam>,
286 pub body: Block,
287}
288
289#[derive(Debug, Clone, Serialize, Deserialize)]
291pub struct EnumVariant {
292 pub name: String,
293 pub ty: Option<PerlTypeName>,
295}
296
297#[derive(Debug, Clone, Serialize, Deserialize)]
299pub struct EnumDef {
300 pub name: String,
301 pub variants: Vec<EnumVariant>,
302}
303
304impl EnumDef {
305 #[inline]
306 pub fn variant_index(&self, name: &str) -> Option<usize> {
307 self.variants.iter().position(|v| v.name == name)
308 }
309
310 #[inline]
311 pub fn variant(&self, name: &str) -> Option<&EnumVariant> {
312 self.variants.iter().find(|v| v.name == name)
313 }
314}
315
316#[derive(Debug, Clone, Serialize, Deserialize)]
318pub struct StructDef {
319 pub name: String,
320 pub fields: Vec<StructField>,
321 #[serde(default, skip_serializing_if = "Vec::is_empty")]
323 pub methods: Vec<StructMethod>,
324}
325
326#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
328pub enum Visibility {
329 #[default]
330 Public,
331 Private,
332 Protected,
333}
334
335#[derive(Debug, Clone, Serialize, Deserialize)]
337pub struct ClassField {
338 pub name: String,
339 pub ty: PerlTypeName,
340 pub visibility: Visibility,
341 #[serde(skip_serializing_if = "Option::is_none")]
342 pub default: Option<Expr>,
343}
344
345#[derive(Debug, Clone, Serialize, Deserialize)]
347pub struct ClassMethod {
348 pub name: String,
349 pub params: Vec<SubSigParam>,
350 pub body: Option<Block>,
351 pub visibility: Visibility,
352 pub is_static: bool,
353 #[serde(default, skip_serializing_if = "is_false")]
354 pub is_final: bool,
355}
356
357#[derive(Debug, Clone, Serialize, Deserialize)]
359pub struct TraitDef {
360 pub name: String,
361 pub methods: Vec<ClassMethod>,
362}
363
364impl TraitDef {
365 #[inline]
366 pub fn method(&self, name: &str) -> Option<&ClassMethod> {
367 self.methods.iter().find(|m| m.name == name)
368 }
369
370 #[inline]
371 pub fn required_methods(&self) -> impl Iterator<Item = &ClassMethod> {
372 self.methods.iter().filter(|m| m.body.is_none())
373 }
374}
375
376#[derive(Debug, Clone, Serialize, Deserialize)]
378pub struct ClassStaticField {
379 pub name: String,
380 pub ty: PerlTypeName,
381 pub visibility: Visibility,
382 #[serde(skip_serializing_if = "Option::is_none")]
383 pub default: Option<Expr>,
384}
385
386#[derive(Debug, Clone, Serialize, Deserialize)]
388pub struct ClassDef {
389 pub name: String,
390 #[serde(default, skip_serializing_if = "is_false")]
391 pub is_abstract: bool,
392 #[serde(default, skip_serializing_if = "is_false")]
393 pub is_final: bool,
394 #[serde(default, skip_serializing_if = "Vec::is_empty")]
395 pub extends: Vec<String>,
396 #[serde(default, skip_serializing_if = "Vec::is_empty")]
397 pub implements: Vec<String>,
398 pub fields: Vec<ClassField>,
399 pub methods: Vec<ClassMethod>,
400 #[serde(default, skip_serializing_if = "Vec::is_empty")]
401 pub static_fields: Vec<ClassStaticField>,
402}
403
404fn is_false(v: &bool) -> bool {
405 !*v
406}
407
408impl ClassDef {
409 #[inline]
410 pub fn field_index(&self, name: &str) -> Option<usize> {
411 self.fields.iter().position(|f| f.name == name)
412 }
413
414 #[inline]
415 pub fn field(&self, name: &str) -> Option<&ClassField> {
416 self.fields.iter().find(|f| f.name == name)
417 }
418
419 #[inline]
420 pub fn method(&self, name: &str) -> Option<&ClassMethod> {
421 self.methods.iter().find(|m| m.name == name)
422 }
423
424 #[inline]
425 pub fn static_methods(&self) -> impl Iterator<Item = &ClassMethod> {
426 self.methods.iter().filter(|m| m.is_static)
427 }
428
429 #[inline]
430 pub fn instance_methods(&self) -> impl Iterator<Item = &ClassMethod> {
431 self.methods.iter().filter(|m| !m.is_static)
432 }
433}
434
435impl StructDef {
436 #[inline]
437 pub fn field_index(&self, name: &str) -> Option<usize> {
438 self.fields.iter().position(|f| f.name == name)
439 }
440
441 #[inline]
443 pub fn field_type(&self, name: &str) -> Option<&PerlTypeName> {
444 self.fields.iter().find(|f| f.name == name).map(|f| &f.ty)
445 }
446
447 #[inline]
449 pub fn method(&self, name: &str) -> Option<&StructMethod> {
450 self.methods.iter().find(|m| m.name == name)
451 }
452}
453
454impl PerlTypeName {
455 #[inline]
457 pub fn from_byte(b: u8) -> Option<Self> {
458 match b {
459 0 => Some(Self::Int),
460 1 => Some(Self::Str),
461 2 => Some(Self::Float),
462 3 => Some(Self::Bool),
463 4 => Some(Self::Array),
464 5 => Some(Self::Hash),
465 6 => Some(Self::Ref),
466 7 => Some(Self::Any),
467 _ => None,
468 }
469 }
470
471 #[inline]
473 pub fn as_byte(&self) -> Option<u8> {
474 match self {
475 Self::Int => Some(0),
476 Self::Str => Some(1),
477 Self::Float => Some(2),
478 Self::Bool => Some(3),
479 Self::Array => Some(4),
480 Self::Hash => Some(5),
481 Self::Ref => Some(6),
482 Self::Any => Some(7),
483 Self::Struct(_) | Self::Enum(_) => None,
484 }
485 }
486
487 pub fn display_name(&self) -> String {
489 match self {
490 Self::Int => "Int".to_string(),
491 Self::Str => "Str".to_string(),
492 Self::Float => "Float".to_string(),
493 Self::Bool => "Bool".to_string(),
494 Self::Array => "Array".to_string(),
495 Self::Hash => "Hash".to_string(),
496 Self::Ref => "Ref".to_string(),
497 Self::Any => "Any".to_string(),
498 Self::Struct(name) => name.clone(),
499 Self::Enum(name) => name.clone(),
500 }
501 }
502
503 pub fn check_value(&self, v: &crate::value::PerlValue) -> Result<(), String> {
505 match self {
506 Self::Int => {
507 if v.is_integer_like() {
508 Ok(())
509 } else {
510 Err(format!("expected Int (INTEGER), got {}", v.type_name()))
511 }
512 }
513 Self::Str => {
514 if v.is_string_like() {
515 Ok(())
516 } else {
517 Err(format!("expected Str (STRING), got {}", v.type_name()))
518 }
519 }
520 Self::Float => {
521 if v.is_integer_like() || v.is_float_like() {
522 Ok(())
523 } else {
524 Err(format!(
525 "expected Float (INTEGER or FLOAT), got {}",
526 v.type_name()
527 ))
528 }
529 }
530 Self::Bool => Ok(()),
531 Self::Array => {
532 if v.as_array_vec().is_some() || v.as_array_ref().is_some() {
533 Ok(())
534 } else {
535 Err(format!("expected Array, got {}", v.type_name()))
536 }
537 }
538 Self::Hash => {
539 if v.as_hash_map().is_some() || v.as_hash_ref().is_some() {
540 Ok(())
541 } else {
542 Err(format!("expected Hash, got {}", v.type_name()))
543 }
544 }
545 Self::Ref => {
546 if v.as_scalar_ref().is_some()
547 || v.as_array_ref().is_some()
548 || v.as_hash_ref().is_some()
549 || v.as_code_ref().is_some()
550 {
551 Ok(())
552 } else {
553 Err(format!("expected Ref, got {}", v.type_name()))
554 }
555 }
556 Self::Struct(name) => {
557 if v.is_undef() {
559 return Ok(());
560 }
561 if let Some(s) = v.as_struct_inst() {
562 if s.def.name == *name {
563 Ok(())
564 } else {
565 Err(format!(
566 "expected struct {}, got struct {}",
567 name, s.def.name
568 ))
569 }
570 } else if let Some(e) = v.as_enum_inst() {
571 if e.def.name == *name {
572 Ok(())
573 } else {
574 Err(format!("expected {}, got enum {}", name, e.def.name))
575 }
576 } else if let Some(c) = v.as_class_inst() {
577 if c.isa(name) {
579 Ok(())
580 } else {
581 Err(format!("expected {}, got {}", name, c.def.name))
582 }
583 } else {
584 Err(format!("expected {}, got {}", name, v.type_name()))
585 }
586 }
587 Self::Enum(name) => {
588 if v.is_undef() {
590 return Ok(());
591 }
592 if let Some(e) = v.as_enum_inst() {
593 if e.def.name == *name {
594 Ok(())
595 } else {
596 Err(format!("expected enum {}, got enum {}", name, e.def.name))
597 }
598 } else {
599 Err(format!("expected enum {}, got {}", name, v.type_name()))
600 }
601 }
602 Self::Any => Ok(()),
603 }
604 }
605}
606
607#[derive(Debug, Clone, Serialize, Deserialize)]
608pub struct VarDecl {
609 pub sigil: Sigil,
610 pub name: String,
611 pub initializer: Option<Expr>,
612 pub frozen: bool,
614 pub type_annotation: Option<PerlTypeName>,
616}
617
618#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
619pub enum Sigil {
620 Scalar,
621 Array,
622 Hash,
623 Typeglob,
625}
626
627pub type Block = Vec<Statement>;
628
629#[derive(Debug, Clone, Serialize, Deserialize)]
631pub enum SortComparator {
632 Block(Block),
633 Code(Box<Expr>),
634}
635
636#[derive(Debug, Clone, Serialize, Deserialize)]
640pub struct MatchArm {
641 pub pattern: MatchPattern,
642 #[serde(skip_serializing_if = "Option::is_none")]
644 pub guard: Option<Box<Expr>>,
645 pub body: Expr,
646}
647
648#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
650pub enum RetryBackoff {
651 None,
653 Linear,
655 Exponential,
657}
658
659#[derive(Debug, Clone, Serialize, Deserialize)]
661pub enum MatchPattern {
662 Any,
664 Regex { pattern: String, flags: String },
667 Value(Box<Expr>),
669 Array(Vec<MatchArrayElem>),
671 Hash(Vec<MatchHashPair>),
673 OptionSome(String),
676}
677
678#[derive(Debug, Clone, Serialize, Deserialize)]
679pub enum MatchArrayElem {
680 Expr(Expr),
681 CaptureScalar(String),
684 Rest,
686 RestBind(String),
688}
689
690#[derive(Debug, Clone, Serialize, Deserialize)]
691pub enum MatchHashPair {
692 KeyOnly { key: Expr },
694 Capture { key: Expr, name: String },
696}
697
698#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
699pub enum MagicConstKind {
700 File,
702 Line,
704 Sub,
706}
707
708#[derive(Debug, Clone, Serialize, Deserialize)]
709pub struct Expr {
710 pub kind: ExprKind,
711 pub line: usize,
712}
713
714#[derive(Debug, Clone, Serialize, Deserialize)]
715pub enum ExprKind {
716 Integer(i64),
718 Float(f64),
719 String(String),
720 Bareword(String),
723 Regex(String, String),
724 QW(Vec<String>),
725 Undef,
726 MagicConst(MagicConstKind),
728
729 InterpolatedString(Vec<StringPart>),
731
732 ScalarVar(String),
734 ArrayVar(String),
735 HashVar(String),
736 ArrayElement {
737 array: String,
738 index: Box<Expr>,
739 },
740 HashElement {
741 hash: String,
742 key: Box<Expr>,
743 },
744 ArraySlice {
745 array: String,
746 indices: Vec<Expr>,
747 },
748 HashSlice {
749 hash: String,
750 keys: Vec<Expr>,
751 },
752 HashSliceDeref {
754 container: Box<Expr>,
755 keys: Vec<Expr>,
756 },
757 AnonymousListSlice {
759 source: Box<Expr>,
760 indices: Vec<Expr>,
761 },
762
763 ScalarRef(Box<Expr>),
765 ArrayRef(Vec<Expr>),
766 HashRef(Vec<(Expr, Expr)>),
767 CodeRef {
768 params: Vec<SubSigParam>,
769 body: Block,
770 },
771 SubroutineRef(String),
773 SubroutineCodeRef(String),
775 DynamicSubCodeRef(Box<Expr>),
777 Deref {
778 expr: Box<Expr>,
779 kind: Sigil,
780 },
781 ArrowDeref {
782 expr: Box<Expr>,
783 index: Box<Expr>,
784 kind: DerefKind,
785 },
786
787 BinOp {
789 left: Box<Expr>,
790 op: BinOp,
791 right: Box<Expr>,
792 },
793 UnaryOp {
794 op: UnaryOp,
795 expr: Box<Expr>,
796 },
797 PostfixOp {
798 expr: Box<Expr>,
799 op: PostfixOp,
800 },
801 Assign {
802 target: Box<Expr>,
803 value: Box<Expr>,
804 },
805 CompoundAssign {
806 target: Box<Expr>,
807 op: BinOp,
808 value: Box<Expr>,
809 },
810 Ternary {
811 condition: Box<Expr>,
812 then_expr: Box<Expr>,
813 else_expr: Box<Expr>,
814 },
815
816 Repeat {
818 expr: Box<Expr>,
819 count: Box<Expr>,
820 },
821
822 Range {
825 from: Box<Expr>,
826 to: Box<Expr>,
827 #[serde(default)]
828 exclusive: bool,
829 #[serde(default)]
830 step: Option<Box<Expr>>,
831 },
832
833 SliceRange {
838 #[serde(default)]
839 from: Option<Box<Expr>>,
840 #[serde(default)]
841 to: Option<Box<Expr>>,
842 #[serde(default)]
843 step: Option<Box<Expr>>,
844 },
845
846 MyExpr {
852 keyword: String, decls: Vec<VarDecl>,
854 },
855
856 FuncCall {
858 name: String,
859 args: Vec<Expr>,
860 },
861
862 MethodCall {
864 object: Box<Expr>,
865 method: String,
866 args: Vec<Expr>,
867 #[serde(default)]
869 super_call: bool,
870 },
871 IndirectCall {
874 target: Box<Expr>,
875 args: Vec<Expr>,
876 #[serde(default)]
877 ampersand: bool,
878 #[serde(default)]
880 pass_caller_arglist: bool,
881 },
882 Typeglob(String),
884 TypeglobExpr(Box<Expr>),
886
887 Print {
889 handle: Option<String>,
890 args: Vec<Expr>,
891 },
892 Say {
893 handle: Option<String>,
894 args: Vec<Expr>,
895 },
896 Printf {
897 handle: Option<String>,
898 args: Vec<Expr>,
899 },
900 Die(Vec<Expr>),
901 Warn(Vec<Expr>),
902
903 Match {
905 expr: Box<Expr>,
906 pattern: String,
907 flags: String,
908 scalar_g: bool,
910 #[serde(default = "default_delim")]
911 delim: char,
912 },
913 Substitution {
914 expr: Box<Expr>,
915 pattern: String,
916 replacement: String,
917 flags: String,
918 #[serde(default = "default_delim")]
919 delim: char,
920 },
921 Transliterate {
922 expr: Box<Expr>,
923 from: String,
924 to: String,
925 flags: String,
926 #[serde(default = "default_delim")]
927 delim: char,
928 },
929
930 MapExpr {
932 block: Block,
933 list: Box<Expr>,
934 flatten_array_refs: bool,
936 #[serde(default)]
938 stream: bool,
939 },
940 MapExprComma {
942 expr: Box<Expr>,
943 list: Box<Expr>,
944 flatten_array_refs: bool,
945 #[serde(default)]
946 stream: bool,
947 },
948 GrepExpr {
949 block: Block,
950 list: Box<Expr>,
951 #[serde(default)]
952 keyword: GrepBuiltinKeyword,
953 },
954 GrepExprComma {
956 expr: Box<Expr>,
957 list: Box<Expr>,
958 #[serde(default)]
959 keyword: GrepBuiltinKeyword,
960 },
961 SortExpr {
963 cmp: Option<SortComparator>,
964 list: Box<Expr>,
965 },
966 ReverseExpr(Box<Expr>),
967 Rev(Box<Expr>),
969 JoinExpr {
970 separator: Box<Expr>,
971 list: Box<Expr>,
972 },
973 SplitExpr {
974 pattern: Box<Expr>,
975 string: Box<Expr>,
976 limit: Option<Box<Expr>>,
977 },
978 ForEachExpr {
981 block: Block,
982 list: Box<Expr>,
983 },
984
985 PMapExpr {
987 block: Block,
988 list: Box<Expr>,
989 progress: Option<Box<Expr>>,
991 flat_outputs: bool,
994 #[serde(default, skip_serializing_if = "Option::is_none")]
996 on_cluster: Option<Box<Expr>>,
997 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
1000 stream: bool,
1001 },
1002 PMapChunkedExpr {
1004 chunk_size: Box<Expr>,
1005 block: Block,
1006 list: Box<Expr>,
1007 progress: Option<Box<Expr>>,
1008 },
1009 PGrepExpr {
1010 block: Block,
1011 list: Box<Expr>,
1012 progress: Option<Box<Expr>>,
1014 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
1016 stream: bool,
1017 },
1018 PForExpr {
1020 block: Block,
1021 list: Box<Expr>,
1022 progress: Option<Box<Expr>>,
1023 },
1024 ParLinesExpr {
1026 path: Box<Expr>,
1027 callback: Box<Expr>,
1028 progress: Option<Box<Expr>>,
1029 },
1030 ParWalkExpr {
1032 path: Box<Expr>,
1033 callback: Box<Expr>,
1034 progress: Option<Box<Expr>>,
1035 },
1036 PwatchExpr {
1038 path: Box<Expr>,
1039 callback: Box<Expr>,
1040 },
1041 PSortExpr {
1043 cmp: Option<Block>,
1044 list: Box<Expr>,
1045 progress: Option<Box<Expr>>,
1046 },
1047 ReduceExpr {
1050 block: Block,
1051 list: Box<Expr>,
1052 },
1053 PReduceExpr {
1056 block: Block,
1057 list: Box<Expr>,
1058 progress: Option<Box<Expr>>,
1060 },
1061 PReduceInitExpr {
1066 init: Box<Expr>,
1067 block: Block,
1068 list: Box<Expr>,
1069 progress: Option<Box<Expr>>,
1070 },
1071 PMapReduceExpr {
1073 map_block: Block,
1074 reduce_block: Block,
1075 list: Box<Expr>,
1076 progress: Option<Box<Expr>>,
1077 },
1078 PcacheExpr {
1080 block: Block,
1081 list: Box<Expr>,
1082 progress: Option<Box<Expr>>,
1083 },
1084 PselectExpr {
1086 receivers: Vec<Expr>,
1087 timeout: Option<Box<Expr>>,
1088 },
1089 FanExpr {
1094 count: Option<Box<Expr>>,
1095 block: Block,
1096 progress: Option<Box<Expr>>,
1097 capture: bool,
1098 },
1099
1100 AsyncBlock {
1102 body: Block,
1103 },
1104 SpawnBlock {
1106 body: Block,
1107 },
1108 Trace {
1110 body: Block,
1111 },
1112 Timer {
1114 body: Block,
1115 },
1116 Bench {
1118 body: Block,
1119 times: Box<Expr>,
1120 },
1121 Spinner {
1123 message: Box<Expr>,
1124 body: Block,
1125 },
1126 Await(Box<Expr>),
1128 Slurp(Box<Expr>),
1130 Capture(Box<Expr>),
1132 Qx(Box<Expr>),
1134 FetchUrl(Box<Expr>),
1136
1137 Pchannel {
1139 capacity: Option<Box<Expr>>,
1140 },
1141
1142 Push {
1144 array: Box<Expr>,
1145 values: Vec<Expr>,
1146 },
1147 Pop(Box<Expr>),
1148 Shift(Box<Expr>),
1149 Unshift {
1150 array: Box<Expr>,
1151 values: Vec<Expr>,
1152 },
1153 Splice {
1154 array: Box<Expr>,
1155 offset: Option<Box<Expr>>,
1156 length: Option<Box<Expr>>,
1157 replacement: Vec<Expr>,
1158 },
1159 Delete(Box<Expr>),
1160 Exists(Box<Expr>),
1161 Keys(Box<Expr>),
1162 Values(Box<Expr>),
1163 Each(Box<Expr>),
1164
1165 Chomp(Box<Expr>),
1167 Chop(Box<Expr>),
1168 Length(Box<Expr>),
1169 Substr {
1170 string: Box<Expr>,
1171 offset: Box<Expr>,
1172 length: Option<Box<Expr>>,
1173 replacement: Option<Box<Expr>>,
1174 },
1175 Index {
1176 string: Box<Expr>,
1177 substr: Box<Expr>,
1178 position: Option<Box<Expr>>,
1179 },
1180 Rindex {
1181 string: Box<Expr>,
1182 substr: Box<Expr>,
1183 position: Option<Box<Expr>>,
1184 },
1185 Sprintf {
1186 format: Box<Expr>,
1187 args: Vec<Expr>,
1188 },
1189
1190 Abs(Box<Expr>),
1192 Int(Box<Expr>),
1193 Sqrt(Box<Expr>),
1194 Sin(Box<Expr>),
1195 Cos(Box<Expr>),
1196 Atan2 {
1197 y: Box<Expr>,
1198 x: Box<Expr>,
1199 },
1200 Exp(Box<Expr>),
1201 Log(Box<Expr>),
1202 Rand(Option<Box<Expr>>),
1204 Srand(Option<Box<Expr>>),
1206 Hex(Box<Expr>),
1207 Oct(Box<Expr>),
1208
1209 Lc(Box<Expr>),
1211 Uc(Box<Expr>),
1212 Lcfirst(Box<Expr>),
1213 Ucfirst(Box<Expr>),
1214
1215 Fc(Box<Expr>),
1217 Crypt {
1219 plaintext: Box<Expr>,
1220 salt: Box<Expr>,
1221 },
1222 Pos(Option<Box<Expr>>),
1224 Study(Box<Expr>),
1226
1227 Defined(Box<Expr>),
1229 Ref(Box<Expr>),
1230 ScalarContext(Box<Expr>),
1231
1232 Chr(Box<Expr>),
1234 Ord(Box<Expr>),
1235
1236 OpenMyHandle {
1239 name: String,
1240 },
1241 Open {
1242 handle: Box<Expr>,
1243 mode: Box<Expr>,
1244 file: Option<Box<Expr>>,
1245 },
1246 Close(Box<Expr>),
1247 ReadLine(Option<String>),
1248 Eof(Option<Box<Expr>>),
1249
1250 Opendir {
1251 handle: Box<Expr>,
1252 path: Box<Expr>,
1253 },
1254 Readdir(Box<Expr>),
1255 Closedir(Box<Expr>),
1256 Rewinddir(Box<Expr>),
1257 Telldir(Box<Expr>),
1258 Seekdir {
1259 handle: Box<Expr>,
1260 position: Box<Expr>,
1261 },
1262
1263 FileTest {
1265 op: char,
1266 expr: Box<Expr>,
1267 },
1268
1269 System(Vec<Expr>),
1271 Exec(Vec<Expr>),
1272 Eval(Box<Expr>),
1273 Do(Box<Expr>),
1274 Require(Box<Expr>),
1275 Exit(Option<Box<Expr>>),
1276 Chdir(Box<Expr>),
1277 Mkdir {
1278 path: Box<Expr>,
1279 mode: Option<Box<Expr>>,
1280 },
1281 Unlink(Vec<Expr>),
1282 Rename {
1283 old: Box<Expr>,
1284 new: Box<Expr>,
1285 },
1286 Chmod(Vec<Expr>),
1288 Chown(Vec<Expr>),
1290
1291 Stat(Box<Expr>),
1292 Lstat(Box<Expr>),
1293 Link {
1294 old: Box<Expr>,
1295 new: Box<Expr>,
1296 },
1297 Symlink {
1298 old: Box<Expr>,
1299 new: Box<Expr>,
1300 },
1301 Readlink(Box<Expr>),
1302 Files(Vec<Expr>),
1304 Filesf(Vec<Expr>),
1306 FilesfRecursive(Vec<Expr>),
1308 Dirs(Vec<Expr>),
1310 DirsRecursive(Vec<Expr>),
1312 SymLinks(Vec<Expr>),
1314 Sockets(Vec<Expr>),
1316 Pipes(Vec<Expr>),
1318 BlockDevices(Vec<Expr>),
1320 CharDevices(Vec<Expr>),
1322 Executables(Vec<Expr>),
1324 Glob(Vec<Expr>),
1325 GlobPar {
1328 args: Vec<Expr>,
1329 progress: Option<Box<Expr>>,
1330 },
1331 ParSed {
1333 args: Vec<Expr>,
1334 progress: Option<Box<Expr>>,
1335 },
1336
1337 Bless {
1339 ref_expr: Box<Expr>,
1340 class: Option<Box<Expr>>,
1341 },
1342
1343 Caller(Option<Box<Expr>>),
1345
1346 Wantarray,
1348
1349 List(Vec<Expr>),
1351
1352 PostfixIf {
1354 expr: Box<Expr>,
1355 condition: Box<Expr>,
1356 },
1357 PostfixUnless {
1358 expr: Box<Expr>,
1359 condition: Box<Expr>,
1360 },
1361 PostfixWhile {
1362 expr: Box<Expr>,
1363 condition: Box<Expr>,
1364 },
1365 PostfixUntil {
1366 expr: Box<Expr>,
1367 condition: Box<Expr>,
1368 },
1369 PostfixForeach {
1370 expr: Box<Expr>,
1371 list: Box<Expr>,
1372 },
1373
1374 RetryBlock {
1376 body: Block,
1377 times: Box<Expr>,
1378 backoff: RetryBackoff,
1379 },
1380 RateLimitBlock {
1383 slot: u32,
1384 max: Box<Expr>,
1385 window: Box<Expr>,
1386 body: Block,
1387 },
1388 EveryBlock {
1390 interval: Box<Expr>,
1391 body: Block,
1392 },
1393 GenBlock {
1395 body: Block,
1396 },
1397 Yield(Box<Expr>),
1399
1400 AlgebraicMatch {
1402 subject: Box<Expr>,
1403 arms: Vec<MatchArm>,
1404 },
1405}
1406
1407#[derive(Debug, Clone, Serialize, Deserialize)]
1408pub enum StringPart {
1409 Literal(String),
1410 ScalarVar(String),
1411 ArrayVar(String),
1412 Expr(Expr),
1413}
1414
1415#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
1416pub enum DerefKind {
1417 Array,
1418 Hash,
1419 Call,
1420}
1421
1422#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
1423pub enum BinOp {
1424 Add,
1425 Sub,
1426 Mul,
1427 Div,
1428 Mod,
1429 Pow,
1430 Concat,
1431 NumEq,
1432 NumNe,
1433 NumLt,
1434 NumGt,
1435 NumLe,
1436 NumGe,
1437 Spaceship,
1438 StrEq,
1439 StrNe,
1440 StrLt,
1441 StrGt,
1442 StrLe,
1443 StrGe,
1444 StrCmp,
1445 LogAnd,
1446 LogOr,
1447 DefinedOr,
1448 BitAnd,
1449 BitOr,
1450 BitXor,
1451 ShiftLeft,
1452 ShiftRight,
1453 LogAndWord,
1454 LogOrWord,
1455 BindMatch,
1456 BindNotMatch,
1457}
1458
1459#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
1460pub enum UnaryOp {
1461 Negate,
1462 LogNot,
1463 BitNot,
1464 LogNotWord,
1465 PreIncrement,
1466 PreDecrement,
1467 Ref,
1468}
1469
1470#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
1471pub enum PostfixOp {
1472 Increment,
1473 Decrement,
1474}
1475
1476#[cfg(test)]
1477mod tests {
1478 use super::*;
1479
1480 #[test]
1481 fn binop_deref_kind_distinct() {
1482 assert_ne!(BinOp::Add, BinOp::Sub);
1483 assert_eq!(DerefKind::Call, DerefKind::Call);
1484 }
1485
1486 #[test]
1487 fn sigil_variants_exhaustive_in_tests() {
1488 let all = [Sigil::Scalar, Sigil::Array, Sigil::Hash];
1489 assert_eq!(all.len(), 3);
1490 }
1491
1492 #[test]
1493 fn program_empty_roundtrip_clone() {
1494 let p = Program { statements: vec![] };
1495 assert!(p.clone().statements.is_empty());
1496 }
1497
1498 #[test]
1499 fn program_serializes_to_json() {
1500 let p = crate::parse("1+2;").expect("parse");
1501 let s = serde_json::to_string(&p).expect("json");
1502 assert!(s.contains("\"statements\""));
1503 assert!(s.contains("BinOp"));
1504 }
1505}