use crate::{
JSError,
core::{
DestructuringElement, Expr, ObjectDestructuringElement, Token, TokenData, parse_array_destructuring_pattern, parse_assignment,
parse_class_body, parse_expression, parse_object_destructuring_pattern,
},
js_class::ClassMember,
raise_parse_error, raise_parse_error_with_token,
};
fn raise_parse_error_at(tokens: &[TokenData]) -> JSError {
if let Some(t) = tokens.first() {
raise_parse_error_with_token!(t)
} else {
raise_parse_error!()
}
}
#[derive(Clone, Debug)]
pub enum SwitchCase {
Case(Expr, Vec<Statement>), Default(Vec<Statement>), }
#[derive(Clone, Debug)]
pub enum ImportSpecifier {
Default(String), Named(String, Option<String>), Namespace(String), }
#[derive(Clone, Debug)]
pub enum ExportSpecifier {
Named(String, Option<String>), Default(Expr), }
#[derive(Clone, Debug)]
pub struct Statement {
pub kind: StatementKind,
pub line: usize,
pub column: usize,
}
impl From<StatementKind> for Statement {
fn from(kind: StatementKind) -> Self {
Statement { kind, line: 0, column: 0 }
}
}
#[derive(Clone)]
pub enum StatementKind {
Let(Vec<(String, Option<Expr>)>),
Var(Vec<(String, Option<Expr>)>),
Const(Vec<(String, Expr)>),
FunctionDeclaration(String, Vec<DestructuringElement>, Vec<Statement>, bool), LetDestructuringArray(Vec<DestructuringElement>, Expr), VarDestructuringArray(Vec<DestructuringElement>, Expr), ConstDestructuringArray(Vec<DestructuringElement>, Expr), LetDestructuringObject(Vec<ObjectDestructuringElement>, Expr), VarDestructuringObject(Vec<ObjectDestructuringElement>, Expr), ConstDestructuringObject(Vec<ObjectDestructuringElement>, Expr), Class(String, Option<crate::core::Expr>, Vec<ClassMember>), Assign(String, Expr), Expr(Expr),
Return(Option<Expr>),
If(Expr, Vec<Statement>, Option<Vec<Statement>>), For(Option<Box<Statement>>, Option<Expr>, Option<Box<Statement>>, Vec<Statement>), ForOf(String, Expr, Vec<Statement>), ForIn(String, Expr, Vec<Statement>), ForOfDestructuringObject(Vec<ObjectDestructuringElement>, Expr, Vec<Statement>), ForOfDestructuringArray(Vec<DestructuringElement>, Expr, Vec<Statement>), While(Expr, Vec<Statement>), DoWhile(Vec<Statement>, Expr), Switch(Expr, Vec<SwitchCase>), Block(Vec<Statement>), Break(Option<String>),
Continue(Option<String>),
Label(String, Box<Statement>),
TryCatch(Vec<Statement>, String, Vec<Statement>, Option<Vec<Statement>>), Throw(Expr), Import(Vec<ImportSpecifier>, String), Export(Vec<ExportSpecifier>, Option<Box<Statement>>), }
impl std::fmt::Debug for StatementKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
StatementKind::Let(decls) => write!(f, "Let({:?})", decls),
StatementKind::Var(decls) => write!(f, "Var({:?})", decls),
StatementKind::Const(decls) => write!(f, "Const({:?})", decls),
StatementKind::FunctionDeclaration(name, params, body, is_gen) => {
write!(f, "FunctionDeclaration({}, {:?}, {:?}, {})", name, params, body, is_gen)
}
StatementKind::LetDestructuringArray(pattern, expr) => write!(f, "LetDestructuringArray({:?}, {:?})", pattern, expr),
StatementKind::VarDestructuringArray(pattern, expr) => write!(f, "VarDestructuringArray({:?}, {:?})", pattern, expr),
StatementKind::ConstDestructuringArray(pattern, expr) => write!(f, "ConstDestructuringArray({:?}, {:?})", pattern, expr),
StatementKind::LetDestructuringObject(pattern, expr) => write!(f, "LetDestructuringObject({:?}, {:?})", pattern, expr),
StatementKind::VarDestructuringObject(pattern, expr) => write!(f, "VarDestructuringObject({:?}, {:?})", pattern, expr),
StatementKind::ConstDestructuringObject(pattern, expr) => write!(f, "ConstDestructuringObject({:?}, {:?})", pattern, expr),
StatementKind::Class(name, extends, members) => write!(f, "Class({name}, {extends:?}, {members:?})"),
StatementKind::Assign(var, expr) => write!(f, "Assign({}, {:?})", var, expr),
StatementKind::Expr(expr) => write!(f, "Expr({:?})", expr),
StatementKind::Return(Some(expr)) => write!(f, "Return({:?})", expr),
StatementKind::Return(None) => write!(f, "Return(None)"),
StatementKind::If(cond, then_body, else_body) => {
write!(f, "If({:?}, {:?}, {:?})", cond, then_body, else_body)
}
StatementKind::For(init, cond, incr, body) => {
write!(f, "For({:?}, {:?}, {:?}, {:?})", init, cond, incr, body)
}
StatementKind::ForOf(var, iterable, body) => {
write!(f, "ForOf({}, {:?}, {:?})", var, iterable, body)
}
StatementKind::ForIn(var, object, body) => {
write!(f, "ForIn({}, {:?}, {:?})", var, object, body)
}
StatementKind::ForOfDestructuringObject(pat, iterable, body) => {
write!(f, "ForOfDestructuringObject({:?}, {:?}, {:?})", pat, iterable, body)
}
StatementKind::ForOfDestructuringArray(pat, iterable, body) => {
write!(f, "ForOfDestructuringArray({:?}, {:?}, {:?})", pat, iterable, body)
}
StatementKind::While(cond, body) => {
write!(f, "While({:?}, {:?})", cond, body)
}
StatementKind::DoWhile(body, cond) => {
write!(f, "DoWhile({:?}, {:?})", body, cond)
}
StatementKind::Switch(expr, cases) => {
write!(f, "Switch({:?}, {:?})", expr, cases)
}
StatementKind::Break(None) => write!(f, "Break"),
StatementKind::Break(Some(lbl)) => write!(f, "Break({})", lbl),
StatementKind::Continue(None) => write!(f, "Continue"),
StatementKind::Continue(Some(lbl)) => write!(f, "Continue({})", lbl),
StatementKind::Label(name, stmt) => write!(f, "Label({}, {:?})", name, stmt),
StatementKind::Block(stmts) => write!(f, "Block({:?})", stmts),
StatementKind::TryCatch(try_body, catch_param, catch_body, finally_body) => {
write!(f, "TryCatch({:?}, {}, {:?}, {:?})", try_body, catch_param, catch_body, finally_body)
}
StatementKind::Throw(expr) => {
write!(f, "Throw({:?})", expr)
}
StatementKind::Import(specifiers, module) => {
write!(f, "Import({:?}, {})", specifiers, module)
}
StatementKind::Export(specifiers, maybe_decl) => {
if let Some(decl) = maybe_decl {
write!(f, "Export({:?}, {:?})", specifiers, decl)
} else {
write!(f, "Export({:?})", specifiers)
}
}
}
}
}
pub fn parse_statements(tokens: &mut Vec<TokenData>) -> Result<Vec<Statement>, JSError> {
let mut statements = Vec::new();
while !tokens.is_empty() && !matches!(tokens[0].token, Token::RBrace) {
if matches!(tokens[0].token, Token::Semicolon | Token::LineTerminator) {
tokens.remove(0);
continue;
}
log::trace!("parse_statements next token: {:?}", tokens.first());
let tokens_snapshot = tokens.clone();
let stmt = match parse_statement(tokens) {
Ok(s) => s,
Err(e) => {
log::warn!("parse_statements error at token start. remaining tokens (first 40):");
for (i, t) in tokens_snapshot.iter().take(40).enumerate() {
log::warn!(" {}: {:?}", i, t);
}
return Err(e);
}
};
statements.push(stmt);
if !tokens.is_empty() && matches!(tokens[0].token, Token::Semicolon | Token::LineTerminator) {
tokens.remove(0);
}
}
Ok(statements)
}
pub fn parse_statement(tokens: &mut Vec<TokenData>) -> Result<Statement, JSError> {
while !tokens.is_empty() && matches!(tokens[0].token, Token::LineTerminator) {
tokens.remove(0);
}
let (line, column) = if let Some(t) = tokens.first() { (t.line, t.column) } else { (0, 0) };
let kind = parse_statement_kind(tokens)?;
Ok(Statement { kind, line, column })
}
pub fn parse_statement_kind(tokens: &mut Vec<TokenData>) -> Result<StatementKind, JSError> {
while !tokens.is_empty() && matches!(tokens[0].token, Token::LineTerminator) {
tokens.remove(0);
}
if !tokens.is_empty() && matches!(tokens[0].token, Token::Import) {
if tokens.len() > 1 && matches!(tokens[1].token, Token::LParen) {
} else {
tokens.remove(0); let mut specifiers = Vec::new();
if !tokens.is_empty() && matches!(tokens[0].token, Token::Multiply) {
tokens.remove(0); if tokens.is_empty() || !matches!(tokens[0].token, Token::As) {
return Err(raise_parse_error_at(tokens));
}
tokens.remove(0); if let Some(Token::Identifier(name)) = tokens.first().map(|t| t.token.clone()) {
if let Token::Identifier(_) = tokens[0].token {
tokens.remove(0);
}
specifiers.push(ImportSpecifier::Namespace(name));
} else {
return Err(raise_parse_error_at(tokens));
}
} else if !tokens.is_empty() && matches!(tokens[0].token, Token::LBrace) {
tokens.remove(0); while !tokens.is_empty() && !matches!(tokens[0].token, Token::RBrace) {
if let Some(Token::Identifier(name)) = tokens.first().map(|t| t.token.clone()) {
tokens.remove(0);
let alias = if !tokens.is_empty() && matches!(tokens[0].token, Token::As) {
tokens.remove(0); if let Some(Token::Identifier(alias_name)) = tokens.first().map(|t| t.token.clone()) {
tokens.remove(0);
Some(alias_name)
} else {
return Err(raise_parse_error_at(tokens));
}
} else {
None
};
specifiers.push(ImportSpecifier::Named(name, alias));
if !tokens.is_empty() && matches!(tokens[0].token, Token::Comma) {
tokens.remove(0); } else if !matches!(tokens[0].token, Token::RBrace) {
return Err(raise_parse_error_at(tokens));
}
} else {
return Err(raise_parse_error_at(tokens));
}
}
if tokens.is_empty() || !matches!(tokens[0].token, Token::RBrace) {
return Err(raise_parse_error_at(tokens));
}
tokens.remove(0); } else if let Some(Token::Identifier(name)) = tokens.first().map(|t| t.token.clone()) {
if let Token::Identifier(_) = tokens[0].token {
tokens.remove(0);
}
specifiers.push(ImportSpecifier::Default(name));
if !tokens.is_empty() && matches!(tokens[0].token, Token::Comma) {
tokens.remove(0); if tokens.is_empty() || !matches!(tokens[0].token, Token::LBrace) {
return Err(raise_parse_error_at(tokens));
}
tokens.remove(0); while !tokens.is_empty() && !matches!(tokens[0].token, Token::RBrace) {
if let Some(Token::Identifier(name)) = tokens.first().map(|t| t.token.clone()) {
tokens.remove(0);
let alias = if !tokens.is_empty() && matches!(tokens[0].token, Token::As) {
tokens.remove(0); if let Some(Token::Identifier(alias_name)) = tokens.first().map(|t| t.token.clone()) {
tokens.remove(0);
Some(alias_name)
} else {
return Err(raise_parse_error_at(tokens));
}
} else {
None
};
specifiers.push(ImportSpecifier::Named(name, alias));
if !tokens.is_empty() && matches!(tokens[0].token, Token::Comma) {
tokens.remove(0); } else if !matches!(tokens[0].token, Token::RBrace) {
return Err(raise_parse_error_at(tokens));
}
} else {
return Err(raise_parse_error_at(tokens));
}
}
if tokens.is_empty() || !matches!(tokens[0].token, Token::RBrace) {
return Err(raise_parse_error_at(tokens));
}
tokens.remove(0); }
}
if tokens.is_empty() || !matches!(tokens[0].token, Token::Identifier(_)) {
return Err(raise_parse_error_at(tokens));
}
if let Token::Identifier(from_keyword) = tokens.remove(0).token {
if from_keyword != "from" {
return Err(raise_parse_error_at(tokens));
}
} else {
return Err(raise_parse_error_at(tokens));
}
let module_name = if let Some(Token::StringLit(utf16_chars)) = tokens.first().map(|t| t.token.clone()) {
tokens.remove(0);
String::from_utf16(&utf16_chars).map_err(|_| raise_parse_error_at(tokens))?
} else {
return Err(raise_parse_error_at(tokens));
};
return Ok(StatementKind::Import(specifiers, module_name));
}
} if !tokens.is_empty() && matches!(tokens[0].token, Token::Export) {
tokens.remove(0); let mut specifiers = Vec::new();
if !tokens.is_empty() && matches!(tokens[0].token, Token::Default) {
tokens.remove(0); if !tokens.is_empty() && (matches!(tokens[0].token, Token::Function) || matches!(tokens[0].token, Token::FunctionStar)) {
let is_generator = matches!(tokens[0].token, Token::FunctionStar);
tokens.remove(0); let _name = if let Some(Token::Identifier(name)) = tokens.first().map(|t| t.token.clone()) {
tokens.remove(0);
Some(name)
} else {
None
};
if tokens.is_empty() || !matches!(tokens[0].token, Token::LParen) {
return Err(raise_parse_error_at(tokens));
}
tokens.remove(0); let params = crate::core::parser::parse_parameters(tokens)?;
if tokens.is_empty() || !matches!(tokens[0].token, Token::LBrace) {
return Err(raise_parse_error_at(tokens));
}
tokens.remove(0); let body = parse_statements(tokens)?;
if tokens.is_empty() || !matches!(tokens[0].token, Token::RBrace) {
return Err(raise_parse_error_at(tokens));
}
tokens.remove(0); let func_expr = if is_generator {
Expr::GeneratorFunction(None, params, body)
} else {
Expr::Function(None, params, body)
};
specifiers.push(ExportSpecifier::Default(func_expr));
} else {
let expr = parse_expression(tokens)?;
specifiers.push(ExportSpecifier::Default(expr));
}
} else if !tokens.is_empty() && matches!(tokens[0].token, Token::LBrace) {
tokens.remove(0); while !tokens.is_empty() && !matches!(tokens[0].token, Token::RBrace) {
if let Some(Token::Identifier(name)) = tokens.first().map(|t| t.token.clone()) {
tokens.remove(0);
let alias = if !tokens.is_empty() && matches!(tokens[0].token, Token::As) {
tokens.remove(0); if let Some(Token::Identifier(alias_name)) = tokens.first().map(|t| t.token.clone()) {
tokens.remove(0);
Some(alias_name)
} else {
return Err(raise_parse_error_at(tokens));
}
} else {
None
};
specifiers.push(ExportSpecifier::Named(name, alias));
if !tokens.is_empty() && matches!(tokens[0].token, Token::Comma) {
tokens.remove(0); } else if !matches!(tokens[0].token, Token::RBrace) {
return Err(raise_parse_error_at(tokens));
}
} else {
return Err(raise_parse_error_at(tokens));
}
}
if tokens.is_empty() || !matches!(tokens[0].token, Token::RBrace) {
return Err(raise_parse_error_at(tokens));
}
tokens.remove(0); } else {
let stmt = parse_statement(tokens)?;
match stmt.clone().kind {
StatementKind::Const(decls) => {
for (name, _) in decls {
specifiers.push(ExportSpecifier::Named(name, None));
}
return Ok(StatementKind::Export(specifiers, Some(Box::new(stmt))));
}
StatementKind::Let(decls) => {
for (name, _) in decls {
specifiers.push(ExportSpecifier::Named(name, None));
}
return Ok(StatementKind::Export(specifiers, Some(Box::new(stmt))));
}
StatementKind::Var(decls) => {
for (name, _) in decls {
specifiers.push(ExportSpecifier::Named(name, None));
}
return Ok(StatementKind::Export(specifiers, Some(Box::new(stmt))));
}
StatementKind::Class(name, _, _) => {
specifiers.push(ExportSpecifier::Named(name, None));
return Ok(StatementKind::Export(specifiers, Some(Box::new(stmt))));
}
StatementKind::FunctionDeclaration(name, _, _, _) => {
specifiers.push(ExportSpecifier::Named(name, None));
return Ok(StatementKind::Export(specifiers, Some(Box::new(stmt))));
}
_ => return Err(raise_parse_error_at(tokens)),
}
}
return Ok(StatementKind::Export(specifiers, None));
}
if tokens.len() > 1 {
if let Token::Identifier(name) = tokens[0].token.clone() {
if matches!(tokens[1].token, Token::Colon) {
tokens.remove(0);
tokens.remove(0);
let stmt = parse_statement(tokens)?;
return Ok(StatementKind::Label(name, Box::new(stmt)));
}
}
}
if !tokens.is_empty() && matches!(tokens[0].token, Token::LBrace) {
tokens.remove(0); let body = parse_statements(tokens)?;
if tokens.is_empty() || !matches!(tokens[0].token, Token::RBrace) {
return Err(raise_parse_error_at(tokens));
}
tokens.remove(0); return Ok(StatementKind::Block(body));
}
if !tokens.is_empty() && matches!(tokens[0].token, Token::Break) {
tokens.remove(0); let label = if !tokens.is_empty() {
if let Some(Token::Identifier(name)) = tokens.first().map(|t| t.token.clone()) {
tokens.remove(0);
Some(name)
} else {
None
}
} else {
None
};
if !tokens.is_empty() && matches!(tokens[0].token, Token::Semicolon | Token::LineTerminator) {
tokens.remove(0); } else if !tokens.is_empty()
&& (matches!(tokens[0].token, Token::RBrace)
|| matches!(tokens[0].token, Token::Catch)
|| matches!(tokens[0].token, Token::Finally))
{
} else {
return Err(raise_parse_error_at(tokens));
}
return Ok(StatementKind::Break(label));
}
if !tokens.is_empty() && matches!(tokens[0].token, Token::Continue) {
tokens.remove(0); let label = if !tokens.is_empty() {
if let Some(Token::Identifier(name)) = tokens.first().map(|t| t.token.clone()) {
tokens.remove(0);
Some(name)
} else {
None
}
} else {
None
};
if tokens.is_empty() || !matches!(tokens[0].token, Token::Semicolon | Token::LineTerminator) {
return Err(raise_parse_error_at(tokens));
}
tokens.remove(0); return Ok(StatementKind::Continue(label));
}
if !tokens.is_empty() && matches!(tokens[0].token, Token::While) {
tokens.remove(0); if tokens.is_empty() || !matches!(tokens[0].token, Token::LParen) {
return Err(raise_parse_error_at(tokens));
}
tokens.remove(0); let condition = parse_expression(tokens)?;
if tokens.is_empty() || !matches!(tokens[0].token, Token::RParen) {
return Err(raise_parse_error_at(tokens));
}
tokens.remove(0); let body = if !tokens.is_empty() && matches!(tokens[0].token, Token::LBrace) {
tokens.remove(0); let b = parse_statements(tokens)?;
if tokens.is_empty() || !matches!(tokens[0].token, Token::RBrace) {
return Err(raise_parse_error_at(tokens));
}
tokens.remove(0); b
} else {
let s = parse_statement(tokens)?;
if !tokens.is_empty() && matches!(tokens[0].token, Token::Semicolon | Token::LineTerminator) {
tokens.remove(0);
}
vec![s]
};
return Ok(StatementKind::While(condition, body));
}
if !tokens.is_empty() && matches!(tokens[0].token, Token::Do) {
tokens.remove(0); let body = if !tokens.is_empty() && matches!(tokens[0].token, Token::LBrace) {
tokens.remove(0); let b = parse_statements(tokens)?;
if tokens.is_empty() || !matches!(tokens[0].token, Token::RBrace) {
return Err(raise_parse_error_at(tokens));
}
tokens.remove(0); b
} else {
let s = parse_statement(tokens)?;
if !tokens.is_empty() && matches!(tokens[0].token, Token::Semicolon | Token::LineTerminator) {
tokens.remove(0);
}
vec![s]
};
while !tokens.is_empty() && matches!(tokens[0].token, Token::LineTerminator) {
tokens.remove(0);
}
if tokens.is_empty() || !matches!(tokens[0].token, Token::While) {
return Err(raise_parse_error_at(tokens));
}
tokens.remove(0); if tokens.is_empty() || !matches!(tokens[0].token, Token::LParen) {
return Err(raise_parse_error_at(tokens));
}
tokens.remove(0); let condition = parse_expression(tokens)?;
if tokens.is_empty() || !matches!(tokens[0].token, Token::RParen) {
return Err(raise_parse_error_at(tokens));
}
tokens.remove(0); if tokens.is_empty() || !matches!(tokens[0].token, Token::Semicolon | Token::LineTerminator) {
return Err(raise_parse_error_at(tokens));
}
tokens.remove(0); return Ok(StatementKind::DoWhile(body, condition));
}
if !tokens.is_empty() && matches!(tokens[0].token, Token::Switch) {
tokens.remove(0); if tokens.is_empty() || !matches!(tokens[0].token, Token::LParen) {
return Err(raise_parse_error_at(tokens));
}
tokens.remove(0); let expr = parse_expression(tokens)?;
if tokens.is_empty() || !matches!(tokens[0].token, Token::RParen) {
return Err(raise_parse_error_at(tokens));
}
tokens.remove(0); if tokens.is_empty() || !matches!(tokens[0].token, Token::LBrace) {
return Err(raise_parse_error_at(tokens));
}
tokens.remove(0); let mut cases = Vec::new();
while !tokens.is_empty() && !matches!(tokens[0].token, Token::RBrace) {
while !tokens.is_empty() && matches!(tokens[0].token, Token::LineTerminator) {
tokens.remove(0);
}
if tokens.is_empty() || matches!(tokens[0].token, Token::RBrace) {
break;
}
if matches!(tokens[0].token, Token::Case) {
tokens.remove(0); let case_value = parse_expression(tokens)?;
if tokens.is_empty() || !matches!(tokens[0].token, Token::Colon) {
return Err(raise_parse_error_at(tokens));
}
tokens.remove(0); let mut case_stmts = Vec::new();
while !tokens.is_empty()
&& !matches!(tokens[0].token, Token::Case)
&& !matches!(tokens[0].token, Token::Default)
&& !matches!(tokens[0].token, Token::RBrace)
{
while !tokens.is_empty() && matches!(tokens[0].token, Token::LineTerminator) {
tokens.remove(0);
}
if tokens.is_empty()
|| matches!(tokens[0].token, Token::Case)
|| matches!(tokens[0].token, Token::Default)
|| matches!(tokens[0].token, Token::RBrace)
{
break;
}
log::trace!("switch case parsing stmt, next token = {:?}", tokens.first());
let stmt = parse_statement(tokens)?;
case_stmts.push(stmt);
if !tokens.is_empty() && matches!(tokens[0].token, Token::Semicolon | Token::LineTerminator) {
tokens.remove(0);
}
}
cases.push(SwitchCase::Case(case_value, case_stmts));
} else if matches!(tokens[0].token, Token::Default) {
tokens.remove(0); if tokens.is_empty() || !matches!(tokens[0].token, Token::Colon) {
return Err(raise_parse_error_at(tokens));
}
tokens.remove(0); let mut default_stmts = Vec::new();
while !tokens.is_empty() && !matches!(tokens[0].token, Token::RBrace) {
while !tokens.is_empty() && matches!(tokens[0].token, Token::LineTerminator) {
tokens.remove(0);
}
if tokens.is_empty() || matches!(tokens[0].token, Token::RBrace) {
break;
}
log::trace!("switch default parsing stmt, next token = {:?}", tokens.first());
let stmt = parse_statement(tokens)?;
default_stmts.push(stmt);
if !tokens.is_empty() && matches!(tokens[0].token, Token::Semicolon | Token::LineTerminator) {
tokens.remove(0);
}
}
cases.push(SwitchCase::Default(default_stmts));
} else {
return Err(raise_parse_error_at(tokens));
}
}
if tokens.is_empty() || !matches!(tokens[0].token, Token::RBrace) {
return Err(raise_parse_error_at(tokens));
}
tokens.remove(0); return Ok(StatementKind::Switch(expr, cases));
}
if !tokens.is_empty() && matches!(tokens[0].token, Token::Throw) {
tokens.remove(0); let expr = parse_expression(tokens)?;
if !tokens.is_empty() && matches!(tokens[0].token, Token::Semicolon | Token::LineTerminator) {
tokens.remove(0); } else if !tokens.is_empty()
&& (matches!(tokens[0].token, Token::RBrace)
|| matches!(tokens[0].token, Token::Catch)
|| matches!(tokens[0].token, Token::Finally))
{
} else {
return Err(raise_parse_error_at(tokens));
}
return Ok(StatementKind::Throw(expr));
}
if !tokens.is_empty() && matches!(tokens[0].token, Token::Async) {
tokens.remove(0); if !tokens.is_empty() && matches!(tokens[0].token, Token::Function) {
tokens.remove(0); if let Some(Token::Identifier(name)) = tokens.first().map(|t| t.token.clone()) {
tokens.remove(0);
if !tokens.is_empty() && matches!(tokens[0].token, Token::LParen) {
tokens.remove(0); let params = crate::core::parser::parse_parameters(tokens)?;
if tokens.is_empty() || !matches!(tokens[0].token, Token::LBrace) {
return Err(raise_parse_error_at(tokens));
}
tokens.remove(0); let body = parse_statements(tokens)?;
if tokens.is_empty() || !matches!(tokens[0].token, Token::RBrace) {
return Err(raise_parse_error_at(tokens));
}
tokens.remove(0); return Ok(StatementKind::Let(vec![(
name.clone(),
Some(Expr::AsyncFunction(Some(name), params, body)),
)]));
}
}
}
return Err(raise_parse_error_at(tokens));
}
if !tokens.is_empty() && (matches!(tokens[0].token, Token::Function) || matches!(tokens[0].token, Token::FunctionStar)) {
let is_generator = matches!(tokens[0].token, Token::FunctionStar);
tokens.remove(0); log::trace!(
"parse_statement: entered Function branch; next tokens: {:?}",
tokens.iter().take(12).collect::<Vec<_>>()
);
if let Some(Token::Identifier(name)) = tokens.first().map(|t| t.token.clone()) {
tokens.remove(0);
log::trace!(
"parse_statement: function name parsed: {} ; remaining: {:?}",
name,
tokens.iter().take(12).collect::<Vec<_>>()
);
if !tokens.is_empty() && matches!(tokens[0].token, Token::LParen) {
tokens.remove(0); let params = crate::core::parser::parse_parameters(tokens)?;
if tokens.is_empty() || !matches!(tokens[0].token, Token::LBrace) {
return Err(raise_parse_error_at(tokens));
}
log::trace!(
"parse_statement: function params parsed; entering body parse; remaining tokens: {:?}",
tokens.iter().take(12).collect::<Vec<_>>()
);
tokens.remove(0); let body = parse_statements(tokens)?;
log::trace!(
"parse_statement: parsed function body, stmt count {} ; remaining tokens: {:?}",
body.len(),
tokens.iter().take(8).collect::<Vec<_>>()
);
if tokens.is_empty() || !matches!(tokens[0].token, Token::RBrace) {
return Err(raise_parse_error_at(tokens));
}
tokens.remove(0); return Ok(StatementKind::FunctionDeclaration(name, params, body, is_generator));
}
}
}
if !tokens.is_empty() && matches!(tokens[0].token, Token::If) {
tokens.remove(0); if tokens.is_empty() || !matches!(tokens[0].token, Token::LParen) {
return Err(raise_parse_error_at(tokens));
}
tokens.remove(0); let condition = parse_expression(tokens)?;
if tokens.is_empty() || !matches!(tokens[0].token, Token::RParen) {
return Err(raise_parse_error_at(tokens));
}
tokens.remove(0); let then_body = if !tokens.is_empty() && matches!(tokens[0].token, Token::LBrace) {
tokens.remove(0); let body = parse_statements(tokens)?;
if tokens.is_empty() || !matches!(tokens[0].token, Token::RBrace) {
return Err(raise_parse_error_at(tokens));
}
tokens.remove(0); body
} else {
let stmt = parse_statement(tokens)?;
if !tokens.is_empty() && matches!(tokens[0].token, Token::Semicolon | Token::LineTerminator) {
tokens.remove(0);
}
vec![stmt]
};
while !tokens.is_empty() && matches!(tokens[0].token, Token::LineTerminator) {
tokens.remove(0);
}
let else_body = if !tokens.is_empty() && matches!(tokens[0].token, Token::Else) {
tokens.remove(0); if !tokens.is_empty() && matches!(tokens[0].token, Token::If) {
let nested_if = parse_statement(tokens)?;
Some(vec![nested_if])
} else if !tokens.is_empty() && matches!(tokens[0].token, Token::LBrace) {
tokens.remove(0); let body = parse_statements(tokens)?;
if tokens.is_empty() || !matches!(tokens[0].token, Token::RBrace) {
return Err(raise_parse_error_at(tokens));
}
tokens.remove(0); Some(body)
} else {
let stmt = parse_statement(tokens)?;
if !tokens.is_empty() && matches!(tokens[0].token, Token::Semicolon | Token::LineTerminator) {
tokens.remove(0);
}
Some(vec![stmt])
}
} else {
None
};
return Ok(StatementKind::If(condition, then_body, else_body));
}
if !tokens.is_empty() && matches!(tokens[0].token, Token::Try) {
tokens.remove(0); if tokens.is_empty() || !matches!(tokens[0].token, Token::LBrace) {
return Err(raise_parse_error_at(tokens));
}
tokens.remove(0); let try_body = parse_statements(tokens)?;
if tokens.is_empty() || !matches!(tokens[0].token, Token::RBrace) {
return Err(raise_parse_error_at(tokens));
}
tokens.remove(0);
let mut catch_param = String::new();
let mut catch_body: Vec<Statement> = Vec::new();
let mut finally_body: Option<Vec<Statement>> = None;
while !tokens.is_empty() && matches!(tokens[0].token, Token::LineTerminator) {
tokens.remove(0);
}
if !tokens.is_empty() && matches!(tokens[0].token, Token::Catch) {
tokens.remove(0); if tokens.is_empty() || !matches!(tokens[0].token, Token::LParen) {
return Err(raise_parse_error_at(tokens));
}
tokens.remove(0); if tokens.is_empty() {
return Err(raise_parse_error_at(tokens));
}
if let Token::Identifier(name) = tokens.remove(0).token {
catch_param = name;
} else {
return Err(raise_parse_error_at(tokens));
}
if tokens.is_empty() || !matches!(tokens[0].token, Token::RParen) {
return Err(raise_parse_error_at(tokens));
}
tokens.remove(0); if tokens.is_empty() || !matches!(tokens[0].token, Token::LBrace) {
return Err(raise_parse_error_at(tokens));
}
tokens.remove(0); catch_body = parse_statements(tokens)?;
if tokens.is_empty() || !matches!(tokens[0].token, Token::RBrace) {
return Err(raise_parse_error_at(tokens));
}
tokens.remove(0); }
while !tokens.is_empty() && matches!(tokens[0].token, Token::LineTerminator) {
tokens.remove(0);
}
if !tokens.is_empty() && matches!(tokens[0].token, Token::Finally) {
tokens.remove(0); if tokens.is_empty() || !matches!(tokens[0].token, Token::LBrace) {
return Err(raise_parse_error_at(tokens));
}
tokens.remove(0); let fb = parse_statements(tokens)?;
if tokens.is_empty() || !matches!(tokens[0].token, Token::RBrace) {
return Err(raise_parse_error_at(tokens));
}
tokens.remove(0); finally_body = Some(fb);
}
return Ok(StatementKind::TryCatch(try_body, catch_param, catch_body, finally_body));
}
if !tokens.is_empty() && matches!(tokens[0].token, Token::For) {
tokens.remove(0); if tokens.is_empty() || !matches!(tokens[0].token, Token::LParen) {
return Err(raise_parse_error_at(tokens));
}
tokens.remove(0);
if !tokens.is_empty()
&& (matches!(tokens[0].token, Token::Let) || matches!(tokens[0].token, Token::Var) || matches!(tokens[0].token, Token::Const))
{
let saved_declaration_token = tokens[0].clone();
tokens.remove(0); if let Some(Token::Identifier(var_name)) = tokens.first().map(|t| t.token.clone()) {
let saved_identifier_token = tokens[0].clone();
tokens.remove(0);
if !tokens.is_empty() && matches!(tokens[0].token, Token::Identifier(ref s) if s == "of") {
tokens.remove(0); let iterable = parse_expression(tokens)?;
if tokens.is_empty() || !matches!(tokens[0].token, Token::RParen) {
return Err(raise_parse_error_at(tokens));
}
tokens.remove(0); let body = if !tokens.is_empty() && matches!(tokens[0].token, Token::LBrace) {
tokens.remove(0); let b = parse_statements(tokens)?;
if tokens.is_empty() || !matches!(tokens[0].token, Token::RBrace) {
return Err(raise_parse_error_at(tokens));
}
tokens.remove(0); b
} else {
let s = parse_statement(tokens)?;
if !tokens.is_empty() && matches!(tokens[0].token, Token::Semicolon | Token::LineTerminator) {
tokens.remove(0);
}
vec![s]
};
return Ok(StatementKind::ForOf(var_name, iterable, body));
} else if !tokens.is_empty() && matches!(tokens[0].token, Token::In) {
tokens.remove(0); let object = parse_expression(tokens)?;
if tokens.is_empty() || !matches!(tokens[0].token, Token::RParen) {
return Err(raise_parse_error_at(tokens));
}
tokens.remove(0); let body = if !tokens.is_empty() && matches!(tokens[0].token, Token::LBrace) {
tokens.remove(0); let b = parse_statements(tokens)?;
if tokens.is_empty() || !matches!(tokens[0].token, Token::RBrace) {
return Err(raise_parse_error_at(tokens));
}
tokens.remove(0); b
} else {
let s = parse_statement(tokens)?;
if !tokens.is_empty() && matches!(tokens[0].token, Token::Semicolon | Token::LineTerminator) {
tokens.remove(0);
}
vec![s]
};
return Ok(StatementKind::ForIn(var_name, object, body));
} else {
tokens.insert(0, saved_identifier_token);
tokens.insert(0, saved_declaration_token);
}
} else if matches!(tokens.first().map(|t| &t.token), Some(Token::LBrace))
&& (matches!(saved_declaration_token.token, Token::Var)
|| matches!(saved_declaration_token.token, Token::Let)
|| matches!(saved_declaration_token.token, Token::Const))
{
let pattern = parse_object_destructuring_pattern(tokens)?;
if !tokens.is_empty() && matches!(tokens[0].token, Token::Identifier(ref s) if s == "of") {
tokens.remove(0); let iterable = parse_expression(tokens)?;
if tokens.is_empty() || !matches!(tokens[0].token, Token::RParen) {
return Err(raise_parse_error_at(tokens));
}
tokens.remove(0); let body = if !tokens.is_empty() && matches!(tokens[0].token, Token::LBrace) {
tokens.remove(0); let b = parse_statements(tokens)?;
if tokens.is_empty() || !matches!(tokens[0].token, Token::RBrace) {
return Err(raise_parse_error_at(tokens));
}
tokens.remove(0); b
} else {
let s = parse_statement(tokens)?;
if !tokens.is_empty() && matches!(tokens[0].token, Token::Semicolon | Token::LineTerminator) {
tokens.remove(0);
}
vec![s]
};
return Ok(StatementKind::ForOfDestructuringObject(pattern, iterable, body));
} else {
tokens.insert(0, saved_declaration_token);
}
} else if matches!(tokens.first().map(|t| &t.token), Some(Token::LBracket))
&& (matches!(saved_declaration_token.token, Token::Var)
|| matches!(saved_declaration_token.token, Token::Let)
|| matches!(saved_declaration_token.token, Token::Const))
{
let pattern = parse_array_destructuring_pattern(tokens)?;
if !tokens.is_empty() && matches!(tokens[0].token, Token::Identifier(ref s) if s == "of") {
tokens.remove(0); let iterable = parse_expression(tokens)?;
if tokens.is_empty() || !matches!(tokens[0].token, Token::RParen) {
return Err(raise_parse_error_at(tokens));
}
tokens.remove(0); let body = if !tokens.is_empty() && matches!(tokens[0].token, Token::LBrace) {
tokens.remove(0); let b = parse_statements(tokens)?;
if tokens.is_empty() || !matches!(tokens[0].token, Token::RBrace) {
return Err(raise_parse_error_at(tokens));
}
tokens.remove(0); b
} else {
let s = parse_statement(tokens)?;
if !tokens.is_empty() && matches!(tokens[0].token, Token::Semicolon | Token::LineTerminator) {
tokens.remove(0);
}
vec![s]
};
return Ok(StatementKind::ForOfDestructuringArray(pattern, iterable, body));
} else {
tokens.insert(0, saved_declaration_token);
}
} else {
tokens.insert(0, saved_declaration_token);
}
}
let init = if !tokens.is_empty() && (matches!(tokens[0].token, Token::Let) || matches!(tokens[0].token, Token::Var)) {
Some(Box::new(parse_statement(tokens)?))
} else if !matches!(tokens[0].token, Token::Semicolon | Token::LineTerminator) {
Some(Box::new(Statement::from(StatementKind::Expr(parse_expression(tokens)?))))
} else {
None
};
if tokens.is_empty() || !matches!(tokens[0].token, Token::Semicolon | Token::LineTerminator) {
return Err(raise_parse_error_at(tokens));
}
tokens.remove(0);
let condition = if !matches!(tokens[0].token, Token::Semicolon | Token::LineTerminator) {
Some(parse_expression(tokens)?)
} else {
None
};
if tokens.is_empty() || !matches!(tokens[0].token, Token::Semicolon | Token::LineTerminator) {
return Err(raise_parse_error_at(tokens));
}
tokens.remove(0);
let increment = if !matches!(tokens[0].token, Token::RParen) {
Some(Box::new(Statement::from(StatementKind::Expr(parse_expression(tokens)?))))
} else {
None
};
if tokens.is_empty() || !matches!(tokens[0].token, Token::RParen) {
return Err(raise_parse_error_at(tokens));
}
tokens.remove(0);
let body = if !tokens.is_empty() && matches!(tokens[0].token, Token::LBrace) {
tokens.remove(0); let b = parse_statements(tokens)?;
if tokens.is_empty() || !matches!(tokens[0].token, Token::RBrace) {
return Err(raise_parse_error_at(tokens));
}
tokens.remove(0); b
} else {
let s = parse_statement(tokens)?;
if !tokens.is_empty() && matches!(tokens[0].token, Token::Semicolon | Token::LineTerminator) {
tokens.remove(0);
}
vec![s]
};
return Ok(StatementKind::For(init, condition, increment, body));
}
if !tokens.is_empty() && matches!(tokens[0].token, Token::Return) {
tokens.remove(0); if tokens.is_empty() || matches!(tokens[0].token, Token::Semicolon | Token::LineTerminator) {
return Ok(StatementKind::Return(None));
}
let expr = parse_expression(tokens)?;
return Ok(StatementKind::Return(Some(expr)));
}
if !tokens.is_empty()
&& (matches!(tokens[0].token, Token::Let) || matches!(tokens[0].token, Token::Var) || matches!(tokens[0].token, Token::Const))
{
let is_const = matches!(tokens[0].token, Token::Const);
let is_var = matches!(tokens[0].token, Token::Var);
let decl_keyword_token = tokens[0].clone();
tokens.remove(0); log::trace!(
"parse_statement: after consuming declaration keyword; next tokens (first 8): {:?}",
tokens.iter().take(8).collect::<Vec<_>>()
);
if !tokens.is_empty() && matches!(tokens[0].token, Token::LBracket) {
let pattern = parse_array_destructuring_pattern(tokens)?;
if tokens.is_empty() || !matches!(tokens[0].token, Token::Assign) {
return Err(raise_parse_error_at(tokens));
}
tokens.remove(0); let expr = parse_expression(tokens)?;
if is_const {
return Ok(StatementKind::ConstDestructuringArray(pattern, expr));
} else if is_var {
return Ok(StatementKind::VarDestructuringArray(pattern, expr));
} else {
return Ok(StatementKind::LetDestructuringArray(pattern, expr));
}
} else if !tokens.is_empty() && matches!(tokens[0].token, Token::LBrace) {
let pattern = parse_object_destructuring_pattern(tokens)?;
log::trace!(
"parse_statement: after object pattern parse; next tokens (first 8): {:?}",
tokens.iter().take(8).collect::<Vec<_>>()
);
if tokens.is_empty() || !matches!(tokens[0].token, Token::Assign) {
log::error!(
"parse_statement: expected '=' after object pattern but found (first 8): {:?}",
tokens.iter().take(8).collect::<Vec<_>>()
);
return Err(raise_parse_error_at(tokens));
}
tokens.remove(0); let expr = parse_expression(tokens)?;
if is_const {
return Ok(StatementKind::ConstDestructuringObject(pattern, expr));
} else if is_var {
return Ok(StatementKind::VarDestructuringObject(pattern, expr));
} else {
return Ok(StatementKind::LetDestructuringObject(pattern, expr));
}
} else {
let mut declarations = Vec::new();
loop {
let name = if let Some(Token::Identifier(n)) = tokens.first().map(|t| t.token.clone()) {
tokens.remove(0);
n
} else {
if declarations.is_empty() {
tokens.insert(0, decl_keyword_token);
}
return Err(raise_parse_error_at(tokens));
};
let init = if !tokens.is_empty() && matches!(tokens[0].token, Token::Assign) {
tokens.remove(0); Some(parse_assignment(tokens)?)
} else {
None
};
declarations.push((name, init));
if !tokens.is_empty() && matches!(tokens[0].token, Token::Comma) {
tokens.remove(0); while !tokens.is_empty() && matches!(tokens[0].token, Token::LineTerminator) {
tokens.remove(0);
}
} else {
break;
}
}
if is_const {
let mut const_decls = Vec::new();
for (name, init) in declarations {
if let Some(expr) = init {
const_decls.push((name, expr));
} else {
return Err(raise_parse_error!("Const declaration must have initializer"));
}
}
return Ok(StatementKind::Const(const_decls));
} else if is_var {
return Ok(StatementKind::Var(declarations));
} else {
return Ok(StatementKind::Let(declarations));
}
}
}
if !tokens.is_empty() && matches!(tokens[0].token, Token::Class) {
tokens.remove(0); if let Some(Token::Identifier(name)) = tokens.first().map(|t| t.token.clone()) {
tokens.remove(0);
let extends = if !tokens.is_empty() && matches!(tokens[0].token, Token::Extends) {
tokens.remove(0); let super_expr = parse_expression(tokens)?;
Some(super_expr)
} else {
None
};
let members = parse_class_body(tokens)?;
return Ok(StatementKind::Class(name, extends, members));
}
}
let expr = parse_expression(tokens)?;
if let Expr::Assign(target, value) = &expr
&& let Expr::Var(name, _, _) = target.as_ref()
{
return Ok(StatementKind::Assign(name.clone(), *value.clone()));
}
Ok(StatementKind::Expr(expr))
}