Skip to content

Commit

Permalink
feat: allow foreach to have an else clause (#43)
Browse files Browse the repository at this point in the history
  • Loading branch information
KennedyTedesco committed Jan 23, 2023
1 parent cdf7dd7 commit 4c55576
Show file tree
Hide file tree
Showing 10 changed files with 346 additions and 197 deletions.
170 changes: 95 additions & 75 deletions src/parser/internal/statement/loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,92 +20,112 @@ use crate::tree::statement::r#loop::WhileStatement;
use crate::tree::utils::CommaSeparated;

pub fn foreach_statement(state: &mut State) -> ParseResult<ForeachStatement> {
Ok(ForeachStatement {
comments: state.iterator.comments(),
foreach: utils::skip_keyword(state, TokenKind::Foreach)?,
iterator: 'iterator: {
let expression = if state.iterator.current().kind == TokenKind::LeftParen {
// this could be either:
// 1. foreach ($array as $value)
// 2. foreach ($array as $key => $value) { ... }
// 3. foreach ($array) as $value { ... }
// 4. foreach ($array) as $key => $value { ... }
let left_parenthesis = utils::skip(state, TokenKind::LeftParen)?;
let expression = expression::create(state)?;
if state.iterator.current().kind == TokenKind::As {
// this is either 1 or 2
let r#as = utils::skip_keyword(state, TokenKind::As)?;
let mut value = variable::parse(state)?;
let current = state.iterator.current();
let comments = state.iterator.comments();
let foreach = utils::skip_keyword(state, TokenKind::Foreach)?;

break 'iterator if current.kind == TokenKind::DoubleArrow {
state.iterator.next();
let double_arrow = current.position;
let mut key = variable::parse(state)?;
std::mem::swap(&mut value, &mut key);
let iterator = 'iterator: {
let expression = if state.iterator.current().kind == TokenKind::LeftParen {
// this could be either:
// 1. foreach ($array as $value)
// 2. foreach ($array as $key => $value) { ... }
// 3. foreach ($array) as $value { ... }
// 4. foreach ($array) as $key => $value { ... }
let left_parenthesis = utils::skip(state, TokenKind::LeftParen)?;
let expression = expression::create(state)?;
if state.iterator.current().kind == TokenKind::As {
// this is either 1 or 2
let r#as = utils::skip_keyword(state, TokenKind::As)?;
let mut value = variable::parse(state)?;
let current = state.iterator.current();

ForeachIteratorStatement::ParenthesizedKeyAndValue {
left_parenthesis,
expression,
r#as,
key,
double_arrow,
value,
right_parenthesis: utils::skip(state, TokenKind::RightParen)?,
}
} else {
ForeachIteratorStatement::ParenthesizedValue {
left_parenthesis,
expression,
r#as,
value,
right_parenthesis: utils::skip(state, TokenKind::RightParen)?,
}
};
break 'iterator if current.kind == TokenKind::DoubleArrow {
state.iterator.next();
let double_arrow = current.position;
let mut key = variable::parse(state)?;
std::mem::swap(&mut value, &mut key);

ForeachIteratorStatement::ParenthesizedKeyAndValue {
left_parenthesis,
expression,
r#as,
key,
double_arrow,
value,
right_parenthesis: utils::skip(state, TokenKind::RightParen)?,
}
} else {
// this is either 3 or 4
Expression::Parenthesized(ParenthesizedExpression {
comments: state.iterator.comments(),
ForeachIteratorStatement::ParenthesizedValue {
left_parenthesis,
expression: Box::new(expression),
expression,
r#as,
value,
right_parenthesis: utils::skip(state, TokenKind::RightParen)?,
})
}
}
};
} else {
expression::create(state)?
};
// this is either 3 or 4
Expression::Parenthesized(ParenthesizedExpression {
comments: state.iterator.comments(),
left_parenthesis,
expression: Box::new(expression),
right_parenthesis: utils::skip(state, TokenKind::RightParen)?,
})
}
} else {
expression::create(state)?
};

// this could be either:
// 1. foreach $array as $value { ... }
// 2. foreach $array as $key => $value { ... }
let r#as = utils::skip_keyword(state, TokenKind::As)?;
// this could be either:
// 1. foreach $array as $value { ... }
// 2. foreach $array as $key => $value { ... }
let r#as = utils::skip_keyword(state, TokenKind::As)?;

let mut value = variable::parse(state)?;
let mut value = variable::parse(state)?;

let current = state.iterator.current();
if current.kind == TokenKind::DoubleArrow {
state.iterator.next();
let double_arrow = current.position;
let mut key = variable::parse(state)?;
std::mem::swap(&mut value, &mut key);
let current = state.iterator.current();
if current.kind == TokenKind::DoubleArrow {
state.iterator.next();
let double_arrow = current.position;
let mut key = variable::parse(state)?;
std::mem::swap(&mut value, &mut key);

ForeachIteratorStatement::KeyAndValue {
expression,
r#as,
key,
double_arrow,
value,
}
} else {
ForeachIteratorStatement::Value {
expression,
r#as,
value,
}
ForeachIteratorStatement::KeyAndValue {
expression,
r#as,
key,
double_arrow,
value,
}
},
block: block::block_statement(state)?,
})
} else {
ForeachIteratorStatement::Value {
expression,
r#as,
value,
}
}
};

let block = block::block_statement(state)?;

if state.iterator.current().kind == TokenKind::Else {
Ok(ForeachStatement {
comments,
foreach,
iterator,
block,
r#else: Some(utils::skip_keyword(state, TokenKind::Else)?),
else_block: Some(block::block_statement(state)?),
})
} else {
Ok(ForeachStatement {
comments,
foreach,
iterator,
block,
r#else: None,
else_block: None,
})
}
}

pub fn for_statement(state: &mut State) -> ParseResult<ForStatement> {
Expand Down
14 changes: 13 additions & 1 deletion src/tree/statement/loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ pub struct ForeachStatement {
pub foreach: Keyword,
pub iterator: ForeachIteratorStatement,
pub block: BlockStatement,
pub r#else: Option<Keyword>,
pub else_block: Option<BlockStatement>,
}

#[derive(Debug, PartialEq, Eq, Clone, Hash, Deserialize, Serialize, JsonSchema)]
Expand Down Expand Up @@ -164,7 +166,17 @@ impl Node for ForeachStatement {
}

fn children(&self) -> Vec<&dyn Node> {
vec![&self.foreach, &self.iterator, &self.block]
let mut children: Vec<&dyn Node> = vec![&self.foreach, &self.iterator, &self.block];

if let Some(r#else) = &self.r#else {
children.push(r#else);
}

if let Some(else_block) = &self.else_block {
children.push(else_block);
}

children
}

fn get_description(&self) -> String {
Expand Down
1 change: 1 addition & 0 deletions tests/samples/0070/code.ara
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ function bar(): void {
foreach $a as $b => $c { }
foreach ($a) as $b => $c {}
foreach ($a as $b => $c) {}
foreach ($a as $b => $c) {} else {}

for $a = 0; $a < 10; $a++ { }
for ($a = 0; $a < 10; $a++) {}
Expand Down
Loading

0 comments on commit 4c55576

Please sign in to comment.