Skip to main content

mago_syntax/ast/ast/loop/
foreach.rs

1use serde::Serialize;
2use strum::Display;
3
4use mago_span::HasSpan;
5use mago_span::Span;
6
7use crate::ast::ast::expression::Expression;
8use crate::ast::ast::keyword::Keyword;
9use crate::ast::ast::statement::Statement;
10use crate::ast::ast::terminator::Terminator;
11use crate::ast::sequence::Sequence;
12
13/// Represents a foreach statement in PHP.
14///
15/// Example:
16///
17/// ```php
18/// <?php
19///
20/// foreach ($array as $value) {
21///    echo $value;
22/// }
23/// ```
24#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, PartialOrd, Ord)]
25pub struct Foreach<'arena> {
26    pub foreach: Keyword<'arena>,
27    pub left_parenthesis: Span,
28    pub expression: &'arena Expression<'arena>,
29    pub r#as: Keyword<'arena>,
30    pub target: ForeachTarget<'arena>,
31    pub right_parenthesis: Span,
32    pub body: ForeachBody<'arena>,
33}
34
35/// Represents the target of a foreach statement.
36#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, PartialOrd, Ord, Display)]
37#[serde(tag = "type", content = "value")]
38pub enum ForeachTarget<'arena> {
39    Value(ForeachValueTarget<'arena>),
40    KeyValue(ForeachKeyValueTarget<'arena>),
41}
42
43/// Represents the target of a foreach statement that only assigns the value.
44///
45/// Example:
46///
47/// ```php
48/// <?php
49///
50/// foreach ($array as $value) {
51///   echo $value;
52/// }
53/// ```
54#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, PartialOrd, Ord)]
55pub struct ForeachValueTarget<'arena> {
56    pub value: &'arena Expression<'arena>,
57}
58
59/// Represents the target of a foreach statement that assigns both the key and value.
60///
61/// Example:
62///
63/// ```php
64/// <?php
65///
66/// foreach ($array as $key => $value) {
67///   echo $key . ' => ' . $value . PHP_EOL;
68/// }
69/// ```
70#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, PartialOrd, Ord)]
71pub struct ForeachKeyValueTarget<'arena> {
72    pub key: &'arena Expression<'arena>,
73    pub double_arrow: Span,
74    pub value: &'arena Expression<'arena>,
75}
76
77/// Represents the body of a foreach statement.
78#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, PartialOrd, Ord, Display)]
79#[serde(tag = "type", content = "value")]
80pub enum ForeachBody<'arena> {
81    /// The body is a statement.
82    Statement(&'arena Statement<'arena>),
83    /// The body is a colon-delimited body.
84    ColonDelimited(ForeachColonDelimitedBody<'arena>),
85}
86
87/// Represents a colon-delimited body of a foreach statement.
88///
89/// Example:
90///
91/// ```php
92/// <?php
93///
94/// foreach ($array as $value):
95///   echo $value;
96/// endforeach;
97/// ```
98#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, PartialOrd, Ord)]
99pub struct ForeachColonDelimitedBody<'arena> {
100    pub colon: Span,
101    pub statements: Sequence<'arena, Statement<'arena>>,
102    pub end_foreach: Keyword<'arena>,
103    pub terminator: Terminator<'arena>,
104}
105
106impl<'arena> ForeachTarget<'arena> {
107    #[must_use]
108    pub fn key(&self) -> Option<&Expression<'arena>> {
109        match self {
110            ForeachTarget::Value(_) => None,
111            ForeachTarget::KeyValue(key_value) => Some(key_value.key),
112        }
113    }
114
115    #[must_use]
116    pub fn value(&self) -> &Expression<'arena> {
117        match self {
118            ForeachTarget::Value(value) => value.value,
119            ForeachTarget::KeyValue(key_value) => key_value.value,
120        }
121    }
122}
123
124impl<'arena> ForeachBody<'arena> {
125    #[must_use]
126    pub fn statements(&self) -> &[Statement<'arena>] {
127        match self {
128            ForeachBody::Statement(statement) => std::slice::from_ref(statement),
129            ForeachBody::ColonDelimited(body) => body.statements.as_slice(),
130        }
131    }
132}
133
134impl HasSpan for Foreach<'_> {
135    fn span(&self) -> Span {
136        self.foreach.span().join(self.body.span())
137    }
138}
139
140impl HasSpan for ForeachTarget<'_> {
141    fn span(&self) -> Span {
142        match self {
143            ForeachTarget::Value(value) => value.span(),
144            ForeachTarget::KeyValue(key_value) => key_value.span(),
145        }
146    }
147}
148
149impl HasSpan for ForeachValueTarget<'_> {
150    fn span(&self) -> Span {
151        self.value.span()
152    }
153}
154
155impl HasSpan for ForeachKeyValueTarget<'_> {
156    fn span(&self) -> Span {
157        self.key.span().join(self.value.span())
158    }
159}
160
161impl HasSpan for ForeachBody<'_> {
162    fn span(&self) -> Span {
163        match self {
164            ForeachBody::Statement(statement) => statement.span(),
165            ForeachBody::ColonDelimited(body) => body.span(),
166        }
167    }
168}
169
170impl HasSpan for ForeachColonDelimitedBody<'_> {
171    fn span(&self) -> Span {
172        self.colon.join(self.terminator.span())
173    }
174}