use super::{Parser, cook};
use crate::ast::{
Class, ClassField, ClassMember, ClassMethod, Expr, Function, MethodKind, PropertyKey, Stmt,
};
use crate::error::Result;
use crate::lexer::{Keyword as Kw, TokenKind};
use alloc::boxed::Box;
use alloc::format;
use alloc::vec::Vec;
impl<'src> Parser<'src> {
pub(super) fn parse_class_declaration(&mut self) -> Result<Stmt> {
let class = self.parse_class(true)?;
Ok(Stmt::Class(class))
}
pub(super) fn parse_class_expr(&mut self) -> Result<Expr> {
let class = self.parse_class(false)?;
Ok(Expr::Class(class))
}
pub(super) fn parse_default_class(&mut self) -> Result<Stmt> {
let class = self.parse_class(false)?;
Ok(Stmt::Class(class))
}
fn parse_class(&mut self, require_name: bool) -> Result<Class> {
let start = self.expect(TokenKind::Keyword(Kw::Class))?.span;
let id = if self.at_binding_ident()
&& !self.at(TokenKind::Keyword(Kw::Extends))
&& !self.at(TokenKind::LBrace)
{
Some(self.parse_binding_ident()?)
} else if require_name {
return Err(self.err("a class declaration requires a name"));
} else {
None
};
let super_class = if self.eat(TokenKind::Keyword(Kw::Extends)) {
Some(Box::new(self.parse_lhs()?))
} else {
None
};
self.expect(TokenKind::LBrace)?;
let mut body = Vec::new();
while !self.at(TokenKind::RBrace) && !self.at(TokenKind::Eof) {
if self.eat(TokenKind::Semicolon) {
continue; }
body.push(self.parse_class_member()?);
}
let end = self.expect(TokenKind::RBrace)?.span;
Ok(Class {
id,
super_class,
body,
span: start.to(end),
})
}
fn parse_class_member(&mut self) -> Result<ClassMember> {
let start = self.cur_span();
let is_static = if self.at(TokenKind::Keyword(Kw::Static)) {
match self.nth_kind(1) {
TokenKind::LBrace => {
self.bump(); let body = self.parse_block_body()?;
return Ok(ClassMember::StaticBlock {
body,
span: start.to(self.prev_span()),
});
}
TokenKind::LParen | TokenKind::Eq | TokenKind::Semicolon | TokenKind::RBrace => {
false
}
_ => {
self.bump(); true
}
}
} else {
false
};
let is_async = self.at(TokenKind::Keyword(Kw::Async))
&& !self.nth_newline(1)
&& !self.modifier_is_name(1);
if is_async {
self.bump();
}
let is_generator = self.eat(TokenKind::Star);
let accessor = if !is_async
&& !is_generator
&& matches!(self.peek(), TokenKind::Keyword(Kw::Get | Kw::Set))
&& !self.modifier_is_name(1)
{
let k = if self.at(TokenKind::Keyword(Kw::Get)) {
MethodKind::Get
} else {
MethodKind::Set
};
self.bump();
Some(k)
} else {
None
};
let key = self.parse_class_key()?;
if self.at(TokenKind::LParen) {
let value = self.parse_method_tail(is_async, is_generator)?;
let kind = match accessor {
Some(k) => k,
None if is_constructor_key(&key, is_static, is_async, is_generator) => {
MethodKind::Constructor
}
None => MethodKind::Method,
};
return Ok(ClassMember::Method(ClassMethod {
key,
kind,
value,
is_static,
span: start.to(self.prev_span()),
}));
}
if accessor.is_some() || is_async || is_generator {
return Err(self.err_at(start, "expected `(` after method modifier"));
}
let value = if self.eat(TokenKind::Eq) {
Some(self.parse_assignment()?)
} else {
None
};
self.semicolon()?;
Ok(ClassMember::Field(ClassField {
key,
value,
is_static,
span: start.to(self.prev_span()),
}))
}
pub(super) fn parse_class_key(&mut self) -> Result<PropertyKey> {
let tok = self.peek_tok();
match tok.kind {
TokenKind::PrivateName => {
self.bump();
Ok(PropertyKey::Private(tok.text(self.source)[1..].into()))
}
TokenKind::LBracket => {
self.bump();
let expr = self.without_no_in(Self::parse_assignment)?;
self.expect(TokenKind::RBracket)?;
Ok(PropertyKey::Computed(Box::new(expr)))
}
TokenKind::String => {
self.bump();
Ok(PropertyKey::Str(
cook::string(tok.text(self.source), tok.span)?.into(),
))
}
TokenKind::Number => {
self.bump();
Ok(PropertyKey::Number(cook::number(tok.text(self.source))))
}
TokenKind::Identifier => {
self.bump();
Ok(PropertyKey::Ident(tok.text(self.source).into()))
}
TokenKind::Keyword(kw) => {
self.bump();
Ok(PropertyKey::Ident(kw.as_str().into()))
}
_ => Err(self.err(format!(
"expected a class member name, found {:?}",
tok.kind
))),
}
}
pub(super) fn modifier_is_name(&self, n: usize) -> bool {
matches!(
self.nth_kind(n),
TokenKind::LParen | TokenKind::Eq | TokenKind::Semicolon | TokenKind::RBrace
)
}
}
fn is_constructor_key(
key: &PropertyKey,
is_static: bool,
is_async: bool,
is_generator: bool,
) -> bool {
!is_static
&& !is_async
&& !is_generator
&& matches!(key, PropertyKey::Ident(name) if &**name == "constructor")
}
impl<'src> Parser<'src> {
pub(super) fn parse_method_tail(
&mut self,
is_async: bool,
is_generator: bool,
) -> Result<Function> {
let start = self.cur_span();
self.expect(TokenKind::LParen)?;
let params = self.parse_params()?;
self.expect(TokenKind::RParen)?;
let body = self.in_function_context(is_generator, is_async, Self::parse_block_body)?;
Ok(Function {
id: None,
params,
body,
is_async,
is_generator,
span: start.to(self.prev_span()),
})
}
}