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>),
67 Array(String),
69 Hash(String),
71 ArrayDestruct(Vec<MatchArrayElem>),
73 HashDestruct(Vec<(String, String)>),
75}
76
77#[derive(Debug, Clone, Serialize, Deserialize)]
78pub enum StmtKind {
79 Expression(Expr),
80 If {
81 condition: Expr,
82 body: Block,
83 elsifs: Vec<(Expr, Block)>,
84 else_block: Option<Block>,
85 },
86 Unless {
87 condition: Expr,
88 body: Block,
89 else_block: Option<Block>,
90 },
91 While {
92 condition: Expr,
93 body: Block,
94 label: Option<String>,
95 continue_block: Option<Block>,
97 },
98 Until {
99 condition: Expr,
100 body: Block,
101 label: Option<String>,
102 continue_block: Option<Block>,
103 },
104 DoWhile {
105 body: Block,
106 condition: Expr,
107 },
108 For {
109 init: Option<Box<Statement>>,
110 condition: Option<Expr>,
111 step: Option<Expr>,
112 body: Block,
113 label: Option<String>,
114 continue_block: Option<Block>,
115 },
116 Foreach {
117 var: String,
118 list: Expr,
119 body: Block,
120 label: Option<String>,
121 continue_block: Option<Block>,
122 },
123 SubDecl {
124 name: String,
125 params: Vec<SubSigParam>,
126 body: Block,
127 prototype: Option<String>,
130 },
131 Package {
132 name: String,
133 },
134 Use {
135 module: String,
136 imports: Vec<Expr>,
137 },
138 UsePerlVersion {
140 version: f64,
141 },
142 UseOverload {
144 pairs: Vec<(String, String)>,
145 },
146 No {
147 module: String,
148 imports: Vec<Expr>,
149 },
150 Return(Option<Expr>),
151 Last(Option<String>),
152 Next(Option<String>),
153 Redo(Option<String>),
154 My(Vec<VarDecl>),
155 Our(Vec<VarDecl>),
156 Local(Vec<VarDecl>),
157 State(Vec<VarDecl>),
159 LocalExpr {
161 target: Expr,
162 initializer: Option<Expr>,
163 },
164 MySync(Vec<VarDecl>),
166 Block(Block),
168 StmtGroup(Block),
170 Begin(Block),
172 End(Block),
174 UnitCheck(Block),
176 Check(Block),
178 Init(Block),
180 Empty,
182 Goto {
184 target: Box<Expr>,
185 },
186 Continue(Block),
188 StructDecl {
190 def: StructDef,
191 },
192 EnumDecl {
194 def: EnumDef,
195 },
196 ClassDecl {
198 def: ClassDef,
199 },
200 TraitDecl {
202 def: TraitDef,
203 },
204 EvalTimeout {
206 timeout: Expr,
207 body: Block,
208 },
209 TryCatch {
212 try_block: Block,
213 catch_var: String,
214 catch_block: Block,
215 finally_block: Option<Block>,
216 },
217 Given {
219 topic: Expr,
220 body: Block,
221 },
222 When {
224 cond: Expr,
225 body: Block,
226 },
227 DefaultCase {
229 body: Block,
230 },
231 Tie {
233 target: TieTarget,
234 class: Expr,
235 args: Vec<Expr>,
236 },
237 FormatDecl {
239 name: String,
240 lines: Vec<String>,
241 },
242}
243
244#[derive(Debug, Clone, Serialize, Deserialize)]
246pub enum TieTarget {
247 Hash(String),
248 Array(String),
249 Scalar(String),
250}
251
252#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
254pub enum PerlTypeName {
255 Int,
256 Str,
257 Float,
258 Bool,
259 Array,
260 Hash,
261 Ref,
262 Struct(String),
264 Enum(String),
266 Any,
268}
269
270#[derive(Debug, Clone, Serialize, Deserialize)]
272pub struct StructField {
273 pub name: String,
274 pub ty: PerlTypeName,
275 #[serde(skip_serializing_if = "Option::is_none")]
277 pub default: Option<Expr>,
278}
279
280#[derive(Debug, Clone, Serialize, Deserialize)]
282pub struct StructMethod {
283 pub name: String,
284 pub params: Vec<SubSigParam>,
285 pub body: Block,
286}
287
288#[derive(Debug, Clone, Serialize, Deserialize)]
290pub struct EnumVariant {
291 pub name: String,
292 pub ty: Option<PerlTypeName>,
294}
295
296#[derive(Debug, Clone, Serialize, Deserialize)]
298pub struct EnumDef {
299 pub name: String,
300 pub variants: Vec<EnumVariant>,
301}
302
303impl EnumDef {
304 #[inline]
305 pub fn variant_index(&self, name: &str) -> Option<usize> {
306 self.variants.iter().position(|v| v.name == name)
307 }
308
309 #[inline]
310 pub fn variant(&self, name: &str) -> Option<&EnumVariant> {
311 self.variants.iter().find(|v| v.name == name)
312 }
313}
314
315#[derive(Debug, Clone, Serialize, Deserialize)]
317pub struct StructDef {
318 pub name: String,
319 pub fields: Vec<StructField>,
320 #[serde(default, skip_serializing_if = "Vec::is_empty")]
322 pub methods: Vec<StructMethod>,
323}
324
325#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
327pub enum Visibility {
328 #[default]
329 Public,
330 Private,
331 Protected,
332}
333
334#[derive(Debug, Clone, Serialize, Deserialize)]
336pub struct ClassField {
337 pub name: String,
338 pub ty: PerlTypeName,
339 pub visibility: Visibility,
340 #[serde(skip_serializing_if = "Option::is_none")]
341 pub default: Option<Expr>,
342}
343
344#[derive(Debug, Clone, Serialize, Deserialize)]
346pub struct ClassMethod {
347 pub name: String,
348 pub params: Vec<SubSigParam>,
349 pub body: Option<Block>,
350 pub visibility: Visibility,
351 pub is_static: bool,
352 #[serde(default, skip_serializing_if = "is_false")]
353 pub is_final: bool,
354}
355
356#[derive(Debug, Clone, Serialize, Deserialize)]
358pub struct TraitDef {
359 pub name: String,
360 pub methods: Vec<ClassMethod>,
361}
362
363impl TraitDef {
364 #[inline]
365 pub fn method(&self, name: &str) -> Option<&ClassMethod> {
366 self.methods.iter().find(|m| m.name == name)
367 }
368
369 #[inline]
370 pub fn required_methods(&self) -> impl Iterator<Item = &ClassMethod> {
371 self.methods.iter().filter(|m| m.body.is_none())
372 }
373}
374
375#[derive(Debug, Clone, Serialize, Deserialize)]
377pub struct ClassStaticField {
378 pub name: String,
379 pub ty: PerlTypeName,
380 pub visibility: Visibility,
381 #[serde(skip_serializing_if = "Option::is_none")]
382 pub default: Option<Expr>,
383}
384
385#[derive(Debug, Clone, Serialize, Deserialize)]
387pub struct ClassDef {
388 pub name: String,
389 #[serde(default, skip_serializing_if = "is_false")]
390 pub is_abstract: bool,
391 #[serde(default, skip_serializing_if = "is_false")]
392 pub is_final: bool,
393 #[serde(default, skip_serializing_if = "Vec::is_empty")]
394 pub extends: Vec<String>,
395 #[serde(default, skip_serializing_if = "Vec::is_empty")]
396 pub implements: Vec<String>,
397 pub fields: Vec<ClassField>,
398 pub methods: Vec<ClassMethod>,
399 #[serde(default, skip_serializing_if = "Vec::is_empty")]
400 pub static_fields: Vec<ClassStaticField>,
401}
402
403fn is_false(v: &bool) -> bool {
404 !*v
405}
406
407impl ClassDef {
408 #[inline]
409 pub fn field_index(&self, name: &str) -> Option<usize> {
410 self.fields.iter().position(|f| f.name == name)
411 }
412
413 #[inline]
414 pub fn field(&self, name: &str) -> Option<&ClassField> {
415 self.fields.iter().find(|f| f.name == name)
416 }
417
418 #[inline]
419 pub fn method(&self, name: &str) -> Option<&ClassMethod> {
420 self.methods.iter().find(|m| m.name == name)
421 }
422
423 #[inline]
424 pub fn static_methods(&self) -> impl Iterator<Item = &ClassMethod> {
425 self.methods.iter().filter(|m| m.is_static)
426 }
427
428 #[inline]
429 pub fn instance_methods(&self) -> impl Iterator<Item = &ClassMethod> {
430 self.methods.iter().filter(|m| !m.is_static)
431 }
432}
433
434impl StructDef {
435 #[inline]
436 pub fn field_index(&self, name: &str) -> Option<usize> {
437 self.fields.iter().position(|f| f.name == name)
438 }
439
440 #[inline]
442 pub fn field_type(&self, name: &str) -> Option<&PerlTypeName> {
443 self.fields.iter().find(|f| f.name == name).map(|f| &f.ty)
444 }
445
446 #[inline]
448 pub fn method(&self, name: &str) -> Option<&StructMethod> {
449 self.methods.iter().find(|m| m.name == name)
450 }
451}
452
453impl PerlTypeName {
454 #[inline]
456 pub fn from_byte(b: u8) -> Option<Self> {
457 match b {
458 0 => Some(Self::Int),
459 1 => Some(Self::Str),
460 2 => Some(Self::Float),
461 3 => Some(Self::Bool),
462 4 => Some(Self::Array),
463 5 => Some(Self::Hash),
464 6 => Some(Self::Ref),
465 7 => Some(Self::Any),
466 _ => None,
467 }
468 }
469
470 #[inline]
472 pub fn as_byte(&self) -> Option<u8> {
473 match self {
474 Self::Int => Some(0),
475 Self::Str => Some(1),
476 Self::Float => Some(2),
477 Self::Bool => Some(3),
478 Self::Array => Some(4),
479 Self::Hash => Some(5),
480 Self::Ref => Some(6),
481 Self::Any => Some(7),
482 Self::Struct(_) | Self::Enum(_) => None,
483 }
484 }
485
486 pub fn display_name(&self) -> String {
488 match self {
489 Self::Int => "Int".to_string(),
490 Self::Str => "Str".to_string(),
491 Self::Float => "Float".to_string(),
492 Self::Bool => "Bool".to_string(),
493 Self::Array => "Array".to_string(),
494 Self::Hash => "Hash".to_string(),
495 Self::Ref => "Ref".to_string(),
496 Self::Any => "Any".to_string(),
497 Self::Struct(name) => name.clone(),
498 Self::Enum(name) => name.clone(),
499 }
500 }
501
502 pub fn check_value(&self, v: &crate::value::PerlValue) -> Result<(), String> {
504 match self {
505 Self::Int => {
506 if v.is_integer_like() {
507 Ok(())
508 } else {
509 Err(format!("expected Int (INTEGER), got {}", v.type_name()))
510 }
511 }
512 Self::Str => {
513 if v.is_string_like() {
514 Ok(())
515 } else {
516 Err(format!("expected Str (STRING), got {}", v.type_name()))
517 }
518 }
519 Self::Float => {
520 if v.is_integer_like() || v.is_float_like() {
521 Ok(())
522 } else {
523 Err(format!(
524 "expected Float (INTEGER or FLOAT), got {}",
525 v.type_name()
526 ))
527 }
528 }
529 Self::Bool => Ok(()),
530 Self::Array => {
531 if v.as_array_vec().is_some() || v.as_array_ref().is_some() {
532 Ok(())
533 } else {
534 Err(format!("expected Array, got {}", v.type_name()))
535 }
536 }
537 Self::Hash => {
538 if v.as_hash_map().is_some() || v.as_hash_ref().is_some() {
539 Ok(())
540 } else {
541 Err(format!("expected Hash, got {}", v.type_name()))
542 }
543 }
544 Self::Ref => {
545 if v.as_scalar_ref().is_some()
546 || v.as_array_ref().is_some()
547 || v.as_hash_ref().is_some()
548 || v.as_code_ref().is_some()
549 {
550 Ok(())
551 } else {
552 Err(format!("expected Ref, got {}", v.type_name()))
553 }
554 }
555 Self::Struct(name) => {
556 if let Some(s) = v.as_struct_inst() {
557 if s.def.name == *name {
558 Ok(())
559 } else {
560 Err(format!(
561 "expected struct {}, got struct {}",
562 name, s.def.name
563 ))
564 }
565 } else if let Some(e) = v.as_enum_inst() {
566 if e.def.name == *name {
567 Ok(())
568 } else {
569 Err(format!("expected {}, got enum {}", name, e.def.name))
570 }
571 } else {
572 Err(format!("expected {}, got {}", name, v.type_name()))
573 }
574 }
575 Self::Enum(name) => {
576 if let Some(e) = v.as_enum_inst() {
577 if e.def.name == *name {
578 Ok(())
579 } else {
580 Err(format!("expected enum {}, got enum {}", name, e.def.name))
581 }
582 } else {
583 Err(format!("expected enum {}, got {}", name, v.type_name()))
584 }
585 }
586 Self::Any => Ok(()),
587 }
588 }
589}
590
591#[derive(Debug, Clone, Serialize, Deserialize)]
592pub struct VarDecl {
593 pub sigil: Sigil,
594 pub name: String,
595 pub initializer: Option<Expr>,
596 pub frozen: bool,
598 pub type_annotation: Option<PerlTypeName>,
600}
601
602#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
603pub enum Sigil {
604 Scalar,
605 Array,
606 Hash,
607 Typeglob,
609}
610
611pub type Block = Vec<Statement>;
612
613#[derive(Debug, Clone, Serialize, Deserialize)]
615pub enum SortComparator {
616 Block(Block),
617 Code(Box<Expr>),
618}
619
620#[derive(Debug, Clone, Serialize, Deserialize)]
624pub struct MatchArm {
625 pub pattern: MatchPattern,
626 #[serde(skip_serializing_if = "Option::is_none")]
628 pub guard: Option<Box<Expr>>,
629 pub body: Expr,
630}
631
632#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
634pub enum RetryBackoff {
635 None,
637 Linear,
639 Exponential,
641}
642
643#[derive(Debug, Clone, Serialize, Deserialize)]
645pub enum MatchPattern {
646 Any,
648 Regex { pattern: String, flags: String },
651 Value(Box<Expr>),
653 Array(Vec<MatchArrayElem>),
655 Hash(Vec<MatchHashPair>),
657 OptionSome(String),
660}
661
662#[derive(Debug, Clone, Serialize, Deserialize)]
663pub enum MatchArrayElem {
664 Expr(Expr),
665 CaptureScalar(String),
668 Rest,
670 RestBind(String),
672}
673
674#[derive(Debug, Clone, Serialize, Deserialize)]
675pub enum MatchHashPair {
676 KeyOnly { key: Expr },
678 Capture { key: Expr, name: String },
680}
681
682#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
683pub enum MagicConstKind {
684 File,
686 Line,
688 Sub,
690}
691
692#[derive(Debug, Clone, Serialize, Deserialize)]
693pub struct Expr {
694 pub kind: ExprKind,
695 pub line: usize,
696}
697
698#[derive(Debug, Clone, Serialize, Deserialize)]
699pub enum ExprKind {
700 Integer(i64),
702 Float(f64),
703 String(String),
704 Bareword(String),
707 Regex(String, String),
708 QW(Vec<String>),
709 Undef,
710 MagicConst(MagicConstKind),
712
713 InterpolatedString(Vec<StringPart>),
715
716 ScalarVar(String),
718 ArrayVar(String),
719 HashVar(String),
720 ArrayElement {
721 array: String,
722 index: Box<Expr>,
723 },
724 HashElement {
725 hash: String,
726 key: Box<Expr>,
727 },
728 ArraySlice {
729 array: String,
730 indices: Vec<Expr>,
731 },
732 HashSlice {
733 hash: String,
734 keys: Vec<Expr>,
735 },
736 HashSliceDeref {
738 container: Box<Expr>,
739 keys: Vec<Expr>,
740 },
741 AnonymousListSlice {
743 source: Box<Expr>,
744 indices: Vec<Expr>,
745 },
746
747 ScalarRef(Box<Expr>),
749 ArrayRef(Vec<Expr>),
750 HashRef(Vec<(Expr, Expr)>),
751 CodeRef {
752 params: Vec<SubSigParam>,
753 body: Block,
754 },
755 SubroutineRef(String),
757 SubroutineCodeRef(String),
759 DynamicSubCodeRef(Box<Expr>),
761 Deref {
762 expr: Box<Expr>,
763 kind: Sigil,
764 },
765 ArrowDeref {
766 expr: Box<Expr>,
767 index: Box<Expr>,
768 kind: DerefKind,
769 },
770
771 BinOp {
773 left: Box<Expr>,
774 op: BinOp,
775 right: Box<Expr>,
776 },
777 UnaryOp {
778 op: UnaryOp,
779 expr: Box<Expr>,
780 },
781 PostfixOp {
782 expr: Box<Expr>,
783 op: PostfixOp,
784 },
785 Assign {
786 target: Box<Expr>,
787 value: Box<Expr>,
788 },
789 CompoundAssign {
790 target: Box<Expr>,
791 op: BinOp,
792 value: Box<Expr>,
793 },
794 Ternary {
795 condition: Box<Expr>,
796 then_expr: Box<Expr>,
797 else_expr: Box<Expr>,
798 },
799
800 Repeat {
802 expr: Box<Expr>,
803 count: Box<Expr>,
804 },
805
806 Range {
808 from: Box<Expr>,
809 to: Box<Expr>,
810 #[serde(default)]
811 exclusive: bool,
812 },
813
814 MyExpr {
820 keyword: String, decls: Vec<VarDecl>,
822 },
823
824 FuncCall {
826 name: String,
827 args: Vec<Expr>,
828 },
829
830 MethodCall {
832 object: Box<Expr>,
833 method: String,
834 args: Vec<Expr>,
835 #[serde(default)]
837 super_call: bool,
838 },
839 IndirectCall {
842 target: Box<Expr>,
843 args: Vec<Expr>,
844 #[serde(default)]
845 ampersand: bool,
846 #[serde(default)]
848 pass_caller_arglist: bool,
849 },
850 Typeglob(String),
852 TypeglobExpr(Box<Expr>),
854
855 Print {
857 handle: Option<String>,
858 args: Vec<Expr>,
859 },
860 Say {
861 handle: Option<String>,
862 args: Vec<Expr>,
863 },
864 Printf {
865 handle: Option<String>,
866 args: Vec<Expr>,
867 },
868 Die(Vec<Expr>),
869 Warn(Vec<Expr>),
870
871 Match {
873 expr: Box<Expr>,
874 pattern: String,
875 flags: String,
876 scalar_g: bool,
878 #[serde(default = "default_delim")]
879 delim: char,
880 },
881 Substitution {
882 expr: Box<Expr>,
883 pattern: String,
884 replacement: String,
885 flags: String,
886 #[serde(default = "default_delim")]
887 delim: char,
888 },
889 Transliterate {
890 expr: Box<Expr>,
891 from: String,
892 to: String,
893 flags: String,
894 #[serde(default = "default_delim")]
895 delim: char,
896 },
897
898 MapExpr {
900 block: Block,
901 list: Box<Expr>,
902 flatten_array_refs: bool,
904 #[serde(default)]
906 stream: bool,
907 },
908 MapExprComma {
910 expr: Box<Expr>,
911 list: Box<Expr>,
912 flatten_array_refs: bool,
913 #[serde(default)]
914 stream: bool,
915 },
916 GrepExpr {
917 block: Block,
918 list: Box<Expr>,
919 #[serde(default)]
920 keyword: GrepBuiltinKeyword,
921 },
922 GrepExprComma {
924 expr: Box<Expr>,
925 list: Box<Expr>,
926 #[serde(default)]
927 keyword: GrepBuiltinKeyword,
928 },
929 SortExpr {
931 cmp: Option<SortComparator>,
932 list: Box<Expr>,
933 },
934 ReverseExpr(Box<Expr>),
935 ScalarReverse(Box<Expr>),
937 JoinExpr {
938 separator: Box<Expr>,
939 list: Box<Expr>,
940 },
941 SplitExpr {
942 pattern: Box<Expr>,
943 string: Box<Expr>,
944 limit: Option<Box<Expr>>,
945 },
946 ForEachExpr {
949 block: Block,
950 list: Box<Expr>,
951 },
952
953 PMapExpr {
955 block: Block,
956 list: Box<Expr>,
957 progress: Option<Box<Expr>>,
959 flat_outputs: bool,
962 #[serde(default, skip_serializing_if = "Option::is_none")]
964 on_cluster: Option<Box<Expr>>,
965 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
968 stream: bool,
969 },
970 PMapChunkedExpr {
972 chunk_size: Box<Expr>,
973 block: Block,
974 list: Box<Expr>,
975 progress: Option<Box<Expr>>,
976 },
977 PGrepExpr {
978 block: Block,
979 list: Box<Expr>,
980 progress: Option<Box<Expr>>,
982 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
984 stream: bool,
985 },
986 PForExpr {
988 block: Block,
989 list: Box<Expr>,
990 progress: Option<Box<Expr>>,
991 },
992 ParLinesExpr {
994 path: Box<Expr>,
995 callback: Box<Expr>,
996 progress: Option<Box<Expr>>,
997 },
998 ParWalkExpr {
1000 path: Box<Expr>,
1001 callback: Box<Expr>,
1002 progress: Option<Box<Expr>>,
1003 },
1004 PwatchExpr {
1006 path: Box<Expr>,
1007 callback: Box<Expr>,
1008 },
1009 PSortExpr {
1011 cmp: Option<Block>,
1012 list: Box<Expr>,
1013 progress: Option<Box<Expr>>,
1014 },
1015 ReduceExpr {
1018 block: Block,
1019 list: Box<Expr>,
1020 },
1021 PReduceExpr {
1024 block: Block,
1025 list: Box<Expr>,
1026 progress: Option<Box<Expr>>,
1028 },
1029 PReduceInitExpr {
1034 init: Box<Expr>,
1035 block: Block,
1036 list: Box<Expr>,
1037 progress: Option<Box<Expr>>,
1038 },
1039 PMapReduceExpr {
1041 map_block: Block,
1042 reduce_block: Block,
1043 list: Box<Expr>,
1044 progress: Option<Box<Expr>>,
1045 },
1046 PcacheExpr {
1048 block: Block,
1049 list: Box<Expr>,
1050 progress: Option<Box<Expr>>,
1051 },
1052 PselectExpr {
1054 receivers: Vec<Expr>,
1055 timeout: Option<Box<Expr>>,
1056 },
1057 FanExpr {
1062 count: Option<Box<Expr>>,
1063 block: Block,
1064 progress: Option<Box<Expr>>,
1065 capture: bool,
1066 },
1067
1068 AsyncBlock {
1070 body: Block,
1071 },
1072 SpawnBlock {
1074 body: Block,
1075 },
1076 Trace {
1078 body: Block,
1079 },
1080 Timer {
1082 body: Block,
1083 },
1084 Bench {
1086 body: Block,
1087 times: Box<Expr>,
1088 },
1089 Spinner {
1091 message: Box<Expr>,
1092 body: Block,
1093 },
1094 Await(Box<Expr>),
1096 Slurp(Box<Expr>),
1098 Capture(Box<Expr>),
1100 Qx(Box<Expr>),
1102 FetchUrl(Box<Expr>),
1104
1105 Pchannel {
1107 capacity: Option<Box<Expr>>,
1108 },
1109
1110 Push {
1112 array: Box<Expr>,
1113 values: Vec<Expr>,
1114 },
1115 Pop(Box<Expr>),
1116 Shift(Box<Expr>),
1117 Unshift {
1118 array: Box<Expr>,
1119 values: Vec<Expr>,
1120 },
1121 Splice {
1122 array: Box<Expr>,
1123 offset: Option<Box<Expr>>,
1124 length: Option<Box<Expr>>,
1125 replacement: Vec<Expr>,
1126 },
1127 Delete(Box<Expr>),
1128 Exists(Box<Expr>),
1129 Keys(Box<Expr>),
1130 Values(Box<Expr>),
1131 Each(Box<Expr>),
1132
1133 Chomp(Box<Expr>),
1135 Chop(Box<Expr>),
1136 Length(Box<Expr>),
1137 Substr {
1138 string: Box<Expr>,
1139 offset: Box<Expr>,
1140 length: Option<Box<Expr>>,
1141 replacement: Option<Box<Expr>>,
1142 },
1143 Index {
1144 string: Box<Expr>,
1145 substr: Box<Expr>,
1146 position: Option<Box<Expr>>,
1147 },
1148 Rindex {
1149 string: Box<Expr>,
1150 substr: Box<Expr>,
1151 position: Option<Box<Expr>>,
1152 },
1153 Sprintf {
1154 format: Box<Expr>,
1155 args: Vec<Expr>,
1156 },
1157
1158 Abs(Box<Expr>),
1160 Int(Box<Expr>),
1161 Sqrt(Box<Expr>),
1162 Sin(Box<Expr>),
1163 Cos(Box<Expr>),
1164 Atan2 {
1165 y: Box<Expr>,
1166 x: Box<Expr>,
1167 },
1168 Exp(Box<Expr>),
1169 Log(Box<Expr>),
1170 Rand(Option<Box<Expr>>),
1172 Srand(Option<Box<Expr>>),
1174 Hex(Box<Expr>),
1175 Oct(Box<Expr>),
1176
1177 Lc(Box<Expr>),
1179 Uc(Box<Expr>),
1180 Lcfirst(Box<Expr>),
1181 Ucfirst(Box<Expr>),
1182
1183 Fc(Box<Expr>),
1185 Crypt {
1187 plaintext: Box<Expr>,
1188 salt: Box<Expr>,
1189 },
1190 Pos(Option<Box<Expr>>),
1192 Study(Box<Expr>),
1194
1195 Defined(Box<Expr>),
1197 Ref(Box<Expr>),
1198 ScalarContext(Box<Expr>),
1199
1200 Chr(Box<Expr>),
1202 Ord(Box<Expr>),
1203
1204 OpenMyHandle {
1207 name: String,
1208 },
1209 Open {
1210 handle: Box<Expr>,
1211 mode: Box<Expr>,
1212 file: Option<Box<Expr>>,
1213 },
1214 Close(Box<Expr>),
1215 ReadLine(Option<String>),
1216 Eof(Option<Box<Expr>>),
1217
1218 Opendir {
1219 handle: Box<Expr>,
1220 path: Box<Expr>,
1221 },
1222 Readdir(Box<Expr>),
1223 Closedir(Box<Expr>),
1224 Rewinddir(Box<Expr>),
1225 Telldir(Box<Expr>),
1226 Seekdir {
1227 handle: Box<Expr>,
1228 position: Box<Expr>,
1229 },
1230
1231 FileTest {
1233 op: char,
1234 expr: Box<Expr>,
1235 },
1236
1237 System(Vec<Expr>),
1239 Exec(Vec<Expr>),
1240 Eval(Box<Expr>),
1241 Do(Box<Expr>),
1242 Require(Box<Expr>),
1243 Exit(Option<Box<Expr>>),
1244 Chdir(Box<Expr>),
1245 Mkdir {
1246 path: Box<Expr>,
1247 mode: Option<Box<Expr>>,
1248 },
1249 Unlink(Vec<Expr>),
1250 Rename {
1251 old: Box<Expr>,
1252 new: Box<Expr>,
1253 },
1254 Chmod(Vec<Expr>),
1256 Chown(Vec<Expr>),
1258
1259 Stat(Box<Expr>),
1260 Lstat(Box<Expr>),
1261 Link {
1262 old: Box<Expr>,
1263 new: Box<Expr>,
1264 },
1265 Symlink {
1266 old: Box<Expr>,
1267 new: Box<Expr>,
1268 },
1269 Readlink(Box<Expr>),
1270 Files(Vec<Expr>),
1272 Filesf(Vec<Expr>),
1274 FilesfRecursive(Vec<Expr>),
1276 Dirs(Vec<Expr>),
1278 DirsRecursive(Vec<Expr>),
1280 SymLinks(Vec<Expr>),
1282 Sockets(Vec<Expr>),
1284 Pipes(Vec<Expr>),
1286 BlockDevices(Vec<Expr>),
1288 CharDevices(Vec<Expr>),
1290 Glob(Vec<Expr>),
1291 GlobPar {
1294 args: Vec<Expr>,
1295 progress: Option<Box<Expr>>,
1296 },
1297 ParSed {
1299 args: Vec<Expr>,
1300 progress: Option<Box<Expr>>,
1301 },
1302
1303 Bless {
1305 ref_expr: Box<Expr>,
1306 class: Option<Box<Expr>>,
1307 },
1308
1309 Caller(Option<Box<Expr>>),
1311
1312 Wantarray,
1314
1315 List(Vec<Expr>),
1317
1318 PostfixIf {
1320 expr: Box<Expr>,
1321 condition: Box<Expr>,
1322 },
1323 PostfixUnless {
1324 expr: Box<Expr>,
1325 condition: Box<Expr>,
1326 },
1327 PostfixWhile {
1328 expr: Box<Expr>,
1329 condition: Box<Expr>,
1330 },
1331 PostfixUntil {
1332 expr: Box<Expr>,
1333 condition: Box<Expr>,
1334 },
1335 PostfixForeach {
1336 expr: Box<Expr>,
1337 list: Box<Expr>,
1338 },
1339
1340 RetryBlock {
1342 body: Block,
1343 times: Box<Expr>,
1344 backoff: RetryBackoff,
1345 },
1346 RateLimitBlock {
1349 slot: u32,
1350 max: Box<Expr>,
1351 window: Box<Expr>,
1352 body: Block,
1353 },
1354 EveryBlock {
1356 interval: Box<Expr>,
1357 body: Block,
1358 },
1359 GenBlock {
1361 body: Block,
1362 },
1363 Yield(Box<Expr>),
1365
1366 AlgebraicMatch {
1368 subject: Box<Expr>,
1369 arms: Vec<MatchArm>,
1370 },
1371}
1372
1373#[derive(Debug, Clone, Serialize, Deserialize)]
1374pub enum StringPart {
1375 Literal(String),
1376 ScalarVar(String),
1377 ArrayVar(String),
1378 Expr(Expr),
1379}
1380
1381#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
1382pub enum DerefKind {
1383 Array,
1384 Hash,
1385 Call,
1386}
1387
1388#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
1389pub enum BinOp {
1390 Add,
1391 Sub,
1392 Mul,
1393 Div,
1394 Mod,
1395 Pow,
1396 Concat,
1397 NumEq,
1398 NumNe,
1399 NumLt,
1400 NumGt,
1401 NumLe,
1402 NumGe,
1403 Spaceship,
1404 StrEq,
1405 StrNe,
1406 StrLt,
1407 StrGt,
1408 StrLe,
1409 StrGe,
1410 StrCmp,
1411 LogAnd,
1412 LogOr,
1413 DefinedOr,
1414 BitAnd,
1415 BitOr,
1416 BitXor,
1417 ShiftLeft,
1418 ShiftRight,
1419 LogAndWord,
1420 LogOrWord,
1421 BindMatch,
1422 BindNotMatch,
1423}
1424
1425#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
1426pub enum UnaryOp {
1427 Negate,
1428 LogNot,
1429 BitNot,
1430 LogNotWord,
1431 PreIncrement,
1432 PreDecrement,
1433 Ref,
1434}
1435
1436#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
1437pub enum PostfixOp {
1438 Increment,
1439 Decrement,
1440}
1441
1442#[cfg(test)]
1443mod tests {
1444 use super::*;
1445
1446 #[test]
1447 fn binop_deref_kind_distinct() {
1448 assert_ne!(BinOp::Add, BinOp::Sub);
1449 assert_eq!(DerefKind::Call, DerefKind::Call);
1450 }
1451
1452 #[test]
1453 fn sigil_variants_exhaustive_in_tests() {
1454 let all = [Sigil::Scalar, Sigil::Array, Sigil::Hash];
1455 assert_eq!(all.len(), 3);
1456 }
1457
1458 #[test]
1459 fn program_empty_roundtrip_clone() {
1460 let p = Program { statements: vec![] };
1461 assert!(p.clone().statements.is_empty());
1462 }
1463
1464 #[test]
1465 fn program_serializes_to_json() {
1466 let p = crate::parse("1+2;").expect("parse");
1467 let s = serde_json::to_string(&p).expect("json");
1468 assert!(s.contains("\"statements\""));
1469 assert!(s.contains("BinOp"));
1470 }
1471}