Skip to main content

php_ast/ast/
stmts.rs

1use serde::Serialize;
2
3use crate::Span;
4
5use super::{
6    ArenaVec, Attribute, ClassDecl, Comment, EnumDecl, Expr, FunctionDecl, Ident, InterfaceDecl,
7    Name, TraitDecl,
8};
9
10fn is_false(b: &bool) -> bool {
11    !b
12}
13
14#[derive(Debug, Serialize)]
15pub struct Stmt<'arena, 'src> {
16    pub kind: StmtKind<'arena, 'src>,
17    pub span: Span,
18}
19
20#[derive(Debug, Serialize)]
21pub enum StmtKind<'arena, 'src> {
22    /// Expression statement (e.g. `foo();`)
23    Expression(&'arena Expr<'arena, 'src>),
24
25    /// Echo statement: `echo expr1, expr2;`
26    Echo(ArenaVec<'arena, Expr<'arena, 'src>>),
27
28    /// Return statement: `return expr;`
29    Return(Option<&'arena Expr<'arena, 'src>>),
30
31    /// Block statement: `{ stmts }`
32    Block(ArenaVec<'arena, Stmt<'arena, 'src>>),
33
34    /// If statement
35    If(&'arena IfStmt<'arena, 'src>),
36
37    /// While loop
38    While(&'arena WhileStmt<'arena, 'src>),
39
40    /// For loop
41    For(&'arena ForStmt<'arena, 'src>),
42
43    /// Foreach loop
44    Foreach(&'arena ForeachStmt<'arena, 'src>),
45
46    /// Do-while loop
47    DoWhile(&'arena DoWhileStmt<'arena, 'src>),
48
49    /// Function declaration
50    Function(&'arena FunctionDecl<'arena, 'src>),
51
52    /// Break statement
53    Break(Option<&'arena Expr<'arena, 'src>>),
54
55    /// Continue statement
56    Continue(Option<&'arena Expr<'arena, 'src>>),
57
58    /// Switch statement
59    Switch(&'arena SwitchStmt<'arena, 'src>),
60
61    /// Goto statement
62    Goto(Ident<'src>),
63
64    /// Label statement
65    Label(&'arena str),
66
67    /// Declare statement
68    Declare(&'arena DeclareStmt<'arena, 'src>),
69
70    /// Unset statement
71    Unset(ArenaVec<'arena, Expr<'arena, 'src>>),
72
73    /// Throw statement (also can be expression in PHP 8)
74    Throw(&'arena Expr<'arena, 'src>),
75
76    /// Try/catch/finally
77    TryCatch(&'arena TryCatchStmt<'arena, 'src>),
78
79    /// Global declaration
80    Global(ArenaVec<'arena, Expr<'arena, 'src>>),
81
82    /// Class declaration
83    Class(&'arena ClassDecl<'arena, 'src>),
84
85    /// Interface declaration
86    Interface(&'arena InterfaceDecl<'arena, 'src>),
87
88    /// Trait declaration
89    Trait(&'arena TraitDecl<'arena, 'src>),
90
91    /// Enum declaration
92    Enum(&'arena EnumDecl<'arena, 'src>),
93
94    /// Namespace declaration
95    Namespace(&'arena NamespaceDecl<'arena, 'src>),
96
97    /// Use declaration
98    Use(&'arena UseDecl<'arena, 'src>),
99
100    /// Top-level constant: `const FOO = expr;`
101    Const(ArenaVec<'arena, ConstItem<'arena, 'src>>),
102
103    /// Static variable declaration: `static $x = 1;`
104    StaticVar(ArenaVec<'arena, StaticVar<'arena, 'src>>),
105
106    /// __halt_compiler(); with remaining data
107    HaltCompiler(&'src str),
108
109    /// Nop (empty statement `;`)
110    Nop,
111
112    /// Inline HTML
113    InlineHtml(&'src str),
114
115    /// Error placeholder — parser always produces a tree
116    Error,
117}
118
119#[derive(Debug, Serialize)]
120pub struct IfStmt<'arena, 'src> {
121    pub condition: Expr<'arena, 'src>,
122    pub then_branch: &'arena Stmt<'arena, 'src>,
123    pub elseif_branches: ArenaVec<'arena, ElseIfBranch<'arena, 'src>>,
124    pub else_branch: Option<&'arena Stmt<'arena, 'src>>,
125    #[serde(default, skip_serializing_if = "is_false")]
126    pub uses_alternative: bool,
127}
128
129#[derive(Debug, Serialize)]
130pub struct ElseIfBranch<'arena, 'src> {
131    pub condition: Expr<'arena, 'src>,
132    pub body: Stmt<'arena, 'src>,
133    pub span: Span,
134}
135
136#[derive(Debug, Serialize)]
137pub struct WhileStmt<'arena, 'src> {
138    pub condition: Expr<'arena, 'src>,
139    pub body: &'arena Stmt<'arena, 'src>,
140    #[serde(default, skip_serializing_if = "is_false")]
141    pub uses_alternative: bool,
142}
143
144#[derive(Debug, Serialize)]
145pub struct ForStmt<'arena, 'src> {
146    pub init: ArenaVec<'arena, Expr<'arena, 'src>>,
147    pub condition: ArenaVec<'arena, Expr<'arena, 'src>>,
148    pub update: ArenaVec<'arena, Expr<'arena, 'src>>,
149    pub body: &'arena Stmt<'arena, 'src>,
150    #[serde(default, skip_serializing_if = "is_false")]
151    pub uses_alternative: bool,
152}
153
154#[derive(Debug, Serialize)]
155pub struct ForeachStmt<'arena, 'src> {
156    pub expr: Expr<'arena, 'src>,
157    pub key: Option<Expr<'arena, 'src>>,
158    pub value: Expr<'arena, 'src>,
159    pub body: &'arena Stmt<'arena, 'src>,
160    #[serde(default, skip_serializing_if = "is_false")]
161    pub uses_alternative: bool,
162}
163
164#[derive(Debug, Serialize)]
165pub struct DoWhileStmt<'arena, 'src> {
166    pub body: &'arena Stmt<'arena, 'src>,
167    pub condition: Expr<'arena, 'src>,
168}
169
170#[derive(Debug, Serialize)]
171pub struct SwitchStmt<'arena, 'src> {
172    pub expr: Expr<'arena, 'src>,
173    pub cases: ArenaVec<'arena, SwitchCase<'arena, 'src>>,
174    #[serde(default, skip_serializing_if = "is_false")]
175    pub uses_alternative: bool,
176}
177
178#[derive(Debug, Serialize)]
179pub struct SwitchCase<'arena, 'src> {
180    pub value: Option<Expr<'arena, 'src>>,
181    pub body: ArenaVec<'arena, Stmt<'arena, 'src>>,
182    pub span: Span,
183}
184
185#[derive(Debug, Serialize)]
186pub struct TryCatchStmt<'arena, 'src> {
187    pub body: ArenaVec<'arena, Stmt<'arena, 'src>>,
188    pub catches: ArenaVec<'arena, CatchClause<'arena, 'src>>,
189    pub finally: Option<ArenaVec<'arena, Stmt<'arena, 'src>>>,
190}
191
192#[derive(Debug, Serialize)]
193pub struct CatchClause<'arena, 'src> {
194    pub types: ArenaVec<'arena, Name<'arena, 'src>>,
195    pub var: Option<&'src str>,
196    pub body: ArenaVec<'arena, Stmt<'arena, 'src>>,
197    pub span: Span,
198}
199
200#[derive(Debug, Serialize)]
201pub struct NamespaceDecl<'arena, 'src> {
202    pub name: Option<Name<'arena, 'src>>,
203    pub body: NamespaceBody<'arena, 'src>,
204}
205
206#[derive(Debug, Serialize)]
207pub enum NamespaceBody<'arena, 'src> {
208    /// `namespace Foo { … }` — braced form; the statements are scoped to this namespace.
209    Braced(ArenaVec<'arena, Stmt<'arena, 'src>>),
210    /// `namespace Foo;` — simple form; all subsequent statements until the next `namespace` or EOF are in scope.
211    Simple,
212}
213
214#[derive(Debug, Serialize)]
215pub struct DeclareStmt<'arena, 'src> {
216    pub directives: ArenaVec<'arena, (&'src str, Expr<'arena, 'src>)>,
217    pub body: Option<&'arena Stmt<'arena, 'src>>,
218    #[serde(default, skip_serializing_if = "is_false")]
219    pub uses_alternative: bool,
220}
221
222#[derive(Debug, Serialize)]
223pub struct UseDecl<'arena, 'src> {
224    pub kind: UseKind,
225    pub uses: ArenaVec<'arena, UseItem<'arena, 'src>>,
226}
227
228#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
229pub enum UseKind {
230    /// `use Foo\Bar` — imports a class, interface, trait, or enum.
231    Normal,
232    /// `use function Foo\bar` — imports a function.
233    Function,
234    /// `use const Foo\BAR` — imports a constant.
235    Const,
236}
237
238#[derive(Debug, Serialize)]
239pub struct UseItem<'arena, 'src> {
240    pub name: Name<'arena, 'src>,
241    pub alias: Option<&'src str>,
242    #[serde(skip_serializing_if = "Option::is_none")]
243    pub kind: Option<UseKind>,
244    pub span: Span,
245}
246
247#[derive(Debug, Serialize)]
248pub struct ConstItem<'arena, 'src> {
249    pub name: Ident<'src>,
250    pub value: Expr<'arena, 'src>,
251    pub attributes: ArenaVec<'arena, Attribute<'arena, 'src>>,
252    pub span: Span,
253    #[serde(skip_serializing_if = "Option::is_none")]
254    pub doc_comment: Option<Comment<'src>>,
255}
256
257#[derive(Debug, Serialize)]
258pub struct StaticVar<'arena, 'src> {
259    pub name: Ident<'src>,
260    pub default: Option<Expr<'arena, 'src>>,
261    pub span: Span,
262}