use super::super::expr::errors::{ParseError, ParseErrorKind, ParseResult};
use super::super::*;
impl Parser {
pub(in super::super) fn parse_ts_loop_stmt(&mut self) -> ParseResult<IrNode> {
#[cfg(debug_assertions)]
let debug_parser = std::env::var("MF_DEBUG_PARSER").is_ok();
let keyword = self
.current()
.ok_or_else(|| {
ParseError::unexpected_eof(self.current_byte_offset(), "loop statement")
})?
.text
.clone();
#[cfg(debug_assertions)]
if debug_parser {
eprintln!("[MF_DEBUG] parse_ts_loop_stmt: keyword={:?}", keyword);
}
match keyword.as_str() {
"for" => {
if let Some(structured) = self.try_parse_for_in_of()? {
return Ok(structured);
}
self.parse_ts_for_stmt()
}
"while" => self.parse_ts_while_stmt(),
"do" => self.parse_ts_do_while_stmt(),
_ => Err(
ParseError::new(ParseErrorKind::UnexpectedToken, self.current_byte_offset())
.with_context("expected 'for', 'while', or 'do'"),
),
}
}
fn parse_ts_for_stmt(&mut self) -> ParseResult<IrNode> {
#[cfg(debug_assertions)]
let debug_parser = std::env::var("MF_DEBUG_PARSER").is_ok();
let start_byte = self.current_byte_offset();
self.consume(); self.skip_whitespace();
#[cfg(debug_assertions)]
if debug_parser {
eprintln!("[MF_DEBUG] parse_ts_for_stmt: parsing C-style for loop");
}
self.expect(SyntaxKind::LParen).ok_or_else(|| {
ParseError::new(
ParseErrorKind::MissingClosingParen,
self.current_byte_offset(),
)
.with_context("expected '(' after 'for'")
})?;
self.skip_whitespace();
let init = if self.at(SyntaxKind::Semicolon) {
None
} else if self.at(SyntaxKind::ConstKw)
|| self.at(SyntaxKind::LetKw)
|| self.at(SyntaxKind::VarKw)
{
Some(Box::new(self.parse_for_init_var_decl()?))
} else {
Some(Box::new(
self.parse_ts_expr_until(&[SyntaxKind::Semicolon])?,
))
};
self.skip_whitespace();
self.expect(SyntaxKind::Semicolon).ok_or_else(|| {
ParseError::new(ParseErrorKind::UnexpectedToken, self.current_byte_offset())
.with_context("expected ';' after for loop init")
})?;
self.skip_whitespace();
let test = if self.at(SyntaxKind::Semicolon) {
None
} else {
Some(Box::new(
self.parse_ts_expr_until(&[SyntaxKind::Semicolon])?,
))
};
self.skip_whitespace();
self.expect(SyntaxKind::Semicolon).ok_or_else(|| {
ParseError::new(ParseErrorKind::UnexpectedToken, self.current_byte_offset())
.with_context("expected ';' after for loop test")
})?;
self.skip_whitespace();
let update = if self.at(SyntaxKind::RParen) {
None
} else {
Some(Box::new(self.parse_ts_expr_until(&[SyntaxKind::RParen])?))
};
self.skip_whitespace();
self.expect(SyntaxKind::RParen).ok_or_else(|| {
ParseError::new(
ParseErrorKind::MissingClosingParen,
self.current_byte_offset(),
)
.with_context("closing for loop header")
})?;
self.skip_whitespace();
let body = if self.at(SyntaxKind::LBrace) {
self.parse_block_stmt()
.map_err(|e| e.with_context("parsing for loop body"))?
} else {
self.parse_stmt()
.map_err(|e| e.with_context("parsing for loop body"))?
};
let end_byte = self.current_byte_offset();
#[cfg(debug_assertions)]
if debug_parser {
eprintln!(
"[MF_DEBUG] parse_ts_for_stmt: completed, init={}, test={}, update={}",
init.is_some(),
test.is_some(),
update.is_some()
);
}
Ok(IrNode::TsForStmt {
span: IrSpan::new(start_byte, end_byte),
init,
test,
update,
body: Box::new(body),
})
}
fn parse_ts_while_stmt(&mut self) -> ParseResult<IrNode> {
#[cfg(debug_assertions)]
let debug_parser = std::env::var("MF_DEBUG_PARSER").is_ok();
let start_byte = self.current_byte_offset();
self.consume(); self.skip_whitespace();
#[cfg(debug_assertions)]
if debug_parser {
eprintln!("[MF_DEBUG] parse_ts_while_stmt: parsing while loop");
}
self.expect(SyntaxKind::LParen).ok_or_else(|| {
ParseError::new(
ParseErrorKind::MissingClosingParen,
self.current_byte_offset(),
)
.with_context("expected '(' after 'while'")
})?;
self.skip_whitespace();
let test = self
.parse_ts_expr_until(&[SyntaxKind::RParen])
.map_err(|e| e.with_context("parsing while loop condition"))?;
self.skip_whitespace();
self.expect(SyntaxKind::RParen).ok_or_else(|| {
ParseError::new(
ParseErrorKind::MissingClosingParen,
self.current_byte_offset(),
)
.with_context("closing while loop condition")
})?;
self.skip_whitespace();
let body = if self.at(SyntaxKind::LBrace) {
self.parse_block_stmt()
.map_err(|e| e.with_context("parsing while loop body"))?
} else {
self.parse_stmt()
.map_err(|e| e.with_context("parsing while loop body"))?
};
let end_byte = self.current_byte_offset();
#[cfg(debug_assertions)]
if debug_parser {
eprintln!("[MF_DEBUG] parse_ts_while_stmt: completed");
}
Ok(IrNode::TsWhileStmt {
span: IrSpan::new(start_byte, end_byte),
test: Box::new(test),
body: Box::new(body),
})
}
fn parse_ts_do_while_stmt(&mut self) -> ParseResult<IrNode> {
#[cfg(debug_assertions)]
let debug_parser = std::env::var("MF_DEBUG_PARSER").is_ok();
let start_byte = self.current_byte_offset();
self.consume(); self.skip_whitespace();
#[cfg(debug_assertions)]
if debug_parser {
eprintln!("[MF_DEBUG] parse_ts_do_while_stmt: parsing do-while loop");
}
let body = if self.at(SyntaxKind::LBrace) {
self.parse_block_stmt()
.map_err(|e| e.with_context("parsing do-while loop body"))?
} else {
self.parse_stmt()
.map_err(|e| e.with_context("parsing do-while loop body"))?
};
self.skip_whitespace();
if !self.at(SyntaxKind::WhileKw) {
return Err(ParseError::new(
ParseErrorKind::UnexpectedToken,
self.current_byte_offset(),
)
.with_context("expected 'while' after do-while body"));
}
self.consume(); self.skip_whitespace();
self.expect(SyntaxKind::LParen).ok_or_else(|| {
ParseError::new(
ParseErrorKind::MissingClosingParen,
self.current_byte_offset(),
)
.with_context("expected '(' after 'while' in do-while")
})?;
self.skip_whitespace();
let test = self
.parse_ts_expr_until(&[SyntaxKind::RParen])
.map_err(|e| e.with_context("parsing do-while loop condition"))?;
self.skip_whitespace();
self.expect(SyntaxKind::RParen).ok_or_else(|| {
ParseError::new(
ParseErrorKind::MissingClosingParen,
self.current_byte_offset(),
)
.with_context("closing do-while loop condition")
})?;
self.skip_whitespace();
if self.at(SyntaxKind::Semicolon) {
self.consume();
}
let end_byte = self.current_byte_offset();
#[cfg(debug_assertions)]
if debug_parser {
eprintln!("[MF_DEBUG] parse_ts_do_while_stmt: completed");
}
Ok(IrNode::TsDoWhileStmt {
span: IrSpan::new(start_byte, end_byte),
body: Box::new(body),
test: Box::new(test),
})
}
fn parse_for_init_var_decl(&mut self) -> ParseResult<IrNode> {
let start_byte = self.current_byte_offset();
let kind = match self.current_kind() {
Some(SyntaxKind::ConstKw) => VarKind::Const,
Some(SyntaxKind::LetKw) => VarKind::Let,
Some(SyntaxKind::VarKw) => VarKind::Var,
_ => {
return Err(ParseError::new(
ParseErrorKind::UnexpectedToken,
self.current_byte_offset(),
)
.with_context("expected 'const', 'let', or 'var'"));
}
};
self.consume(); self.skip_whitespace();
let mut decls = Vec::new();
loop {
let decl_start = self.current_byte_offset();
let name = self
.parse_for_loop_binding()
.map_err(|e| e.with_context("parsing for loop variable binding"))?;
self.skip_whitespace();
let type_ann = if self.at(SyntaxKind::Colon) {
self.consume();
self.skip_whitespace();
self.parse_type_until(&[SyntaxKind::Eq, SyntaxKind::Comma, SyntaxKind::Semicolon])?
.map(Box::new)
} else {
None
};
self.skip_whitespace();
let init = if self.at(SyntaxKind::Eq) {
self.consume();
self.skip_whitespace();
Some(Box::new(self.parse_ts_expr_until(&[
SyntaxKind::Semicolon,
SyntaxKind::Comma,
])?))
} else {
None
};
let decl_end = self.current_byte_offset();
decls.push(VarDeclarator {
span: IrSpan::new(decl_start, decl_end),
name: Box::new(name),
type_ann,
init,
definite: false,
});
self.skip_whitespace();
if self.at(SyntaxKind::Comma) {
self.consume();
self.skip_whitespace();
} else {
break;
}
}
let end_byte = self.current_byte_offset();
Ok(IrNode::VarDecl {
span: IrSpan::new(start_byte, end_byte),
exported: false,
declare: false,
kind,
decls,
})
}
fn try_parse_for_in_of(&mut self) -> ParseResult<Option<IrNode>> {
#[cfg(debug_assertions)]
let debug_parser = std::env::var("MF_DEBUG_PARSER").is_ok();
let start_pos = self.pos;
let start_byte = self.current_byte_offset();
self.consume(); self.skip_whitespace();
let has_await = if self.at(SyntaxKind::AwaitKw) {
self.consume();
self.skip_whitespace();
true
} else {
false
};
if !self.at(SyntaxKind::LParen) {
self.pos = start_pos;
return Ok(None);
}
self.consume(); self.skip_whitespace();
let left = match self.parse_for_loop_left() {
Ok(node) => node,
Err(_) => {
self.pos = start_pos;
return Ok(None);
}
};
self.skip_whitespace();
let is_for_in = self.at(SyntaxKind::InKw);
let is_for_of = self.at(SyntaxKind::OfKw);
if !is_for_in && !is_for_of {
self.pos = start_pos;
return Ok(None);
}
self.consume(); self.skip_whitespace();
#[cfg(debug_assertions)]
if debug_parser {
eprintln!(
"[MF_DEBUG] parse_for_in_of: is_for_in={}, is_for_of={}, has_await={}",
is_for_in, is_for_of, has_await
);
}
let right = self
.parse_ts_expr_until(&[SyntaxKind::RParen])
.map_err(|e| e.with_context("parsing for loop iterable"))?;
self.skip_whitespace();
self.expect(SyntaxKind::RParen);
self.skip_whitespace();
let body = if self.at(SyntaxKind::LBrace) {
self.parse_block_stmt()
.map_err(|e| e.with_context("parsing for loop body"))?
} else {
self.parse_stmt()
.map_err(|e| e.with_context("parsing for loop body"))?
};
let end_byte = self.current_byte_offset();
if is_for_in {
Ok(Some(IrNode::ForInStmt {
span: IrSpan::new(start_byte, end_byte),
left: Box::new(left),
right: Box::new(right),
body: Box::new(body),
}))
} else {
Ok(Some(IrNode::ForOfStmt {
span: IrSpan::new(start_byte, end_byte),
await_: has_await,
left: Box::new(left),
right: Box::new(right),
body: Box::new(body),
}))
}
}
fn parse_for_loop_left(&mut self) -> ParseResult<IrNode> {
let start_byte = self.current_byte_offset();
let kind = self.current_kind().ok_or_else(|| {
ParseError::unexpected_eof(self.current_byte_offset(), "for loop left-hand side")
})?;
match kind {
SyntaxKind::ConstKw | SyntaxKind::LetKw | SyntaxKind::VarKw => {
let var_kind = match kind {
SyntaxKind::ConstKw => VarKind::Const,
SyntaxKind::LetKw => VarKind::Let,
SyntaxKind::VarKw => VarKind::Var,
_ => unreachable!(),
};
self.consume(); self.skip_whitespace();
let name = self
.parse_for_loop_binding()
.map_err(|e| e.with_context("parsing for loop variable binding"))?;
let end_byte = self.current_byte_offset();
Ok(IrNode::VarDecl {
span: IrSpan::new(start_byte, end_byte),
exported: false,
declare: false,
kind: var_kind,
decls: vec![VarDeclarator {
span: IrSpan::new(start_byte, end_byte),
name: Box::new(name),
type_ann: None,
init: None,
definite: false,
}],
})
}
SyntaxKind::At => {
self.parse_interpolation()
}
SyntaxKind::LBracket => {
self.parse_for_loop_array_pattern()
}
SyntaxKind::LBrace => {
self.parse_for_loop_object_pattern()
}
_ => {
self.parse_ts_expr_until(&[SyntaxKind::InKw, SyntaxKind::OfKw])
.map_err(|e| e.with_context("parsing for loop left-hand side expression"))
}
}
}
fn parse_for_loop_binding(&mut self) -> ParseResult<IrNode> {
let kind = self.current_kind().ok_or_else(|| {
ParseError::unexpected_eof(self.current_byte_offset(), "for loop binding")
})?;
match kind {
SyntaxKind::LBracket => self
.parse_for_loop_array_pattern()
.map_err(|e| e.with_context("parsing for loop array pattern")),
SyntaxKind::LBrace => self
.parse_for_loop_object_pattern()
.map_err(|e| e.with_context("parsing for loop object pattern")),
SyntaxKind::At => self.parse_interpolation(),
_ => self.parse_ts_ident_or_placeholder().ok_or_else(|| {
ParseError::new(
ParseErrorKind::ExpectedIdentifier,
self.current_byte_offset(),
)
.with_context("parsing for loop binding identifier")
}),
}
}
fn parse_for_loop_array_pattern(&mut self) -> ParseResult<IrNode> {
let start_byte = self.current_byte_offset();
self.consume(); self.skip_whitespace();
let mut elems: Vec<IrNode> = Vec::new();
while !self.at_eof() && !self.at(SyntaxKind::RBracket) {
self.skip_whitespace();
if self.at(SyntaxKind::Comma) {
self.consume();
continue;
}
if self.at(SyntaxKind::RBracket) {
break;
}
if self.at(SyntaxKind::DotDotDot) {
let rest_start = self.current_byte_offset();
self.consume();
self.skip_whitespace();
let arg = self
.parse_for_loop_binding()
.map_err(|e| e.with_context("parsing rest element in array pattern"))?;
let rest_end = self.current_byte_offset();
elems.push(IrNode::RestPat {
span: IrSpan::new(rest_start, rest_end),
arg: Box::new(arg),
type_ann: None,
});
} else {
let elem = self
.parse_for_loop_binding()
.map_err(|e| e.with_context("parsing element in array pattern"))?;
elems.push(elem);
}
self.skip_whitespace();
if self.at(SyntaxKind::Comma) {
self.consume();
}
}
self.expect(SyntaxKind::RBracket).ok_or_else(|| {
ParseError::new(
ParseErrorKind::MissingClosingBracket,
self.current_byte_offset(),
)
.with_context("parsing array pattern")
})?;
let end_byte = self.current_byte_offset();
Ok(IrNode::ArrayPat {
span: IrSpan::new(start_byte, end_byte),
elems: elems.into_iter().map(Some).collect(),
type_ann: None,
optional: false,
})
}
fn parse_for_loop_object_pattern(&mut self) -> ParseResult<IrNode> {
let start_byte = self.current_byte_offset();
self.consume(); self.skip_whitespace();
let mut props: Vec<IrNode> = Vec::new();
while !self.at_eof() && !self.at(SyntaxKind::RBrace) {
self.skip_whitespace();
if self.at(SyntaxKind::RBrace) {
break;
}
if self.at(SyntaxKind::DotDotDot) {
let rest_start = self.current_byte_offset();
self.consume();
self.skip_whitespace();
let arg = self
.parse_for_loop_binding()
.map_err(|e| e.with_context("parsing rest element in object pattern"))?;
let rest_end = self.current_byte_offset();
props.push(IrNode::RestPat {
span: IrSpan::new(rest_start, rest_end),
arg: Box::new(arg),
type_ann: None,
});
} else {
let prop_start = self.current_byte_offset();
let key = self.parse_ts_ident_or_placeholder().ok_or_else(|| {
ParseError::new(
ParseErrorKind::ExpectedIdentifier,
self.current_byte_offset(),
)
.with_context("parsing object pattern property key")
})?;
self.skip_whitespace();
if self.at(SyntaxKind::Colon) {
self.consume();
self.skip_whitespace();
let value = self
.parse_for_loop_binding()
.map_err(|e| e.with_context("parsing object pattern property value"))?;
let prop_end = self.current_byte_offset();
props.push(IrNode::ObjectPatProp {
span: IrSpan::new(prop_start, prop_end),
key: Box::new(key),
value: Some(Box::new(value)),
});
} else {
let prop_end = self.current_byte_offset();
props.push(IrNode::ObjectPatProp {
span: IrSpan::new(prop_start, prop_end),
key: Box::new(key),
value: None,
});
}
}
self.skip_whitespace();
if self.at(SyntaxKind::Comma) {
self.consume();
}
}
self.expect(SyntaxKind::RBrace).ok_or_else(|| {
ParseError::new(
ParseErrorKind::MissingClosingBrace,
self.current_byte_offset(),
)
.with_context("parsing object pattern")
})?;
let end_byte = self.current_byte_offset();
Ok(IrNode::ObjectPat {
span: IrSpan::new(start_byte, end_byte),
props,
type_ann: None,
optional: false,
})
}
}