use super::{PResult, Parser};
use crate::ast::{BinOp, Binding, Block, Expr, Stmt};
use crate::source::token::{Punct, Tok};
use crate::words::{ALL, Role};
const ASSIGN: &[(Punct, Option<BinOp>)] = &[
(Punct::Eq, None),
(Punct::PlusEq, Some(BinOp::Add)),
(Punct::MinusEq, Some(BinOp::Sub)),
(Punct::StarEq, Some(BinOp::Mul)),
(Punct::SlashEq, Some(BinOp::Div)),
(Punct::PercentEq, Some(BinOp::Rem)),
(Punct::NullAssign, Some(BinOp::Coalesce)),
(Punct::AmpEq, Some(BinOp::BitAnd)),
(Punct::PipeEq, Some(BinOp::BitOr)),
(Punct::CaretEq, Some(BinOp::BitXor)),
(Punct::ShlEq, Some(BinOp::Shl)),
];
impl Parser<'_, '_, '_> {
pub(crate) fn block(&mut self) -> PResult<Block> {
let start = self.need(Punct::LBrace)?;
let stmts = self.braces(|p| p.stmts(false))?;
Ok(Block {
stmts,
span: start.to(self.prev()),
})
}
pub(crate) fn value_block(&mut self) -> PResult<Block> {
let start = self.need(Punct::LBrace)?;
let stmts = self.braces(|p| p.stmts(true))?;
Ok(Block {
stmts,
span: start.to(self.prev()),
})
}
pub(crate) fn stmts(&mut self, value: bool) -> PResult<Vec<Stmt>> {
let mut stmts = Vec::new();
loop {
self.skip_docs();
if self.eat(Punct::RBrace) {
return Ok(stmts);
}
if *self.tok() == Tok::Eof {
return Err(self.expected("`}`"));
}
if self.await_using_ahead() && !self.nth_is(2, Punct::LParen) {
match self.await_using_rest(value) {
Ok(rest) => {
stmts.extend(rest);
return Ok(stmts);
}
Err(_) => {
self.recover();
continue;
}
}
}
match self.stmt_in(value) {
Ok(s) => stmts.push(s),
Err(_) => self.recover(),
}
}
}
pub(crate) fn stmt(&mut self) -> PResult<Stmt> {
self.stmt_in(false)
}
fn stmt_in(&mut self, value: bool) -> PResult<Stmt> {
if self.at(Punct::Semi) {
let span = self.bump().span;
return Ok(Stmt::Block(Block {
stmts: Vec::new(),
span,
}));
}
if self.at(Punct::LBrace) {
return self.block().map(Stmt::Block);
}
if let Tok::Ident(name) = self.tok()
&& self.nth_is(1, Punct::Colon)
{
let name = crate::ast::Name {
text: name.clone(),
span: self.span(),
};
self.bump();
self.bump();
return Ok(Stmt::Label(name));
}
if self.await_using_ahead() {
return self.await_using_stmt();
}
if self.at_word(&crate::words::r#await::WORD)
&& self.nth_word(1, &crate::words::foreach::WORD)
{
return crate::words::foreach::awaited(self);
}
let word = ALL.iter().find_map(|w| {
w.role(|r| match *r {
Role::Stmt { parse } if self.at_word(w) => Some(parse),
_ => None,
})
});
if let Some(parse) = word {
return parse(self);
}
if self.local_fn_ahead() {
return Ok(Stmt::Function(Box::new(self.decl()?)));
}
if let Some(len) = self.typed_ahead()
&& matches!(self.nth(len + 1), Tok::Punct(Punct::Eq | Punct::Semi))
{
let s = self.typed_local()?;
self.end()?;
return Ok(s);
}
let expr = self.expr()?;
if value && self.at(Punct::RBrace) {
return Ok(Stmt::Value(expr));
}
let s = self.assign_or(expr)?;
self.end()?;
Ok(s)
}
fn local_fn_ahead(&self) -> bool {
let mut at = 0;
while [&crate::words::r#static::WORD, &crate::words::r#async::WORD]
.iter()
.any(|w| self.nth_word(at, w))
{
at += 1;
}
let ret = if self.nth_word(at, &crate::words::void::WORD) {
Some(1)
} else {
self.type_len(at)
};
let Some(len) = ret else {
return false;
};
matches!(self.nth(at + len), Tok::Ident(_))
&& matches!(
self.nth(at + len + 1),
Tok::Punct(Punct::LParen | Punct::Lt)
)
}
pub(crate) fn typed_local(&mut self) -> PResult<Stmt> {
let ty = self.ty()?;
let name = self.name("a name")?;
let value = if self.eat(Punct::Eq) {
Some(self.expr()?)
} else {
None
};
Ok(Stmt::Local {
ty: Some(ty),
binding: Binding::One(name),
value,
mutable: true,
})
}
pub(crate) fn arrow_body(&mut self) -> PResult<Result<Expr, Block>> {
let start = self.span();
let expr = self.expr()?;
Ok(match self.assign_or(expr)? {
Stmt::Expr(e) => Ok(e),
assign => Err(Block {
stmts: vec![assign],
span: start.to(self.prev()),
}),
})
}
pub(crate) fn assign_or(&mut self, expr: Expr) -> PResult<Stmt> {
let op = if self.at(Punct::Gt) && self.joined(Punct::GtEq) {
self.bump();
Some(BinOp::Shr)
} else {
let assign = ASSIGN.iter().find(|(p, _)| self.at(*p));
let Some(&(_, op)) = assign else {
return Ok(Stmt::Expr(expr));
};
op
};
self.bump();
let value = self.expr()?;
Ok(Stmt::Assign {
place: expr,
op,
value,
})
}
pub(crate) fn optional_value(&mut self) -> PResult<Option<Expr>> {
if self.at(Punct::Semi) {
Ok(None)
} else {
self.expr().map(Some)
}
}
pub(crate) fn paren_cond(&mut self) -> PResult<Expr> {
self.need(Punct::LParen)?;
let cond = self.braces(Self::expr)?;
self.need(Punct::RParen)?;
Ok(cond)
}
}