1use crate::expect_literal;
2use crate::lexer::token::OpenTagKind;
3use crate::lexer::token::Token;
4use crate::lexer::token::TokenKind;
5use crate::lexer::Lexer;
6use crate::parser::ast::declares::DeclareBody;
7use crate::parser::ast::declares::DeclareEntry;
8use crate::parser::ast::declares::DeclareEntryGroup;
9use crate::parser::ast::declares::DeclareStatement;
10use crate::parser::ast::variables::Variable;
11use crate::parser::ast::{Program, Statement, StaticVar};
12use crate::parser::error::ParseErrorStack;
13use crate::parser::error::ParseResult;
14use crate::parser::internal::attributes;
15use crate::parser::internal::blocks;
16use crate::parser::internal::classes;
17use crate::parser::internal::constants;
18use crate::parser::internal::control_flow;
19use crate::parser::internal::enums;
20use crate::parser::internal::functions;
21use crate::parser::internal::goto;
22use crate::parser::internal::identifiers;
23use crate::parser::internal::interfaces;
24use crate::parser::internal::loops;
25use crate::parser::internal::namespaces;
26use crate::parser::internal::traits;
27use crate::parser::internal::try_block;
28use crate::parser::internal::uses;
29use crate::parser::internal::utils;
30use crate::parser::internal::variables;
31use crate::parser::state::State;
32
33pub use crate::lexer::stream::TokenStream;
34
35use self::ast::ClosingTagStatement;
36use self::ast::EchoOpeningTagStatement;
37use self::ast::EchoStatement;
38use self::ast::ExpressionStatement;
39use self::ast::FullOpeningTagStatement;
40use self::ast::GlobalStatement;
41use self::ast::HaltCompilerStatement;
42use self::ast::InlineHtmlStatement;
43use self::ast::ReturnStatement;
44use self::ast::ShortOpeningTagStatement;
45use self::ast::StaticStatement;
46use self::internal::precedences::Precedence;
47
48pub mod ast;
49pub mod error;
50
51mod expressions;
52mod internal;
53mod macros;
54mod state;
55
56pub fn parse<B: ?Sized + AsRef<[u8]>>(input: &B) -> Result<Program, ParseErrorStack> {
57 let lexer = Lexer::new();
58 let tokens = match lexer.tokenize(input) {
59 Ok(tokens) => tokens,
60 Err(error) => {
61 return Err(ParseErrorStack {
62 errors: vec![error.into()],
63 partial: Vec::new(),
64 })
65 }
66 };
67
68 construct(&tokens)
69}
70
71pub fn construct(tokens: &[Token]) -> Result<Program, ParseErrorStack> {
72 let mut stream = TokenStream::new(tokens);
73 let mut state = State::new(&mut stream);
74
75 let mut program = Program::new();
76
77 while !state.stream.is_eof() {
78 let statement = match top_level_statement(&mut state) {
79 Ok(statement) => statement,
80 Err(error) => {
81 let mut previous = state.errors;
82 previous.push(error);
83
84 return Err(ParseErrorStack {
85 errors: previous,
86 partial: program,
87 });
88 }
89 };
90
91 program.push(statement);
92 }
93
94 let errors = state.errors;
95 if !errors.is_empty() {
96 return Err(ParseErrorStack {
97 errors,
98 partial: program,
99 });
100 }
101
102 Ok(program.to_vec())
103}
104
105fn top_level_statement(state: &mut State) -> ParseResult<Statement> {
106 let statement = match &state.stream.current().kind {
107 TokenKind::Namespace => namespaces::namespace(state)?,
108 TokenKind::Use => uses::use_statement(state)?,
109 TokenKind::Const => Statement::Constant(constants::parse(state)?),
110 TokenKind::HaltCompiler => {
111 state.stream.next();
112
113 let content = if let TokenKind::InlineHtml = state.stream.current().kind.clone() {
114 let content = state.stream.current().value.clone();
115 state.stream.next();
116 Some(content)
117 } else {
118 None
119 };
120
121 Statement::HaltCompiler(HaltCompilerStatement { content })
122 }
123 _ => statement(state)?,
124 };
125
126 Ok(statement)
127}
128
129fn statement(state: &mut State) -> ParseResult<Statement> {
130 let has_attributes = attributes::gather_attributes(state)?;
131
132 let current = state.stream.current();
133 let peek = state.stream.peek();
134 let statement = if has_attributes {
135 match ¤t.kind {
136 TokenKind::Abstract => classes::parse(state)?,
137 TokenKind::Readonly if peek.kind != TokenKind::LeftParen => classes::parse(state)?,
138 TokenKind::Final => classes::parse(state)?,
139 TokenKind::Class => classes::parse(state)?,
140 TokenKind::Interface => interfaces::parse(state)?,
141 TokenKind::Trait => traits::parse(state)?,
142 TokenKind::Enum
143 if !matches!(
144 peek.kind,
145 TokenKind::LeftParen | TokenKind::DoubleColon | TokenKind::Colon,
146 ) =>
147 {
148 enums::parse(state)?
149 }
150 TokenKind::Function
151 if identifiers::is_identifier_maybe_soft_reserved(&peek.kind)
152 || peek.kind == TokenKind::Ampersand =>
153 {
154 if peek.kind == TokenKind::Ampersand {
155 if !identifiers::is_identifier_maybe_soft_reserved(
156 &state.stream.lookahead(1).kind,
157 ) {
158 return Ok(Statement::Expression(ExpressionStatement {
159 expression: expressions::attributes(state, &Precedence::Lowest)?,
160 ending: utils::skip_ending(state)?,
161 }));
162 }
163
164 functions::function(state)?
165 } else {
166 functions::function(state)?
167 }
168 }
169 _ => Statement::Expression(ExpressionStatement {
170 expression: expressions::attributes(state, &Precedence::Lowest)?,
171 ending: utils::skip_ending(state)?,
172 }),
173 }
174 } else {
175 match ¤t.kind {
176 TokenKind::OpenTag(OpenTagKind::Echo) => {
177 let span = current.span;
178 state.stream.next();
179
180 Statement::EchoOpeningTag(EchoOpeningTagStatement { span })
181 }
182 TokenKind::OpenTag(OpenTagKind::Full) => {
183 let span = current.span;
184 state.stream.next();
185
186 Statement::FullOpeningTag(FullOpeningTagStatement { span })
187 }
188 TokenKind::OpenTag(OpenTagKind::Short) => {
189 let span = current.span;
190 state.stream.next();
191
192 Statement::ShortOpeningTag(ShortOpeningTagStatement { span })
193 }
194 TokenKind::CloseTag => {
195 let span = current.span;
196 state.stream.next();
197
198 Statement::ClosingTag(ClosingTagStatement { span })
199 }
200 TokenKind::Abstract => classes::parse(state)?,
201 TokenKind::Readonly if peek.kind != TokenKind::LeftParen => classes::parse(state)?,
202 TokenKind::Final => classes::parse(state)?,
203 TokenKind::Class => classes::parse(state)?,
204 TokenKind::Interface => interfaces::parse(state)?,
205 TokenKind::Trait => traits::parse(state)?,
206 TokenKind::Enum
207 if !matches!(
208 peek.kind,
209 TokenKind::LeftParen | TokenKind::DoubleColon | TokenKind::Colon,
210 ) =>
211 {
212 enums::parse(state)?
213 }
214 TokenKind::Function
215 if identifiers::is_identifier_maybe_soft_reserved(&peek.kind)
216 || peek.kind == TokenKind::Ampersand =>
217 {
218 if peek.kind == TokenKind::Ampersand {
219 if !identifiers::is_identifier_maybe_soft_reserved(
220 &state.stream.lookahead(1).kind,
221 ) {
222 return Ok(Statement::Expression(ExpressionStatement {
223 expression: expressions::attributes(state, &Precedence::Lowest)?,
224 ending: utils::skip_ending(state)?,
225 }));
226 }
227
228 functions::function(state)?
229 } else {
230 functions::function(state)?
231 }
232 }
233 TokenKind::Goto => goto::goto_statement(state)?,
234 token
235 if identifiers::is_identifier_maybe_reserved(token)
236 && peek.kind == TokenKind::Colon =>
237 {
238 goto::label_statement(state)?
239 }
240 TokenKind::Declare => {
241 let span = utils::skip(state, TokenKind::Declare)?;
242
243 let entries = {
244 let start = utils::skip_left_parenthesis(state)?;
245 let mut entries = Vec::new();
246 loop {
247 let key = identifiers::identifier(state)?;
248 let span = utils::skip(state, TokenKind::Equals)?;
249 let value = expect_literal!(state);
250
251 entries.push(DeclareEntry {
252 key,
253 equals: span,
254 value,
255 });
256
257 if state.stream.current().kind == TokenKind::Comma {
258 state.stream.next();
259 } else {
260 break;
261 }
262 }
263 let end = utils::skip_right_parenthesis(state)?;
264
265 DeclareEntryGroup {
266 left_parenthesis: start,
267 entries,
268 right_parenthesis: end,
269 }
270 };
271
272 let body = match state.stream.current().kind.clone() {
273 TokenKind::SemiColon => {
274 let span = utils::skip_semicolon(state)?;
275
276 DeclareBody::Noop { semicolon: span }
277 }
278 TokenKind::LeftBrace => {
279 let start = utils::skip_left_brace(state)?;
280 let statements =
281 blocks::multiple_statements_until(state, &TokenKind::RightBrace)?;
282 let end = utils::skip_right_brace(state)?;
283
284 DeclareBody::Braced {
285 left_brace: start,
286 statements,
287 right_brace: end,
288 }
289 }
290 TokenKind::Colon => {
291 let start = utils::skip_colon(state)?;
292 let statements =
293 blocks::multiple_statements_until(state, &TokenKind::EndDeclare)?;
294 let end = (
295 utils::skip(state, TokenKind::EndDeclare)?,
296 utils::skip_semicolon(state)?,
297 );
298
299 DeclareBody::Block {
300 colon: start,
301 statements,
302 end,
303 }
304 }
305 _ => {
306 let expression = expressions::create(state)?;
307 let end = utils::skip_semicolon(state)?;
308
309 DeclareBody::Expression {
310 expression,
311 semicolon: end,
312 }
313 }
314 };
315
316 Statement::Declare(DeclareStatement {
317 declare: span,
318 entries,
319 body,
320 })
321 }
322 TokenKind::Global => {
323 let span = current.span;
324 state.stream.next();
325
326 let mut variables = vec![];
327 loop {
329 variables.push(variables::dynamic_variable(state)?);
330
331 if state.stream.current().kind == TokenKind::Comma {
332 state.stream.next();
333 } else {
334 break;
335 }
336 }
337
338 utils::skip_semicolon(state)?;
339 Statement::Global(GlobalStatement {
340 global: span,
341 variables,
342 })
343 }
344 TokenKind::Static if matches!(peek.kind, TokenKind::Variable) => {
345 state.stream.next();
346
347 let mut vars = vec![];
348
349 loop {
351 let var = variables::simple_variable(state)?;
352 let mut default = None;
353
354 if state.stream.current().kind == TokenKind::Equals {
355 state.stream.next();
356
357 default = Some(expressions::create(state)?);
358 }
359
360 vars.push(StaticVar {
361 var: Variable::SimpleVariable(var),
362 default,
363 });
364
365 if state.stream.current().kind == TokenKind::Comma {
366 state.stream.next();
367 } else {
368 break;
369 }
370 }
371
372 utils::skip_semicolon(state)?;
373
374 Statement::Static(StaticStatement { vars })
375 }
376 TokenKind::InlineHtml => {
377 let html = state.stream.current().value.clone();
378 state.stream.next();
379
380 Statement::InlineHtml(InlineHtmlStatement { html })
381 }
382 TokenKind::Do => loops::do_while_statement(state)?,
383 TokenKind::While => loops::while_statement(state)?,
384 TokenKind::For => loops::for_statement(state)?,
385 TokenKind::Foreach => loops::foreach_statement(state)?,
386 TokenKind::Continue => loops::continue_statement(state)?,
387 TokenKind::Break => loops::break_statement(state)?,
388 TokenKind::Switch => control_flow::switch_statement(state)?,
389 TokenKind::If => control_flow::if_statement(state)?,
390 TokenKind::Try => try_block::try_block(state)?,
391 TokenKind::LeftBrace => blocks::block_statement(state)?,
392 TokenKind::SemiColon => {
393 let start = current.span;
394
395 state.stream.next();
396
397 Statement::Noop(start)
398 }
399 TokenKind::Echo => {
400 state.stream.next();
401
402 let mut values = Vec::new();
403 loop {
404 values.push(expressions::create(state)?);
405
406 if state.stream.current().kind == TokenKind::Comma {
407 state.stream.next();
408 } else {
409 break;
410 }
411 }
412
413 Statement::Echo(EchoStatement {
414 echo: current.span,
415 values,
416 ending: utils::skip_ending(state)?,
417 })
418 }
419 TokenKind::Return => {
420 state.stream.next();
421
422 let value = if matches!(
423 state.stream.current().kind,
424 TokenKind::SemiColon | TokenKind::CloseTag
425 ) {
426 None
427 } else {
428 expressions::create(state).map(Some)?
429 };
430
431 Statement::Return(ReturnStatement {
432 r#return: current.span,
433 value,
434 ending: utils::skip_ending(state)?,
435 })
436 }
437 _ => Statement::Expression(ExpressionStatement {
438 expression: expressions::create(state)?,
439 ending: utils::skip_ending(state)?,
440 }),
441 }
442 };
443
444 Ok(statement)
445}