1use std::fmt::{Display, Write};
5
6use crate::tokenizer;
7
8const DISPLAY_INDENT: &str = " ";
9
10#[derive(Clone, Debug)]
12#[cfg_attr(feature = "fuzz-testing", derive(arbitrary::Arbitrary))]
13#[cfg_attr(test, derive(PartialEq, Eq, serde::Serialize))]
14pub struct Program {
15 #[cfg_attr(test, serde(rename = "cmds"))]
17 pub complete_commands: Vec<CompleteCommand>,
18}
19
20impl Display for Program {
21 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22 for complete_command in &self.complete_commands {
23 write!(f, "{complete_command}")?;
24 }
25 Ok(())
26 }
27}
28
29pub type CompleteCommand = CompoundList;
31
32pub type CompleteCommandItem = CompoundListItem;
34
35#[derive(Clone, Debug)]
37#[cfg_attr(feature = "fuzz-testing", derive(arbitrary::Arbitrary))]
38#[cfg_attr(test, derive(PartialEq, Eq, serde::Serialize))]
39pub enum SeparatorOperator {
40 Async,
42 Sequence,
44}
45
46impl Display for SeparatorOperator {
47 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
48 match self {
49 Self::Async => write!(f, "&"),
50 Self::Sequence => write!(f, ";"),
51 }
52 }
53}
54
55#[derive(Clone, Debug)]
57#[cfg_attr(feature = "fuzz-testing", derive(arbitrary::Arbitrary))]
58#[cfg_attr(test, derive(PartialEq, Eq, serde::Serialize))]
59#[cfg_attr(test, serde(rename = "AndOr"))]
60pub struct AndOrList {
61 pub first: Pipeline,
63 #[cfg_attr(test, serde(skip_serializing_if = "Vec::is_empty"))]
65 pub additional: Vec<AndOr>,
66}
67
68impl Display for AndOrList {
69 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
70 write!(f, "{}", self.first)?;
71 for item in &self.additional {
72 write!(f, "{item}")?;
73 }
74
75 Ok(())
76 }
77}
78
79#[derive(PartialEq, Eq)]
81pub enum PipelineOperator {
82 And,
84 Or,
86}
87
88impl PartialEq<AndOr> for PipelineOperator {
89 fn eq(&self, other: &AndOr) -> bool {
90 matches!(
91 (self, other),
92 (Self::And, AndOr::And(_)) | (Self::Or, AndOr::Or(_))
93 )
94 }
95}
96
97#[expect(clippy::from_over_into)]
99impl Into<PipelineOperator> for AndOr {
100 fn into(self) -> PipelineOperator {
101 match self {
102 Self::And(_) => PipelineOperator::And,
103 Self::Or(_) => PipelineOperator::Or,
104 }
105 }
106}
107
108pub struct AndOrListIter<'a> {
110 first: Option<&'a Pipeline>,
111 additional_iter: std::slice::Iter<'a, AndOr>,
112}
113
114impl<'a> Iterator for AndOrListIter<'a> {
115 type Item = (PipelineOperator, &'a Pipeline);
116
117 fn next(&mut self) -> Option<Self::Item> {
118 if let Some(first) = self.first.take() {
119 Some((PipelineOperator::And, first))
120 } else {
121 self.additional_iter.next().map(|and_or| match and_or {
122 AndOr::And(pipeline) => (PipelineOperator::And, pipeline),
123 AndOr::Or(pipeline) => (PipelineOperator::Or, pipeline),
124 })
125 }
126 }
127}
128
129impl<'a> IntoIterator for &'a AndOrList {
130 type Item = (PipelineOperator, &'a Pipeline);
131 type IntoIter = AndOrListIter<'a>;
132
133 fn into_iter(self) -> Self::IntoIter {
134 AndOrListIter {
135 first: Some(&self.first),
136 additional_iter: self.additional.iter(),
137 }
138 }
139}
140
141impl<'a> From<(PipelineOperator, &'a Pipeline)> for AndOr {
142 fn from(value: (PipelineOperator, &'a Pipeline)) -> Self {
143 match value.0 {
144 PipelineOperator::Or => Self::Or(value.1.to_owned()),
145 PipelineOperator::And => Self::And(value.1.to_owned()),
146 }
147 }
148}
149
150impl AndOrList {
151 pub fn iter(&self) -> AndOrListIter<'_> {
153 self.into_iter()
154 }
155}
156
157#[derive(Clone, Debug)]
160#[cfg_attr(feature = "fuzz-testing", derive(arbitrary::Arbitrary))]
161#[cfg_attr(test, derive(PartialEq, Eq, serde::Serialize))]
162pub enum AndOr {
163 And(Pipeline),
166 Or(Pipeline),
169}
170
171impl Display for AndOr {
172 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
173 match self {
174 Self::And(pipeline) => write!(f, " && {pipeline}"),
175 Self::Or(pipeline) => write!(f, " || {pipeline}"),
176 }
177 }
178}
179
180#[derive(Clone, Debug)]
182#[cfg_attr(feature = "fuzz-testing", derive(arbitrary::Arbitrary))]
183#[cfg_attr(test, derive(PartialEq, Eq, serde::Serialize))]
184pub enum PipelineTimed {
185 Timed,
187 TimedWithPosixOutput,
189}
190
191#[derive(Clone, Debug)]
194#[cfg_attr(feature = "fuzz-testing", derive(arbitrary::Arbitrary))]
195#[cfg_attr(test, derive(PartialEq, Eq, serde::Serialize))]
196pub struct Pipeline {
197 #[cfg_attr(test, serde(skip_serializing_if = "Option::is_none"))]
200 pub timed: Option<PipelineTimed>,
201 #[cfg_attr(test, serde(skip_serializing_if = "<&bool as std::ops::Not>::not"))]
204 pub bang: bool,
205 pub seq: Vec<Command>,
207}
208
209impl Display for Pipeline {
210 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
211 if self.bang {
212 write!(f, "!")?;
213 }
214 for (i, command) in self.seq.iter().enumerate() {
215 if i > 0 {
216 write!(f, " |")?;
217 }
218 write!(f, "{command}")?;
219 }
220
221 Ok(())
222 }
223}
224
225#[derive(Clone, Debug)]
227#[cfg_attr(feature = "fuzz-testing", derive(arbitrary::Arbitrary))]
228#[cfg_attr(test, derive(PartialEq, Eq, serde::Serialize))]
229pub enum Command {
230 Simple(SimpleCommand),
233 Compound(CompoundCommand, Option<RedirectList>),
235 Function(FunctionDefinition),
237 ExtendedTest(ExtendedTestExpr),
239}
240
241impl Display for Command {
242 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
243 match self {
244 Self::Simple(simple_command) => write!(f, "{simple_command}"),
245 Self::Compound(compound_command, redirect_list) => {
246 write!(f, "{compound_command}")?;
247 if let Some(redirect_list) = redirect_list {
248 write!(f, "{redirect_list}")?;
249 }
250 Ok(())
251 }
252 Self::Function(function_definition) => write!(f, "{function_definition}"),
253 Self::ExtendedTest(extended_test_expr) => {
254 write!(f, "[[ {extended_test_expr} ]]")
255 }
256 }
257 }
258}
259
260#[derive(Clone, Debug)]
262#[cfg_attr(feature = "fuzz-testing", derive(arbitrary::Arbitrary))]
263#[cfg_attr(test, derive(PartialEq, Eq, serde::Serialize))]
264pub enum CompoundCommand {
265 Arithmetic(ArithmeticCommand),
267 ArithmeticForClause(ArithmeticForClauseCommand),
269 BraceGroup(BraceGroupCommand),
271 Subshell(SubshellCommand),
273 ForClause(ForClauseCommand),
275 CaseClause(CaseClauseCommand),
278 IfClause(IfClauseCommand),
280 WhileClause(WhileOrUntilClauseCommand),
282 UntilClause(WhileOrUntilClauseCommand),
284}
285
286impl Display for CompoundCommand {
287 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
288 match self {
289 Self::Arithmetic(arithmetic_command) => write!(f, "{arithmetic_command}"),
290 Self::ArithmeticForClause(arithmetic_for_clause_command) => {
291 write!(f, "{arithmetic_for_clause_command}")
292 }
293 Self::BraceGroup(brace_group_command) => {
294 write!(f, "{brace_group_command}")
295 }
296 Self::Subshell(subshell_command) => write!(f, "{subshell_command}"),
297 Self::ForClause(for_clause_command) => write!(f, "{for_clause_command}"),
298 Self::CaseClause(case_clause_command) => {
299 write!(f, "{case_clause_command}")
300 }
301 Self::IfClause(if_clause_command) => write!(f, "{if_clause_command}"),
302 Self::WhileClause(while_or_until_clause_command) => {
303 write!(f, "while {while_or_until_clause_command}")
304 }
305 Self::UntilClause(while_or_until_clause_command) => {
306 write!(f, "until {while_or_until_clause_command}")
307 }
308 }
309 }
310}
311
312#[derive(Clone, Debug)]
314#[cfg_attr(feature = "fuzz-testing", derive(arbitrary::Arbitrary))]
315#[cfg_attr(test, derive(PartialEq, Eq, serde::Serialize))]
316pub struct ArithmeticCommand {
317 pub expr: UnexpandedArithmeticExpr,
319}
320
321impl Display for ArithmeticCommand {
322 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
323 write!(f, "(({}))", self.expr)
324 }
325}
326
327#[derive(Clone, Debug)]
329#[cfg_attr(feature = "fuzz-testing", derive(arbitrary::Arbitrary))]
330#[cfg_attr(test, derive(PartialEq, Eq, serde::Serialize))]
331pub struct SubshellCommand(pub CompoundList);
332
333impl Display for SubshellCommand {
334 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
335 write!(f, "( ")?;
336 write!(f, "{}", self.0)?;
337 write!(f, " )")
338 }
339}
340
341#[derive(Clone, Debug)]
343#[cfg_attr(feature = "fuzz-testing", derive(arbitrary::Arbitrary))]
344#[cfg_attr(test, derive(PartialEq, Eq, serde::Serialize))]
345pub struct ForClauseCommand {
346 pub variable_name: String,
348 pub values: Option<Vec<Word>>,
350 pub body: DoGroupCommand,
352}
353
354impl Display for ForClauseCommand {
355 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
356 write!(f, "for {} in ", self.variable_name)?;
357
358 if let Some(values) = &self.values {
359 for (i, value) in values.iter().enumerate() {
360 if i > 0 {
361 write!(f, " ")?;
362 }
363
364 write!(f, "{value}")?;
365 }
366 }
367
368 writeln!(f, ";")?;
369
370 write!(f, "{}", self.body)
371 }
372}
373
374#[derive(Clone, Debug)]
376#[cfg_attr(feature = "fuzz-testing", derive(arbitrary::Arbitrary))]
377#[cfg_attr(test, derive(PartialEq, Eq, serde::Serialize))]
378pub struct ArithmeticForClauseCommand {
379 pub initializer: Option<UnexpandedArithmeticExpr>,
381 pub condition: Option<UnexpandedArithmeticExpr>,
383 pub updater: Option<UnexpandedArithmeticExpr>,
385 pub body: DoGroupCommand,
387}
388
389impl Display for ArithmeticForClauseCommand {
390 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
391 write!(f, "for ((")?;
392
393 if let Some(initializer) = &self.initializer {
394 write!(f, "{initializer}")?;
395 }
396
397 write!(f, "; ")?;
398
399 if let Some(condition) = &self.condition {
400 write!(f, "{condition}")?;
401 }
402
403 write!(f, "; ")?;
404
405 if let Some(updater) = &self.updater {
406 write!(f, "{updater}")?;
407 }
408
409 writeln!(f, "))")?;
410
411 write!(f, "{}", self.body)
412 }
413}
414
415#[derive(Clone, Debug)]
418#[cfg_attr(feature = "fuzz-testing", derive(arbitrary::Arbitrary))]
419#[cfg_attr(test, derive(PartialEq, Eq, serde::Serialize))]
420pub struct CaseClauseCommand {
421 pub value: Word,
423 pub cases: Vec<CaseItem>,
425}
426
427impl Display for CaseClauseCommand {
428 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
429 write!(f, "case {} in", self.value)?;
430 for case in &self.cases {
431 write!(indenter::indented(f).with_str(DISPLAY_INDENT), "{case}")?;
432 }
433 writeln!(f)?;
434 write!(f, "esac")
435 }
436}
437
438#[derive(Clone, Debug)]
440#[cfg_attr(feature = "fuzz-testing", derive(arbitrary::Arbitrary))]
441#[cfg_attr(test, derive(PartialEq, Eq, serde::Serialize))]
442#[cfg_attr(test, serde(rename = "List"))]
443pub struct CompoundList(pub Vec<CompoundListItem>);
444
445impl Display for CompoundList {
446 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
447 for (i, item) in self.0.iter().enumerate() {
448 if i > 0 {
449 writeln!(f)?;
450 }
451
452 write!(f, "{}", item.0)?;
454
455 if i == self.0.len() - 1 && matches!(item.1, SeparatorOperator::Sequence) {
457 } else {
459 write!(f, "{}", item.1)?;
460 }
461 }
462
463 Ok(())
464 }
465}
466
467#[derive(Clone, Debug)]
469#[cfg_attr(feature = "fuzz-testing", derive(arbitrary::Arbitrary))]
470#[cfg_attr(test, derive(PartialEq, Eq, serde::Serialize))]
471#[cfg_attr(test, serde(rename = "Item"))]
472pub struct CompoundListItem(pub AndOrList, pub SeparatorOperator);
473
474impl Display for CompoundListItem {
475 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
476 write!(f, "{}", self.0)?;
477 write!(f, "{}", self.1)?;
478 Ok(())
479 }
480}
481
482#[derive(Clone, Debug)]
484#[cfg_attr(feature = "fuzz-testing", derive(arbitrary::Arbitrary))]
485#[cfg_attr(test, derive(PartialEq, Eq, serde::Serialize))]
486pub struct IfClauseCommand {
487 pub condition: CompoundList,
489 pub then: CompoundList,
491 #[cfg_attr(test, serde(skip_serializing_if = "Option::is_none"))]
493 pub elses: Option<Vec<ElseClause>>,
494}
495
496impl Display for IfClauseCommand {
497 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
498 writeln!(f, "if {}; then", self.condition)?;
499 write!(
500 indenter::indented(f).with_str(DISPLAY_INDENT),
501 "{}",
502 self.then
503 )?;
504 if let Some(elses) = &self.elses {
505 for else_clause in elses {
506 write!(f, "{else_clause}")?;
507 }
508 }
509
510 writeln!(f)?;
511 write!(f, "fi")?;
512
513 Ok(())
514 }
515}
516
517#[derive(Clone, Debug)]
519#[cfg_attr(feature = "fuzz-testing", derive(arbitrary::Arbitrary))]
520#[cfg_attr(test, derive(PartialEq, Eq, serde::Serialize))]
521pub struct ElseClause {
522 #[cfg_attr(test, serde(skip_serializing_if = "Option::is_none"))]
524 pub condition: Option<CompoundList>,
525 pub body: CompoundList,
527}
528
529impl Display for ElseClause {
530 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
531 writeln!(f)?;
532 if let Some(condition) = &self.condition {
533 writeln!(f, "elif {condition}; then")?;
534 } else {
535 writeln!(f, "else")?;
536 }
537
538 write!(
539 indenter::indented(f).with_str(DISPLAY_INDENT),
540 "{}",
541 self.body
542 )
543 }
544}
545
546#[derive(Clone, Debug)]
548#[cfg_attr(feature = "fuzz-testing", derive(arbitrary::Arbitrary))]
549#[cfg_attr(test, derive(PartialEq, Eq, serde::Serialize))]
550pub struct CaseItem {
551 pub patterns: Vec<Word>,
553 pub cmd: Option<CompoundList>,
555 pub post_action: CaseItemPostAction,
557}
558
559impl Display for CaseItem {
560 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
561 writeln!(f)?;
562 for (i, pattern) in self.patterns.iter().enumerate() {
563 if i > 0 {
564 write!(f, "|")?;
565 }
566 write!(f, "{pattern}")?;
567 }
568 writeln!(f, ")")?;
569
570 if let Some(cmd) = &self.cmd {
571 write!(indenter::indented(f).with_str(DISPLAY_INDENT), "{cmd}")?;
572 }
573 writeln!(f)?;
574 write!(f, "{}", self.post_action)
575 }
576}
577
578#[derive(Clone, Debug)]
580#[cfg_attr(feature = "fuzz-testing", derive(arbitrary::Arbitrary))]
581#[cfg_attr(test, derive(PartialEq, Eq, serde::Serialize))]
582pub enum CaseItemPostAction {
583 ExitCase,
585 UnconditionallyExecuteNextCaseItem,
588 ContinueEvaluatingCases,
591}
592
593impl Display for CaseItemPostAction {
594 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
595 match self {
596 Self::ExitCase => write!(f, ";;"),
597 Self::UnconditionallyExecuteNextCaseItem => write!(f, ";&"),
598 Self::ContinueEvaluatingCases => write!(f, ";;&"),
599 }
600 }
601}
602
603#[derive(Clone, Debug)]
605#[cfg_attr(feature = "fuzz-testing", derive(arbitrary::Arbitrary))]
606#[cfg_attr(test, derive(PartialEq, Eq, serde::Serialize))]
607pub struct WhileOrUntilClauseCommand(pub CompoundList, pub DoGroupCommand);
608
609impl Display for WhileOrUntilClauseCommand {
610 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
611 write!(f, "{}; {}", self.0, self.1)
612 }
613}
614
615#[derive(Clone, Debug)]
617#[cfg_attr(feature = "fuzz-testing", derive(arbitrary::Arbitrary))]
618#[cfg_attr(test, derive(PartialEq, Eq, serde::Serialize))]
619pub struct FunctionDefinition {
620 pub fname: String,
622 pub body: FunctionBody,
624 pub source: String,
626}
627
628impl Display for FunctionDefinition {
629 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
630 writeln!(f, "{} () ", self.fname)?;
631 write!(f, "{}", self.body)?;
632 Ok(())
633 }
634}
635
636#[derive(Clone, Debug)]
638#[cfg_attr(feature = "fuzz-testing", derive(arbitrary::Arbitrary))]
639#[cfg_attr(test, derive(PartialEq, Eq, serde::Serialize))]
640pub struct FunctionBody(pub CompoundCommand, pub Option<RedirectList>);
641
642impl Display for FunctionBody {
643 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
644 write!(f, "{}", self.0)?;
645 if let Some(redirect_list) = &self.1 {
646 write!(f, "{redirect_list}")?;
647 }
648
649 Ok(())
650 }
651}
652
653#[derive(Clone, Debug)]
655#[cfg_attr(feature = "fuzz-testing", derive(arbitrary::Arbitrary))]
656#[cfg_attr(test, derive(PartialEq, Eq, serde::Serialize))]
657pub struct BraceGroupCommand(pub CompoundList);
658
659impl Display for BraceGroupCommand {
660 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
661 writeln!(f, "{{ ")?;
662 write!(indenter::indented(f).with_str(DISPLAY_INDENT), "{}", self.0)?;
663 writeln!(f)?;
664 write!(f, "}}")?;
665
666 Ok(())
667 }
668}
669
670#[derive(Clone, Debug)]
672#[cfg_attr(feature = "fuzz-testing", derive(arbitrary::Arbitrary))]
673#[cfg_attr(test, derive(PartialEq, Eq, serde::Serialize))]
674pub struct DoGroupCommand(pub CompoundList);
675
676impl Display for DoGroupCommand {
677 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
678 writeln!(f, "do")?;
679 write!(indenter::indented(f).with_str(DISPLAY_INDENT), "{}", self.0)?;
680 writeln!(f)?;
681 write!(f, "done")
682 }
683}
684
685#[derive(Clone, Debug)]
687#[cfg_attr(feature = "fuzz-testing", derive(arbitrary::Arbitrary))]
688#[cfg_attr(test, derive(PartialEq, Eq, serde::Serialize))]
689#[cfg_attr(test, serde(rename = "Simple"))]
690pub struct SimpleCommand {
691 #[cfg_attr(test, serde(skip_serializing_if = "Option::is_none"))]
693 pub prefix: Option<CommandPrefix>,
694 #[cfg_attr(test, serde(skip_serializing_if = "Option::is_none"))]
696 #[cfg_attr(test, serde(rename = "w"))]
697 pub word_or_name: Option<Word>,
698 #[cfg_attr(test, serde(skip_serializing_if = "Option::is_none"))]
700 pub suffix: Option<CommandSuffix>,
701}
702
703impl Display for SimpleCommand {
704 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
705 let mut wrote_something = false;
706
707 if let Some(prefix) = &self.prefix {
708 if wrote_something {
709 write!(f, " ")?;
710 }
711
712 write!(f, "{prefix}")?;
713 wrote_something = true;
714 }
715
716 if let Some(word_or_name) = &self.word_or_name {
717 if wrote_something {
718 write!(f, " ")?;
719 }
720
721 write!(f, "{word_or_name}")?;
722 wrote_something = true;
723 }
724
725 if let Some(suffix) = &self.suffix {
726 if wrote_something {
727 write!(f, " ")?;
728 }
729
730 write!(f, "{suffix}")?;
731 }
732
733 Ok(())
734 }
735}
736
737#[derive(Clone, Debug, Default)]
739#[cfg_attr(feature = "fuzz-testing", derive(arbitrary::Arbitrary))]
740#[cfg_attr(test, derive(PartialEq, Eq, serde::Serialize))]
741#[cfg_attr(test, serde(rename = "Prefix"))]
742pub struct CommandPrefix(pub Vec<CommandPrefixOrSuffixItem>);
743
744impl Display for CommandPrefix {
745 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
746 for (i, item) in self.0.iter().enumerate() {
747 if i > 0 {
748 write!(f, " ")?;
749 }
750
751 write!(f, "{item}")?;
752 }
753 Ok(())
754 }
755}
756
757#[derive(Clone, Default, Debug)]
759#[cfg_attr(feature = "fuzz-testing", derive(arbitrary::Arbitrary))]
760#[cfg_attr(test, derive(PartialEq, Eq, serde::Serialize))]
761#[cfg_attr(test, serde(rename = "Suffix"))]
762pub struct CommandSuffix(pub Vec<CommandPrefixOrSuffixItem>);
763
764impl Display for CommandSuffix {
765 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
766 for (i, item) in self.0.iter().enumerate() {
767 if i > 0 {
768 write!(f, " ")?;
769 }
770
771 write!(f, "{item}")?;
772 }
773 Ok(())
774 }
775}
776
777#[derive(Clone, Debug)]
779#[cfg_attr(feature = "fuzz-testing", derive(arbitrary::Arbitrary))]
780#[cfg_attr(test, derive(PartialEq, Eq, serde::Serialize))]
781pub enum ProcessSubstitutionKind {
782 Read,
784 Write,
786}
787
788impl Display for ProcessSubstitutionKind {
789 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
790 match self {
791 Self::Read => write!(f, "<"),
792 Self::Write => write!(f, ">"),
793 }
794 }
795}
796
797#[derive(Clone, Debug)]
799#[cfg_attr(feature = "fuzz-testing", derive(arbitrary::Arbitrary))]
800#[cfg_attr(test, derive(PartialEq, Eq, serde::Serialize))]
801pub enum CommandPrefixOrSuffixItem {
802 IoRedirect(IoRedirect),
804 Word(Word),
806 #[cfg_attr(test, serde(rename = "Assign"))]
808 AssignmentWord(Assignment, Word),
809 ProcessSubstitution(ProcessSubstitutionKind, SubshellCommand),
811}
812
813impl Display for CommandPrefixOrSuffixItem {
814 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
815 match self {
816 Self::IoRedirect(io_redirect) => write!(f, "{io_redirect}"),
817 Self::Word(word) => write!(f, "{word}"),
818 Self::AssignmentWord(_assignment, word) => write!(f, "{word}"),
819 Self::ProcessSubstitution(kind, subshell_command) => {
820 write!(f, "{kind}({subshell_command})")
821 }
822 }
823 }
824}
825
826#[derive(Clone, Debug)]
828#[cfg_attr(feature = "fuzz-testing", derive(arbitrary::Arbitrary))]
829#[cfg_attr(test, derive(PartialEq, Eq, serde::Serialize))]
830#[cfg_attr(test, serde(rename = "Assign"))]
831pub struct Assignment {
832 pub name: AssignmentName,
834 pub value: AssignmentValue,
836 #[cfg_attr(test, serde(skip_serializing_if = "<&bool as std::ops::Not>::not"))]
838 pub append: bool,
839}
840
841impl Display for Assignment {
842 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
843 write!(f, "{}", self.name)?;
844 if self.append {
845 write!(f, "+")?;
846 }
847 write!(f, "={}", self.value)
848 }
849}
850
851#[derive(Clone, Debug)]
853#[cfg_attr(feature = "fuzz-testing", derive(arbitrary::Arbitrary))]
854#[cfg_attr(test, derive(PartialEq, Eq, serde::Serialize))]
855pub enum AssignmentName {
856 #[cfg_attr(test, serde(rename = "Var"))]
858 VariableName(String),
859 ArrayElementName(String, String),
861}
862
863impl Display for AssignmentName {
864 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
865 match self {
866 Self::VariableName(name) => write!(f, "{name}"),
867 Self::ArrayElementName(name, index) => {
868 write!(f, "{name}[{index}]")
869 }
870 }
871 }
872}
873
874#[derive(Clone, Debug)]
876#[cfg_attr(feature = "fuzz-testing", derive(arbitrary::Arbitrary))]
877#[cfg_attr(test, derive(PartialEq, Eq, serde::Serialize))]
878pub enum AssignmentValue {
879 Scalar(Word),
881 Array(Vec<(Option<Word>, Word)>),
883}
884
885impl Display for AssignmentValue {
886 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
887 match self {
888 Self::Scalar(word) => write!(f, "{word}"),
889 Self::Array(words) => {
890 write!(f, "(")?;
891 for (i, value) in words.iter().enumerate() {
892 if i > 0 {
893 write!(f, " ")?;
894 }
895 match value {
896 (Some(key), value) => write!(f, "[{key}]={value}")?,
897 (None, value) => write!(f, "{value}")?,
898 }
899 }
900 write!(f, ")")
901 }
902 }
903 }
904}
905
906#[derive(Clone, Debug)]
908#[cfg_attr(feature = "fuzz-testing", derive(arbitrary::Arbitrary))]
909#[cfg_attr(test, derive(PartialEq, Eq, serde::Serialize))]
910pub struct RedirectList(pub Vec<IoRedirect>);
911
912impl Display for RedirectList {
913 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
914 for item in &self.0 {
915 write!(f, "{item}")?;
916 }
917 Ok(())
918 }
919}
920
921#[derive(Clone, Debug)]
923#[cfg_attr(feature = "fuzz-testing", derive(arbitrary::Arbitrary))]
924#[cfg_attr(test, derive(PartialEq, Eq, serde::Serialize))]
925pub enum IoRedirect {
926 File(Option<u32>, IoFileRedirectKind, IoFileRedirectTarget),
928 HereDocument(Option<u32>, IoHereDocument),
930 HereString(Option<u32>, Word),
932 OutputAndError(Word, bool),
934}
935
936impl Display for IoRedirect {
937 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
938 match self {
939 Self::File(fd_num, kind, target) => {
940 if let Some(fd_num) = fd_num {
941 write!(f, "{fd_num}")?;
942 }
943
944 write!(f, "{kind} {target}")?;
945 }
946 Self::OutputAndError(target, append) => {
947 write!(f, "&>")?;
948 if *append {
949 write!(f, ">")?;
950 }
951 write!(f, " {target}")?;
952 }
953 Self::HereDocument(
954 fd_num,
955 IoHereDocument {
956 remove_tabs,
957 here_end,
958 doc,
959 ..
960 },
961 ) => {
962 if let Some(fd_num) = fd_num {
963 write!(f, "{fd_num}")?;
964 }
965
966 write!(f, "<<")?;
967 if *remove_tabs {
968 write!(f, "-")?;
969 }
970
971 writeln!(f, "{here_end}")?;
972
973 write!(f, "{doc}")?;
974 writeln!(f, "{here_end}")?;
975 }
976 Self::HereString(fd_num, s) => {
977 if let Some(fd_num) = fd_num {
978 write!(f, "{fd_num}")?;
979 }
980
981 write!(f, "<<< {s}")?;
982 }
983 }
984
985 Ok(())
986 }
987}
988
989#[derive(Clone, Debug)]
991#[cfg_attr(feature = "fuzz-testing", derive(arbitrary::Arbitrary))]
992#[cfg_attr(test, derive(PartialEq, Eq, serde::Serialize))]
993pub enum IoFileRedirectKind {
994 Read,
996 Write,
998 Append,
1000 ReadAndWrite,
1002 Clobber,
1004 DuplicateInput,
1006 DuplicateOutput,
1008}
1009
1010impl Display for IoFileRedirectKind {
1011 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1012 match self {
1013 Self::Read => write!(f, "<"),
1014 Self::Write => write!(f, ">"),
1015 Self::Append => write!(f, ">>"),
1016 Self::ReadAndWrite => write!(f, "<>"),
1017 Self::Clobber => write!(f, ">|"),
1018 Self::DuplicateInput => write!(f, "<&"),
1019 Self::DuplicateOutput => write!(f, ">&"),
1020 }
1021 }
1022}
1023
1024#[derive(Clone, Debug)]
1026#[cfg_attr(feature = "fuzz-testing", derive(arbitrary::Arbitrary))]
1027#[cfg_attr(test, derive(PartialEq, Eq, serde::Serialize))]
1028pub enum IoFileRedirectTarget {
1029 Filename(Word),
1031 Fd(u32),
1033 ProcessSubstitution(ProcessSubstitutionKind, SubshellCommand),
1036 Duplicate(Word),
1040}
1041
1042impl Display for IoFileRedirectTarget {
1043 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1044 match self {
1045 Self::Filename(word) => write!(f, "{word}"),
1046 Self::Fd(fd) => write!(f, "{fd}"),
1047 Self::ProcessSubstitution(kind, subshell_command) => {
1048 write!(f, "{kind}{subshell_command}")
1049 }
1050 Self::Duplicate(word) => write!(f, "{word}"),
1051 }
1052 }
1053}
1054
1055#[derive(Clone, Debug)]
1057#[cfg_attr(feature = "fuzz-testing", derive(arbitrary::Arbitrary))]
1058#[cfg_attr(test, derive(PartialEq, Eq, serde::Serialize))]
1059pub struct IoHereDocument {
1060 #[cfg_attr(test, serde(skip_serializing_if = "<&bool as std::ops::Not>::not"))]
1062 pub remove_tabs: bool,
1063 #[cfg_attr(test, serde(skip_serializing_if = "<&bool as std::ops::Not>::not"))]
1065 pub requires_expansion: bool,
1066 pub here_end: Word,
1068 pub doc: Word,
1070}
1071
1072#[derive(Clone, Debug)]
1074#[cfg_attr(test, derive(PartialEq, Eq, serde::Serialize))]
1075pub enum TestExpr {
1076 False,
1078 Literal(String),
1080 And(Box<TestExpr>, Box<TestExpr>),
1082 Or(Box<TestExpr>, Box<TestExpr>),
1084 Not(Box<TestExpr>),
1086 Parenthesized(Box<TestExpr>),
1088 UnaryTest(UnaryPredicate, String),
1090 BinaryTest(BinaryPredicate, String, String),
1092}
1093
1094impl Display for TestExpr {
1095 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1096 match self {
1097 Self::False => Ok(()),
1098 Self::Literal(s) => write!(f, "{s}"),
1099 Self::And(left, right) => write!(f, "{left} -a {right}"),
1100 Self::Or(left, right) => write!(f, "{left} -o {right}"),
1101 Self::Not(expr) => write!(f, "! {expr}"),
1102 Self::Parenthesized(expr) => write!(f, "( {expr} )"),
1103 Self::UnaryTest(pred, word) => write!(f, "{pred} {word}"),
1104 Self::BinaryTest(left, op, right) => write!(f, "{left} {op} {right}"),
1105 }
1106 }
1107}
1108
1109#[derive(Clone, Debug)]
1111#[cfg_attr(feature = "fuzz-testing", derive(arbitrary::Arbitrary))]
1112#[cfg_attr(test, derive(PartialEq, Eq, serde::Serialize))]
1113pub enum ExtendedTestExpr {
1114 And(Box<ExtendedTestExpr>, Box<ExtendedTestExpr>),
1116 Or(Box<ExtendedTestExpr>, Box<ExtendedTestExpr>),
1118 Not(Box<ExtendedTestExpr>),
1120 Parenthesized(Box<ExtendedTestExpr>),
1122 UnaryTest(UnaryPredicate, Word),
1124 BinaryTest(BinaryPredicate, Word, Word),
1126}
1127
1128impl Display for ExtendedTestExpr {
1129 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1130 match self {
1131 Self::And(left, right) => {
1132 write!(f, "{left} && {right}")
1133 }
1134 Self::Or(left, right) => {
1135 write!(f, "{left} || {right}")
1136 }
1137 Self::Not(expr) => {
1138 write!(f, "! {expr}")
1139 }
1140 Self::Parenthesized(expr) => {
1141 write!(f, "( {expr} )")
1142 }
1143 Self::UnaryTest(pred, word) => {
1144 write!(f, "{pred} {word}")
1145 }
1146 Self::BinaryTest(pred, left, right) => {
1147 write!(f, "{left} {pred} {right}")
1148 }
1149 }
1150 }
1151}
1152
1153#[derive(Clone, Debug)]
1155#[cfg_attr(feature = "fuzz-testing", derive(arbitrary::Arbitrary))]
1156#[cfg_attr(test, derive(PartialEq, Eq, serde::Serialize))]
1157pub enum UnaryPredicate {
1158 FileExists,
1160 FileExistsAndIsBlockSpecialFile,
1162 FileExistsAndIsCharSpecialFile,
1164 FileExistsAndIsDir,
1166 FileExistsAndIsRegularFile,
1168 FileExistsAndIsSetgid,
1170 FileExistsAndIsSymlink,
1172 FileExistsAndHasStickyBit,
1174 FileExistsAndIsFifo,
1176 FileExistsAndIsReadable,
1178 FileExistsAndIsNotZeroLength,
1180 FdIsOpenTerminal,
1182 FileExistsAndIsSetuid,
1184 FileExistsAndIsWritable,
1186 FileExistsAndIsExecutable,
1188 FileExistsAndOwnedByEffectiveGroupId,
1191 FileExistsAndModifiedSinceLastRead,
1194 FileExistsAndOwnedByEffectiveUserId,
1197 FileExistsAndIsSocket,
1199 ShellOptionEnabled,
1201 ShellVariableIsSetAndAssigned,
1203 ShellVariableIsSetAndNameRef,
1205 StringHasZeroLength,
1207 StringHasNonZeroLength,
1209}
1210
1211impl Display for UnaryPredicate {
1212 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1213 match self {
1214 Self::FileExists => write!(f, "-e"),
1215 Self::FileExistsAndIsBlockSpecialFile => write!(f, "-b"),
1216 Self::FileExistsAndIsCharSpecialFile => write!(f, "-c"),
1217 Self::FileExistsAndIsDir => write!(f, "-d"),
1218 Self::FileExistsAndIsRegularFile => write!(f, "-f"),
1219 Self::FileExistsAndIsSetgid => write!(f, "-g"),
1220 Self::FileExistsAndIsSymlink => write!(f, "-h"),
1221 Self::FileExistsAndHasStickyBit => write!(f, "-k"),
1222 Self::FileExistsAndIsFifo => write!(f, "-p"),
1223 Self::FileExistsAndIsReadable => write!(f, "-r"),
1224 Self::FileExistsAndIsNotZeroLength => write!(f, "-s"),
1225 Self::FdIsOpenTerminal => write!(f, "-t"),
1226 Self::FileExistsAndIsSetuid => write!(f, "-u"),
1227 Self::FileExistsAndIsWritable => write!(f, "-w"),
1228 Self::FileExistsAndIsExecutable => write!(f, "-x"),
1229 Self::FileExistsAndOwnedByEffectiveGroupId => write!(f, "-G"),
1230 Self::FileExistsAndModifiedSinceLastRead => write!(f, "-N"),
1231 Self::FileExistsAndOwnedByEffectiveUserId => write!(f, "-O"),
1232 Self::FileExistsAndIsSocket => write!(f, "-S"),
1233 Self::ShellOptionEnabled => write!(f, "-o"),
1234 Self::ShellVariableIsSetAndAssigned => write!(f, "-v"),
1235 Self::ShellVariableIsSetAndNameRef => write!(f, "-R"),
1236 Self::StringHasZeroLength => write!(f, "-z"),
1237 Self::StringHasNonZeroLength => write!(f, "-n"),
1238 }
1239 }
1240}
1241
1242#[derive(Clone, Debug)]
1244#[cfg_attr(feature = "fuzz-testing", derive(arbitrary::Arbitrary))]
1245#[cfg_attr(test, derive(PartialEq, Eq, serde::Serialize))]
1246pub enum BinaryPredicate {
1247 FilesReferToSameDeviceAndInodeNumbers,
1249 LeftFileIsNewerOrExistsWhenRightDoesNot,
1251 LeftFileIsOlderOrDoesNotExistWhenRightDoes,
1253 StringExactlyMatchesPattern,
1255 StringDoesNotExactlyMatchPattern,
1257 StringMatchesRegex,
1259 StringExactlyMatchesString,
1261 StringDoesNotExactlyMatchString,
1263 StringContainsSubstring,
1265 LeftSortsBeforeRight,
1267 LeftSortsAfterRight,
1269 ArithmeticEqualTo,
1271 ArithmeticNotEqualTo,
1273 ArithmeticLessThan,
1275 ArithmeticLessThanOrEqualTo,
1277 ArithmeticGreaterThan,
1279 ArithmeticGreaterThanOrEqualTo,
1281}
1282
1283impl Display for BinaryPredicate {
1284 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1285 match self {
1286 Self::FilesReferToSameDeviceAndInodeNumbers => write!(f, "-ef"),
1287 Self::LeftFileIsNewerOrExistsWhenRightDoesNot => write!(f, "-nt"),
1288 Self::LeftFileIsOlderOrDoesNotExistWhenRightDoes => write!(f, "-ot"),
1289 Self::StringExactlyMatchesPattern => write!(f, "=="),
1290 Self::StringDoesNotExactlyMatchPattern => write!(f, "!="),
1291 Self::StringMatchesRegex => write!(f, "=~"),
1292 Self::StringContainsSubstring => write!(f, "=~"),
1293 Self::StringExactlyMatchesString => write!(f, "=="),
1294 Self::StringDoesNotExactlyMatchString => write!(f, "!="),
1295 Self::LeftSortsBeforeRight => write!(f, "<"),
1296 Self::LeftSortsAfterRight => write!(f, ">"),
1297 Self::ArithmeticEqualTo => write!(f, "-eq"),
1298 Self::ArithmeticNotEqualTo => write!(f, "-ne"),
1299 Self::ArithmeticLessThan => write!(f, "-lt"),
1300 Self::ArithmeticLessThanOrEqualTo => write!(f, "-le"),
1301 Self::ArithmeticGreaterThan => write!(f, "-gt"),
1302 Self::ArithmeticGreaterThanOrEqualTo => write!(f, "-ge"),
1303 }
1304 }
1305}
1306
1307#[derive(Clone, Debug)]
1309#[cfg_attr(feature = "fuzz-testing", derive(arbitrary::Arbitrary))]
1310#[cfg_attr(test, derive(PartialEq, Eq, serde::Serialize))]
1311#[cfg_attr(test, serde(rename = "W"))]
1312pub struct Word {
1313 #[cfg_attr(test, serde(rename = "v"))]
1315 pub value: String,
1316}
1317
1318impl Display for Word {
1319 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1320 write!(f, "{}", self.value)
1321 }
1322}
1323
1324impl From<&tokenizer::Token> for Word {
1325 fn from(t: &tokenizer::Token) -> Self {
1326 match t {
1327 tokenizer::Token::Word(value, _) => Self {
1328 value: value.clone(),
1329 },
1330 tokenizer::Token::Operator(value, _) => Self {
1331 value: value.clone(),
1332 },
1333 }
1334 }
1335}
1336
1337impl From<String> for Word {
1338 fn from(s: String) -> Self {
1339 Self { value: s }
1340 }
1341}
1342
1343impl Word {
1344 pub fn new(s: &str) -> Self {
1346 Self {
1347 value: s.to_owned(),
1348 }
1349 }
1350
1351 pub fn flatten(&self) -> String {
1353 self.value.clone()
1354 }
1355}
1356
1357#[derive(Clone, Debug)]
1359#[cfg_attr(feature = "fuzz-testing", derive(arbitrary::Arbitrary))]
1360#[cfg_attr(test, derive(PartialEq, Eq, serde::Serialize))]
1361pub struct UnexpandedArithmeticExpr {
1362 pub value: String,
1364}
1365
1366impl Display for UnexpandedArithmeticExpr {
1367 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1368 write!(f, "{}", self.value)
1369 }
1370}
1371
1372#[derive(Clone, Debug)]
1374#[cfg_attr(test, derive(PartialEq, Eq, serde::Serialize))]
1375pub enum ArithmeticExpr {
1376 Literal(i64),
1378 Reference(ArithmeticTarget),
1380 UnaryOp(UnaryOperator, Box<ArithmeticExpr>),
1382 BinaryOp(BinaryOperator, Box<ArithmeticExpr>, Box<ArithmeticExpr>),
1384 Conditional(
1386 Box<ArithmeticExpr>,
1387 Box<ArithmeticExpr>,
1388 Box<ArithmeticExpr>,
1389 ),
1390 Assignment(ArithmeticTarget, Box<ArithmeticExpr>),
1392 BinaryAssignment(BinaryOperator, ArithmeticTarget, Box<ArithmeticExpr>),
1394 UnaryAssignment(UnaryAssignmentOperator, ArithmeticTarget),
1396}
1397
1398#[cfg(feature = "fuzz-testing")]
1399impl<'a> arbitrary::Arbitrary<'a> for ArithmeticExpr {
1400 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
1401 let variant = u.choose(&[
1402 "Literal",
1403 "Reference",
1404 "UnaryOp",
1405 "BinaryOp",
1406 "Conditional",
1407 "Assignment",
1408 "BinaryAssignment",
1409 "UnaryAssignment",
1410 ])?;
1411
1412 match *variant {
1413 "Literal" => Ok(Self::Literal(i64::arbitrary(u)?)),
1414 "Reference" => Ok(Self::Reference(ArithmeticTarget::arbitrary(u)?)),
1415 "UnaryOp" => Ok(Self::UnaryOp(
1416 UnaryOperator::arbitrary(u)?,
1417 Box::new(Self::arbitrary(u)?),
1418 )),
1419 "BinaryOp" => Ok(Self::BinaryOp(
1420 BinaryOperator::arbitrary(u)?,
1421 Box::new(Self::arbitrary(u)?),
1422 Box::new(Self::arbitrary(u)?),
1423 )),
1424 "Conditional" => Ok(Self::Conditional(
1425 Box::new(Self::arbitrary(u)?),
1426 Box::new(Self::arbitrary(u)?),
1427 Box::new(Self::arbitrary(u)?),
1428 )),
1429 "Assignment" => Ok(Self::Assignment(
1430 ArithmeticTarget::arbitrary(u)?,
1431 Box::new(Self::arbitrary(u)?),
1432 )),
1433 "BinaryAssignment" => Ok(Self::BinaryAssignment(
1434 BinaryOperator::arbitrary(u)?,
1435 ArithmeticTarget::arbitrary(u)?,
1436 Box::new(Self::arbitrary(u)?),
1437 )),
1438 "UnaryAssignment" => Ok(Self::UnaryAssignment(
1439 UnaryAssignmentOperator::arbitrary(u)?,
1440 ArithmeticTarget::arbitrary(u)?,
1441 )),
1442 _ => unreachable!(),
1443 }
1444 }
1445}
1446
1447impl Display for ArithmeticExpr {
1448 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1449 match self {
1450 Self::Literal(literal) => write!(f, "{literal}"),
1451 Self::Reference(target) => write!(f, "{target}"),
1452 Self::UnaryOp(op, operand) => write!(f, "{op}{operand}"),
1453 Self::BinaryOp(op, left, right) => {
1454 if matches!(op, BinaryOperator::Comma) {
1455 write!(f, "{left}{op} {right}")
1456 } else {
1457 write!(f, "{left} {op} {right}")
1458 }
1459 }
1460 Self::Conditional(condition, if_branch, else_branch) => {
1461 write!(f, "{condition} ? {if_branch} : {else_branch}")
1462 }
1463 Self::Assignment(target, value) => write!(f, "{target} = {value}"),
1464 Self::BinaryAssignment(op, target, operand) => {
1465 write!(f, "{target} {op}= {operand}")
1466 }
1467 Self::UnaryAssignment(op, target) => match op {
1468 UnaryAssignmentOperator::PrefixIncrement
1469 | UnaryAssignmentOperator::PrefixDecrement => write!(f, "{op}{target}"),
1470 UnaryAssignmentOperator::PostfixIncrement
1471 | UnaryAssignmentOperator::PostfixDecrement => write!(f, "{target}{op}"),
1472 },
1473 }
1474 }
1475}
1476
1477#[derive(Clone, Copy, Debug)]
1479#[cfg_attr(feature = "fuzz-testing", derive(arbitrary::Arbitrary))]
1480#[cfg_attr(test, derive(PartialEq, Eq, serde::Serialize))]
1481pub enum BinaryOperator {
1482 Power,
1484 Multiply,
1486 Divide,
1488 Modulo,
1490 Comma,
1492 Add,
1494 Subtract,
1496 ShiftLeft,
1498 ShiftRight,
1500 LessThan,
1502 LessThanOrEqualTo,
1504 GreaterThan,
1506 GreaterThanOrEqualTo,
1508 Equals,
1510 NotEquals,
1512 BitwiseAnd,
1514 BitwiseXor,
1516 BitwiseOr,
1518 LogicalAnd,
1520 LogicalOr,
1522}
1523
1524impl Display for BinaryOperator {
1525 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1526 match self {
1527 Self::Power => write!(f, "**"),
1528 Self::Multiply => write!(f, "*"),
1529 Self::Divide => write!(f, "/"),
1530 Self::Modulo => write!(f, "%"),
1531 Self::Comma => write!(f, ","),
1532 Self::Add => write!(f, "+"),
1533 Self::Subtract => write!(f, "-"),
1534 Self::ShiftLeft => write!(f, "<<"),
1535 Self::ShiftRight => write!(f, ">>"),
1536 Self::LessThan => write!(f, "<"),
1537 Self::LessThanOrEqualTo => write!(f, "<="),
1538 Self::GreaterThan => write!(f, ">"),
1539 Self::GreaterThanOrEqualTo => write!(f, ">="),
1540 Self::Equals => write!(f, "=="),
1541 Self::NotEquals => write!(f, "!="),
1542 Self::BitwiseAnd => write!(f, "&"),
1543 Self::BitwiseXor => write!(f, "^"),
1544 Self::BitwiseOr => write!(f, "|"),
1545 Self::LogicalAnd => write!(f, "&&"),
1546 Self::LogicalOr => write!(f, "||"),
1547 }
1548 }
1549}
1550
1551#[derive(Clone, Copy, Debug)]
1553#[cfg_attr(feature = "fuzz-testing", derive(arbitrary::Arbitrary))]
1554#[cfg_attr(test, derive(PartialEq, Eq, serde::Serialize))]
1555pub enum UnaryOperator {
1556 UnaryPlus,
1558 UnaryMinus,
1560 BitwiseNot,
1562 LogicalNot,
1564}
1565
1566impl Display for UnaryOperator {
1567 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1568 match self {
1569 Self::UnaryPlus => write!(f, "+"),
1570 Self::UnaryMinus => write!(f, "-"),
1571 Self::BitwiseNot => write!(f, "~"),
1572 Self::LogicalNot => write!(f, "!"),
1573 }
1574 }
1575}
1576
1577#[derive(Clone, Copy, Debug)]
1579#[cfg_attr(feature = "fuzz-testing", derive(arbitrary::Arbitrary))]
1580#[cfg_attr(test, derive(PartialEq, Eq, serde::Serialize))]
1581pub enum UnaryAssignmentOperator {
1582 PrefixIncrement,
1584 PrefixDecrement,
1586 PostfixIncrement,
1588 PostfixDecrement,
1590}
1591
1592impl Display for UnaryAssignmentOperator {
1593 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1594 match self {
1595 Self::PrefixIncrement => write!(f, "++"),
1596 Self::PrefixDecrement => write!(f, "--"),
1597 Self::PostfixIncrement => write!(f, "++"),
1598 Self::PostfixDecrement => write!(f, "--"),
1599 }
1600 }
1601}
1602
1603#[derive(Clone, Debug)]
1605#[cfg_attr(feature = "fuzz-testing", derive(arbitrary::Arbitrary))]
1606#[cfg_attr(test, derive(PartialEq, Eq, serde::Serialize))]
1607pub enum ArithmeticTarget {
1608 Variable(String),
1610 ArrayElement(String, Box<ArithmeticExpr>),
1612}
1613
1614impl Display for ArithmeticTarget {
1615 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1616 match self {
1617 Self::Variable(name) => write!(f, "{name}"),
1618 Self::ArrayElement(name, index) => write!(f, "{name}[{index}]"),
1619 }
1620 }
1621}