use super::expr::Asi;
use super::pattern::is_valid_pattern_identifier;
use super::pattern::ParsePatternRules;
use super::ParseCtx;
use super::Parser;
use crate::ast::ClassOrObjectMemberKey;
use crate::ast::ClassOrObjectMemberValue;
use crate::ast::Node;
use crate::ast::Syntax;
use crate::error::SyntaxResult;
use crate::lex::KEYWORDS_MAPPING;
use crate::loc::Loc;
use crate::token::TokenType;
pub struct ParseClassBodyResult {
pub members: Vec<Node>, pub end: Loc,
}
pub struct ParseClassOrObjectMemberResult {
pub key: ClassOrObjectMemberKey,
pub key_loc: Loc,
pub value: ClassOrObjectMemberValue,
pub value_loc: Loc,
}
impl<'a> Parser<'a> {
pub fn parse_class_body(&mut self, ctx: ParseCtx) -> SyntaxResult<ParseClassBodyResult> {
self.require(TokenType::BraceOpen)?;
let mut members = Vec::new();
while self.peek()?.typ != TokenType::BraceClose {
let static_ = self.consume_if(TokenType::KeywordStatic)?.match_loc();
let ParseClassOrObjectMemberResult {
key,
key_loc,
value,
value_loc,
} = self.parse_class_or_object_member(
ctx,
TokenType::Equals,
TokenType::Semicolon,
&mut Asi::can(),
)?;
self.consume_if(TokenType::Semicolon)?;
members.push(Node::new(
key_loc.add_option(static_) + value_loc,
Syntax::ClassMember {
key,
static_: static_.is_some(),
value,
},
));
}
let end = self.require(TokenType::BraceClose)?.loc;
Ok(ParseClassBodyResult { members, end })
}
pub fn parse_class_or_object_member(
&mut self,
ctx: ParseCtx,
value_delimiter: TokenType,
statement_delimiter: TokenType,
property_initialiser_asi: &mut Asi,
) -> SyntaxResult<ParseClassOrObjectMemberResult> {
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_loc, key) = if self.consume_if(TokenType::BracketOpen)?.is_match() {
let key = self.parse_expr(ctx, TokenType::BracketClose)?;
self.require(TokenType::BracketClose)?;
(key.loc, ClassOrObjectMemberKey::Computed(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
};
(loc, ClassOrObjectMemberKey::Direct(self.string(loc)))
};
let (value_loc, value) =
if is_generator || is_async || self.peek()?.typ == TokenType::ParenthesisOpen {
let parameters = self.parse_function_parameters(ctx)?;
let body = self.parse_function_body(ctx.with_rules(ParsePatternRules {
await_allowed: !is_async && ctx.rules.await_allowed,
yield_allowed: !is_generator && ctx.rules.yield_allowed,
}))?;
(body.loc, ClassOrObjectMemberValue::Method {
function: Node::new(body.loc, Syntax::Function {
arrow: false,
async_: is_async,
generator: is_generator,
parameters,
body,
}),
})
} else if is_getter {
let mut loc = self.require(TokenType::ParenthesisOpen)?.loc;
self.require(TokenType::ParenthesisClose)?;
let body = self.parse_function_body(ctx)?;
loc += body.loc;
(loc, ClassOrObjectMemberValue::Getter {
function: Node::new(loc, Syntax::Function {
arrow: false,
async_: false,
body,
generator: false,
parameters: Vec::new(),
}),
})
} else if is_setter {
let mut loc = self.require(TokenType::ParenthesisOpen)?.loc;
let param = self.parse_pattern(ctx)?;
self.require(TokenType::ParenthesisClose)?;
let body = self.parse_function_body(ctx)?;
loc += body.loc;
(loc, ClassOrObjectMemberValue::Setter {
function: Node::new(loc, Syntax::Function {
arrow: false,
async_: false,
generator: false,
body,
parameters: vec![Node::new(param.loc, Syntax::ParamDecl {
rest: false,
pattern: param,
default_value: None,
})],
}),
})
} 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,
} {
(key_loc, ClassOrObjectMemberValue::Property {
initializer: None,
})
} else {
let loc_start = self.require(value_delimiter)?.loc;
let value = self.parse_expr_until_either_with_asi(
ctx,
statement_delimiter,
TokenType::BraceClose,
property_initialiser_asi,
)?;
(loc_start + value.loc, ClassOrObjectMemberValue::Property {
initializer: Some(value),
})
};
Ok(ParseClassOrObjectMemberResult {
key,
key_loc,
value,
value_loc,
})
}
}