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 MyExpr {
839 keyword: String, decls: Vec<VarDecl>,
841 },
842
843 FuncCall {
845 name: String,
846 args: Vec<Expr>,
847 },
848
849 MethodCall {
851 object: Box<Expr>,
852 method: String,
853 args: Vec<Expr>,
854 #[serde(default)]
856 super_call: bool,
857 },
858 IndirectCall {
861 target: Box<Expr>,
862 args: Vec<Expr>,
863 #[serde(default)]
864 ampersand: bool,
865 #[serde(default)]
867 pass_caller_arglist: bool,
868 },
869 Typeglob(String),
871 TypeglobExpr(Box<Expr>),
873
874 Print {
876 handle: Option<String>,
877 args: Vec<Expr>,
878 },
879 Say {
880 handle: Option<String>,
881 args: Vec<Expr>,
882 },
883 Printf {
884 handle: Option<String>,
885 args: Vec<Expr>,
886 },
887 Die(Vec<Expr>),
888 Warn(Vec<Expr>),
889
890 Match {
892 expr: Box<Expr>,
893 pattern: String,
894 flags: String,
895 scalar_g: bool,
897 #[serde(default = "default_delim")]
898 delim: char,
899 },
900 Substitution {
901 expr: Box<Expr>,
902 pattern: String,
903 replacement: String,
904 flags: String,
905 #[serde(default = "default_delim")]
906 delim: char,
907 },
908 Transliterate {
909 expr: Box<Expr>,
910 from: String,
911 to: String,
912 flags: String,
913 #[serde(default = "default_delim")]
914 delim: char,
915 },
916
917 MapExpr {
919 block: Block,
920 list: Box<Expr>,
921 flatten_array_refs: bool,
923 #[serde(default)]
925 stream: bool,
926 },
927 MapExprComma {
929 expr: Box<Expr>,
930 list: Box<Expr>,
931 flatten_array_refs: bool,
932 #[serde(default)]
933 stream: bool,
934 },
935 GrepExpr {
936 block: Block,
937 list: Box<Expr>,
938 #[serde(default)]
939 keyword: GrepBuiltinKeyword,
940 },
941 GrepExprComma {
943 expr: Box<Expr>,
944 list: Box<Expr>,
945 #[serde(default)]
946 keyword: GrepBuiltinKeyword,
947 },
948 SortExpr {
950 cmp: Option<SortComparator>,
951 list: Box<Expr>,
952 },
953 ReverseExpr(Box<Expr>),
954 Rev(Box<Expr>),
956 JoinExpr {
957 separator: Box<Expr>,
958 list: Box<Expr>,
959 },
960 SplitExpr {
961 pattern: Box<Expr>,
962 string: Box<Expr>,
963 limit: Option<Box<Expr>>,
964 },
965 ForEachExpr {
968 block: Block,
969 list: Box<Expr>,
970 },
971
972 PMapExpr {
974 block: Block,
975 list: Box<Expr>,
976 progress: Option<Box<Expr>>,
978 flat_outputs: bool,
981 #[serde(default, skip_serializing_if = "Option::is_none")]
983 on_cluster: Option<Box<Expr>>,
984 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
987 stream: bool,
988 },
989 PMapChunkedExpr {
991 chunk_size: Box<Expr>,
992 block: Block,
993 list: Box<Expr>,
994 progress: Option<Box<Expr>>,
995 },
996 PGrepExpr {
997 block: Block,
998 list: Box<Expr>,
999 progress: Option<Box<Expr>>,
1001 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
1003 stream: bool,
1004 },
1005 PForExpr {
1007 block: Block,
1008 list: Box<Expr>,
1009 progress: Option<Box<Expr>>,
1010 },
1011 ParLinesExpr {
1013 path: Box<Expr>,
1014 callback: Box<Expr>,
1015 progress: Option<Box<Expr>>,
1016 },
1017 ParWalkExpr {
1019 path: Box<Expr>,
1020 callback: Box<Expr>,
1021 progress: Option<Box<Expr>>,
1022 },
1023 PwatchExpr {
1025 path: Box<Expr>,
1026 callback: Box<Expr>,
1027 },
1028 PSortExpr {
1030 cmp: Option<Block>,
1031 list: Box<Expr>,
1032 progress: Option<Box<Expr>>,
1033 },
1034 ReduceExpr {
1037 block: Block,
1038 list: Box<Expr>,
1039 },
1040 PReduceExpr {
1043 block: Block,
1044 list: Box<Expr>,
1045 progress: Option<Box<Expr>>,
1047 },
1048 PReduceInitExpr {
1053 init: Box<Expr>,
1054 block: Block,
1055 list: Box<Expr>,
1056 progress: Option<Box<Expr>>,
1057 },
1058 PMapReduceExpr {
1060 map_block: Block,
1061 reduce_block: Block,
1062 list: Box<Expr>,
1063 progress: Option<Box<Expr>>,
1064 },
1065 PcacheExpr {
1067 block: Block,
1068 list: Box<Expr>,
1069 progress: Option<Box<Expr>>,
1070 },
1071 PselectExpr {
1073 receivers: Vec<Expr>,
1074 timeout: Option<Box<Expr>>,
1075 },
1076 FanExpr {
1081 count: Option<Box<Expr>>,
1082 block: Block,
1083 progress: Option<Box<Expr>>,
1084 capture: bool,
1085 },
1086
1087 AsyncBlock {
1089 body: Block,
1090 },
1091 SpawnBlock {
1093 body: Block,
1094 },
1095 Trace {
1097 body: Block,
1098 },
1099 Timer {
1101 body: Block,
1102 },
1103 Bench {
1105 body: Block,
1106 times: Box<Expr>,
1107 },
1108 Spinner {
1110 message: Box<Expr>,
1111 body: Block,
1112 },
1113 Await(Box<Expr>),
1115 Slurp(Box<Expr>),
1117 Capture(Box<Expr>),
1119 Qx(Box<Expr>),
1121 FetchUrl(Box<Expr>),
1123
1124 Pchannel {
1126 capacity: Option<Box<Expr>>,
1127 },
1128
1129 Push {
1131 array: Box<Expr>,
1132 values: Vec<Expr>,
1133 },
1134 Pop(Box<Expr>),
1135 Shift(Box<Expr>),
1136 Unshift {
1137 array: Box<Expr>,
1138 values: Vec<Expr>,
1139 },
1140 Splice {
1141 array: Box<Expr>,
1142 offset: Option<Box<Expr>>,
1143 length: Option<Box<Expr>>,
1144 replacement: Vec<Expr>,
1145 },
1146 Delete(Box<Expr>),
1147 Exists(Box<Expr>),
1148 Keys(Box<Expr>),
1149 Values(Box<Expr>),
1150 Each(Box<Expr>),
1151
1152 Chomp(Box<Expr>),
1154 Chop(Box<Expr>),
1155 Length(Box<Expr>),
1156 Substr {
1157 string: Box<Expr>,
1158 offset: Box<Expr>,
1159 length: Option<Box<Expr>>,
1160 replacement: Option<Box<Expr>>,
1161 },
1162 Index {
1163 string: Box<Expr>,
1164 substr: Box<Expr>,
1165 position: Option<Box<Expr>>,
1166 },
1167 Rindex {
1168 string: Box<Expr>,
1169 substr: Box<Expr>,
1170 position: Option<Box<Expr>>,
1171 },
1172 Sprintf {
1173 format: Box<Expr>,
1174 args: Vec<Expr>,
1175 },
1176
1177 Abs(Box<Expr>),
1179 Int(Box<Expr>),
1180 Sqrt(Box<Expr>),
1181 Sin(Box<Expr>),
1182 Cos(Box<Expr>),
1183 Atan2 {
1184 y: Box<Expr>,
1185 x: Box<Expr>,
1186 },
1187 Exp(Box<Expr>),
1188 Log(Box<Expr>),
1189 Rand(Option<Box<Expr>>),
1191 Srand(Option<Box<Expr>>),
1193 Hex(Box<Expr>),
1194 Oct(Box<Expr>),
1195
1196 Lc(Box<Expr>),
1198 Uc(Box<Expr>),
1199 Lcfirst(Box<Expr>),
1200 Ucfirst(Box<Expr>),
1201
1202 Fc(Box<Expr>),
1204 Crypt {
1206 plaintext: Box<Expr>,
1207 salt: Box<Expr>,
1208 },
1209 Pos(Option<Box<Expr>>),
1211 Study(Box<Expr>),
1213
1214 Defined(Box<Expr>),
1216 Ref(Box<Expr>),
1217 ScalarContext(Box<Expr>),
1218
1219 Chr(Box<Expr>),
1221 Ord(Box<Expr>),
1222
1223 OpenMyHandle {
1226 name: String,
1227 },
1228 Open {
1229 handle: Box<Expr>,
1230 mode: Box<Expr>,
1231 file: Option<Box<Expr>>,
1232 },
1233 Close(Box<Expr>),
1234 ReadLine(Option<String>),
1235 Eof(Option<Box<Expr>>),
1236
1237 Opendir {
1238 handle: Box<Expr>,
1239 path: Box<Expr>,
1240 },
1241 Readdir(Box<Expr>),
1242 Closedir(Box<Expr>),
1243 Rewinddir(Box<Expr>),
1244 Telldir(Box<Expr>),
1245 Seekdir {
1246 handle: Box<Expr>,
1247 position: Box<Expr>,
1248 },
1249
1250 FileTest {
1252 op: char,
1253 expr: Box<Expr>,
1254 },
1255
1256 System(Vec<Expr>),
1258 Exec(Vec<Expr>),
1259 Eval(Box<Expr>),
1260 Do(Box<Expr>),
1261 Require(Box<Expr>),
1262 Exit(Option<Box<Expr>>),
1263 Chdir(Box<Expr>),
1264 Mkdir {
1265 path: Box<Expr>,
1266 mode: Option<Box<Expr>>,
1267 },
1268 Unlink(Vec<Expr>),
1269 Rename {
1270 old: Box<Expr>,
1271 new: Box<Expr>,
1272 },
1273 Chmod(Vec<Expr>),
1275 Chown(Vec<Expr>),
1277
1278 Stat(Box<Expr>),
1279 Lstat(Box<Expr>),
1280 Link {
1281 old: Box<Expr>,
1282 new: Box<Expr>,
1283 },
1284 Symlink {
1285 old: Box<Expr>,
1286 new: Box<Expr>,
1287 },
1288 Readlink(Box<Expr>),
1289 Files(Vec<Expr>),
1291 Filesf(Vec<Expr>),
1293 FilesfRecursive(Vec<Expr>),
1295 Dirs(Vec<Expr>),
1297 DirsRecursive(Vec<Expr>),
1299 SymLinks(Vec<Expr>),
1301 Sockets(Vec<Expr>),
1303 Pipes(Vec<Expr>),
1305 BlockDevices(Vec<Expr>),
1307 CharDevices(Vec<Expr>),
1309 Executables(Vec<Expr>),
1311 Glob(Vec<Expr>),
1312 GlobPar {
1315 args: Vec<Expr>,
1316 progress: Option<Box<Expr>>,
1317 },
1318 ParSed {
1320 args: Vec<Expr>,
1321 progress: Option<Box<Expr>>,
1322 },
1323
1324 Bless {
1326 ref_expr: Box<Expr>,
1327 class: Option<Box<Expr>>,
1328 },
1329
1330 Caller(Option<Box<Expr>>),
1332
1333 Wantarray,
1335
1336 List(Vec<Expr>),
1338
1339 PostfixIf {
1341 expr: Box<Expr>,
1342 condition: Box<Expr>,
1343 },
1344 PostfixUnless {
1345 expr: Box<Expr>,
1346 condition: Box<Expr>,
1347 },
1348 PostfixWhile {
1349 expr: Box<Expr>,
1350 condition: Box<Expr>,
1351 },
1352 PostfixUntil {
1353 expr: Box<Expr>,
1354 condition: Box<Expr>,
1355 },
1356 PostfixForeach {
1357 expr: Box<Expr>,
1358 list: Box<Expr>,
1359 },
1360
1361 RetryBlock {
1363 body: Block,
1364 times: Box<Expr>,
1365 backoff: RetryBackoff,
1366 },
1367 RateLimitBlock {
1370 slot: u32,
1371 max: Box<Expr>,
1372 window: Box<Expr>,
1373 body: Block,
1374 },
1375 EveryBlock {
1377 interval: Box<Expr>,
1378 body: Block,
1379 },
1380 GenBlock {
1382 body: Block,
1383 },
1384 Yield(Box<Expr>),
1386
1387 AlgebraicMatch {
1389 subject: Box<Expr>,
1390 arms: Vec<MatchArm>,
1391 },
1392}
1393
1394#[derive(Debug, Clone, Serialize, Deserialize)]
1395pub enum StringPart {
1396 Literal(String),
1397 ScalarVar(String),
1398 ArrayVar(String),
1399 Expr(Expr),
1400}
1401
1402#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
1403pub enum DerefKind {
1404 Array,
1405 Hash,
1406 Call,
1407}
1408
1409#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
1410pub enum BinOp {
1411 Add,
1412 Sub,
1413 Mul,
1414 Div,
1415 Mod,
1416 Pow,
1417 Concat,
1418 NumEq,
1419 NumNe,
1420 NumLt,
1421 NumGt,
1422 NumLe,
1423 NumGe,
1424 Spaceship,
1425 StrEq,
1426 StrNe,
1427 StrLt,
1428 StrGt,
1429 StrLe,
1430 StrGe,
1431 StrCmp,
1432 LogAnd,
1433 LogOr,
1434 DefinedOr,
1435 BitAnd,
1436 BitOr,
1437 BitXor,
1438 ShiftLeft,
1439 ShiftRight,
1440 LogAndWord,
1441 LogOrWord,
1442 BindMatch,
1443 BindNotMatch,
1444}
1445
1446#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
1447pub enum UnaryOp {
1448 Negate,
1449 LogNot,
1450 BitNot,
1451 LogNotWord,
1452 PreIncrement,
1453 PreDecrement,
1454 Ref,
1455}
1456
1457#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
1458pub enum PostfixOp {
1459 Increment,
1460 Decrement,
1461}
1462
1463#[cfg(test)]
1464mod tests {
1465 use super::*;
1466
1467 #[test]
1468 fn binop_deref_kind_distinct() {
1469 assert_ne!(BinOp::Add, BinOp::Sub);
1470 assert_eq!(DerefKind::Call, DerefKind::Call);
1471 }
1472
1473 #[test]
1474 fn sigil_variants_exhaustive_in_tests() {
1475 let all = [Sigil::Scalar, Sigil::Array, Sigil::Hash];
1476 assert_eq!(all.len(), 3);
1477 }
1478
1479 #[test]
1480 fn program_empty_roundtrip_clone() {
1481 let p = Program { statements: vec![] };
1482 assert!(p.clone().statements.is_empty());
1483 }
1484
1485 #[test]
1486 fn program_serializes_to_json() {
1487 let p = crate::parse("1+2;").expect("parse");
1488 let s = serde_json::to_string(&p).expect("json");
1489 assert!(s.contains("\"statements\""));
1490 assert!(s.contains("BinOp"));
1491 }
1492}