use super::ParseCtx;
use super::Parser;
use crate::ast::ArrayPatternElement;
use crate::ast::ClassOrObjectMemberKey;
use crate::ast::Node;
use crate::ast::Syntax;
use crate::error::SyntaxErrorType;
use crate::error::SyntaxResult;
use crate::token::TokenType;
use crate::token::UNRESERVED_KEYWORDS;
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum ParsePatternAction {
None,
AddToBlockScope,
AddToClosureScope,
}
#[derive(Clone, Copy)]
pub struct ParsePatternRules {
pub await_allowed: bool,
pub yield_allowed: bool,
}
impl ParsePatternRules {
pub fn with_await_allowed(&self, await_allowed: bool) -> ParsePatternRules {
Self {
await_allowed,
..*self
}
}
pub fn with_yield_allowed(&self, yield_allowed: bool) -> ParsePatternRules {
Self {
yield_allowed,
..*self
}
}
}
pub fn is_valid_pattern_identifier(typ: TokenType, rules: ParsePatternRules) -> bool {
match typ {
TokenType::Identifier => true,
TokenType::KeywordAwait => rules.await_allowed,
TokenType::KeywordYield => rules.yield_allowed,
t => UNRESERVED_KEYWORDS.contains(&t),
}
}
impl<'a> Parser<'a> {
fn parse_pattern_identifier(
&mut self,
ctx: ParseCtx<'a>,
action: ParsePatternAction,
) -> SyntaxResult<'a, Node<'a>> {
if !is_valid_pattern_identifier(self.peek()?.typ(), ctx.rules) {
return Err(
self
.peek()?
.error(SyntaxErrorType::ExpectedSyntax("identifier")),
);
}
let t = self.next()?;
let node = ctx.create_node(t.loc().clone(), Syntax::IdentifierPattern {
name: t.loc().clone(),
});
match action {
ParsePatternAction::None => {}
ParsePatternAction::AddToBlockScope => {
ctx.scope.add_block_symbol(t.loc().clone(), node)?;
}
ParsePatternAction::AddToClosureScope => {
if let Some(closure) = ctx.scope.find_self_or_ancestor(|t| t.is_closure()) {
closure.add_symbol(t.loc().clone(), node)?;
};
}
};
Ok(node)
}
pub fn parse_pattern(
&mut self,
ctx: ParseCtx<'a>,
action: ParsePatternAction,
) -> SyntaxResult<'a, Node<'a>> {
let checkpoint = self.checkpoint();
let t = self.next()?;
Ok(match t.typ() {
t if is_valid_pattern_identifier(t, ctx.rules) => {
self.restore_checkpoint(checkpoint);
self.parse_pattern_identifier(ctx, action)?
}
TokenType::BraceOpen => {
let mut properties = ctx.session.new_vec();
let mut rest = None;
loop {
if self.peek()?.typ() == TokenType::BraceClose {
break;
};
let mut loc = self.peek()?.loc_take();
if self.consume_if(TokenType::DotDotDot)?.is_match() {
rest = Some(self.parse_pattern_identifier(ctx, action)?);
break;
};
let key = self.parse_class_or_object_member_key(ctx)?;
let target = if self.consume_if(TokenType::Colon)?.is_match() {
Some(self.parse_pattern(ctx, action)?)
} else {
if let ClassOrObjectMemberKey::Computed(name) = key {
return Err(name.error(SyntaxErrorType::ExpectedSyntax(
"object pattern property subpattern",
)));
};
None
};
let default_value = if self.consume_if(TokenType::Equals)?.is_match() {
Some(self.parse_expr_until_either(ctx, TokenType::Comma, TokenType::BraceClose)?)
} else {
None
};
if let Some(n) = default_value.or(target) {
loc.extend(n.loc());
};
let direct_key_name = match &key {
ClassOrObjectMemberKey::Direct(name) => Some(name.clone()),
_ => None,
};
let property = ctx.create_node(loc, Syntax::ObjectPatternProperty {
key,
target,
default_value,
});
properties.push(property);
match (direct_key_name, target, action) {
(Some(name), None, ParsePatternAction::AddToBlockScope) => {
ctx.scope.add_block_symbol(name, property)?;
}
(Some(name), None, ParsePatternAction::AddToClosureScope) => {
if let Some(closure) = ctx.scope.find_self_or_ancestor(|t| t.is_closure()) {
closure.add_symbol(name, property)?;
}
}
_ => {}
};
if !self.consume_if(TokenType::Comma)?.is_match() {
break;
};
}
let close = self.require(TokenType::BraceClose)?;
ctx.create_node(t.loc() + close.loc(), Syntax::ObjectPattern {
properties,
rest,
})
}
TokenType::BracketOpen => {
let mut elements = ctx.session.new_vec::<Option<ArrayPatternElement>>();
let mut rest = None;
loop {
if self.consume_if(TokenType::BracketClose)?.is_match() {
break;
};
if self.consume_if(TokenType::DotDotDot)?.is_match() {
rest = Some(self.parse_pattern(ctx, action)?);
break;
};
if self.consume_if(TokenType::Comma)?.is_match() {
elements.push(None);
} else {
let target = self.parse_pattern(ctx, action)?;
let default_value = if self.consume_if(TokenType::Equals)?.is_match() {
Some(self.parse_expr_until_either(ctx, TokenType::Comma, TokenType::BracketClose)?)
} else {
None
};
elements.push(Some(ArrayPatternElement {
target,
default_value,
}));
if !self.consume_if(TokenType::Comma)?.is_match() {
break;
};
};
}
let close = self.require(TokenType::BracketClose)?;
ctx.create_node(t.loc() + close.loc(), Syntax::ArrayPattern {
elements,
rest,
})
}
_ => return Err(t.error(SyntaxErrorType::ExpectedSyntax("pattern"))),
})
}
}