use super::expr::Asi;
use super::pattern::is_valid_pattern_identifier;
use super::pattern::ParsePatternAction;
use super::pattern::ParsePatternRules;
use super::ParseCtx;
use super::Parser;
use crate::ast::ClassMember;
use crate::ast::ClassOrObjectMemberKey;
use crate::ast::ClassOrObjectMemberValue;
use crate::error::SyntaxResult;
use crate::lex::KEYWORDS_MAPPING;
use crate::session::SessionVec;
use crate::source::SourceRange;
use crate::symbol::ScopeType;
use crate::token::TokenType;
pub struct ParseClassBodyResult<'a> {
pub members: SessionVec<'a, ClassMember<'a>>,
pub end: SourceRange<'a>,
}
pub struct ParseClassOrObjectMemberResult<'a> {
pub key: ClassOrObjectMemberKey<'a>,
pub value: ClassOrObjectMemberValue<'a>,
}
impl<'a> Parser<'a> {
pub fn parse_class_body(
&mut self,
ctx: ParseCtx<'a>,
) -> SyntaxResult<'a, ParseClassBodyResult<'a>> {
self.require(TokenType::BraceOpen)?;
let mut members = ctx.session.new_vec();
while self.peek()?.typ != TokenType::BraceClose {
let statik = self.consume_if(TokenType::KeywordStatic)?.is_match();
let ParseClassOrObjectMemberResult { key, value } = self.parse_class_or_object_member(
ctx,
TokenType::Equals,
TokenType::Semicolon,
&mut Asi::can(),
)?;
self.consume_if(TokenType::Semicolon)?;
members.push(ClassMember { key, statik, value });
}
let end = self.require(TokenType::BraceClose)?.loc;
Ok(ParseClassBodyResult { members, end })
}
pub fn parse_class_or_object_member(
&mut self,
ctx: ParseCtx<'a>,
value_delimiter: TokenType,
statement_delimiter: TokenType,
property_initialiser_asi: &mut Asi,
) -> SyntaxResult<'a, ParseClassOrObjectMemberResult<'a>> {
let checkpoint = self.checkpoint();
let mut is_getter = false;
let mut is_setter = false;
let mut is_async = false;
if self.consume_if(TokenType::KeywordGet)?.is_match() {
is_getter = true;
} else if self.consume_if(TokenType::KeywordSet)?.is_match() {
is_setter = true;
} else if self.consume_if(TokenType::KeywordAsync)?.is_match() {
is_async = true;
}
if is_getter || is_setter || is_async {
let next_tok = self.peek()?.typ;
if next_tok == value_delimiter || next_tok == TokenType::ParenthesisOpen {
self.restore_checkpoint(checkpoint);
is_getter = false;
is_setter = false;
is_async = false;
};
}
let is_generator = self.consume_if(TokenType::Asterisk)?.is_match();
let key = if self.consume_if(TokenType::BracketOpen)?.is_match() {
let key = ClassOrObjectMemberKey::Computed(self.parse_expr(ctx, TokenType::BracketClose)?);
self.require(TokenType::BracketClose)?;
key
} else {
let loc = if let Some(str) = self.consume_if(TokenType::LiteralString)?.match_loc() {
str
} else if let Some(num) = self.consume_if(TokenType::LiteralNumber)?.match_loc() {
num
} else if let Some(loc) = self.consume_if(TokenType::PrivateMember)?.match_loc() {
loc
} else if let Some(loc) = self
.consume_if_pred(|t| is_valid_pattern_identifier(t.typ, ctx.rules))?
.match_loc()
{
loc
} else {
self
.require_predicate(
|t| KEYWORDS_MAPPING.contains_key(&t),
"keyword or identifier",
)?
.loc
};
ClassOrObjectMemberKey::Direct(loc)
};
let value = if is_generator || is_async || self.peek()?.typ == TokenType::ParenthesisOpen {
let fn_scope = ctx.create_child_scope(ScopeType::NonArrowFunction);
let fn_ctx = ctx.with_scope(fn_scope);
let signature = self.parse_signature_function(fn_ctx)?;
ClassOrObjectMemberValue::Method {
is_async,
generator: is_generator,
signature,
body: self.parse_stmt_block_with_existing_scope(fn_ctx.with_rules(ParsePatternRules {
await_allowed: !is_async && ctx.rules.await_allowed,
yield_allowed: !is_generator && ctx.rules.yield_allowed,
}))?,
}
} else if is_getter {
self.require(TokenType::ParenthesisOpen)?;
self.require(TokenType::ParenthesisClose)?;
ClassOrObjectMemberValue::Getter {
body: self.parse_stmt_block(ctx)?,
}
} else if is_setter {
let setter_scope = ctx.create_child_scope(ScopeType::NonArrowFunction);
let setter_ctx = ctx.with_scope(setter_scope);
self.require(TokenType::ParenthesisOpen)?;
let parameter = self.parse_pattern(setter_ctx, ParsePatternAction::AddToClosureScope)?;
self.require(TokenType::ParenthesisClose)?;
ClassOrObjectMemberValue::Setter {
parameter,
body: self.parse_stmt_block(setter_ctx)?,
}
} else if match key {
ClassOrObjectMemberKey::Direct(_) => match self.peek()? {
t if t.typ == TokenType::BraceClose => true,
t if t.typ == statement_delimiter => true,
t if property_initialiser_asi.can_end_with_asi && t.preceded_by_line_terminator => true,
_ => false,
},
_ => false,
} {
ClassOrObjectMemberValue::Property { initializer: None }
} else {
self.require(value_delimiter)?;
let value = self.parse_expr_until_either_with_asi(
ctx,
statement_delimiter,
TokenType::BraceClose,
property_initialiser_asi,
)?;
ClassOrObjectMemberValue::Property {
initializer: Some(value),
}
};
Ok(ParseClassOrObjectMemberResult { key, value })
}
}