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