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)]
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) -> SyntaxResult<Node> {
if !is_valid_pattern_identifier(self.peek()?.typ, ctx.rules) {
return Err(
self
.peek()?
.error(SyntaxErrorType::ExpectedSyntax("identifier")),
);
}
let t = self.next()?;
let node = Node::new(t.loc, Syntax::IdentifierPattern {
name: self.string(t.loc),
});
Ok(node)
}
pub fn parse_pattern(&mut self, ctx: ParseCtx) -> SyntaxResult<Node> {
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)?
}
TokenType::BraceOpen => {
let mut properties = Vec::new();
let mut rest = None;
loop {
if self.peek()?.typ == TokenType::BraceClose {
break;
};
let mut loc = self.peek()?.loc;
if self.consume_if(TokenType::DotDotDot)?.is_match() {
rest = Some(self.parse_pattern_identifier(ctx)?);
break;
};
let (key_loc, key) = if self.consume_if(TokenType::BracketOpen)?.is_match() {
let expr = self.parse_expr(ctx, TokenType::BracketClose)?;
self.require(TokenType::BracketClose)?;
(expr.loc, ClassOrObjectMemberKey::Computed(expr))
} else {
let name = self.next()?;
if !is_valid_pattern_identifier(name.typ, ctx.rules) {
return Err(name.error(SyntaxErrorType::ExpectedNotFound));
};
(
name.loc,
ClassOrObjectMemberKey::Direct(self.string(name.loc)),
)
};
let (shorthand, target) = if self.consume_if(TokenType::Colon)?.is_match() {
(false, self.parse_pattern(ctx)?)
} else {
match &key {
ClassOrObjectMemberKey::Computed(name) => {
return Err(name.error(SyntaxErrorType::ExpectedSyntax(
"object pattern property subpattern",
)));
}
ClassOrObjectMemberKey::Direct(name) => (
true,
Node::new(key_loc, Syntax::IdentifierPattern { name: name.clone() }),
),
}
};
let default_value = if self.consume_if(TokenType::Equals)?.is_match() {
Some(self.parse_expr_until_either(ctx, TokenType::Comma, TokenType::BraceClose)?)
} else {
None
};
loc.extend(default_value.as_ref().unwrap_or(&target).loc);
let direct_key_name = match &key {
ClassOrObjectMemberKey::Direct(name) => Some(name.clone()),
_ => None,
};
let property = Node::new(loc, Syntax::ObjectPatternProperty {
key,
target,
default_value,
shorthand,
});
properties.push(property);
if !self.consume_if(TokenType::Comma)?.is_match() {
break;
};
}
let close = self.require(TokenType::BraceClose)?;
Node::new(t.loc + close.loc, Syntax::ObjectPattern {
properties,
rest,
})
}
TokenType::BracketOpen => {
let mut elements = Vec::<Option<ArrayPatternElement>>::new();
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)?);
break;
};
if self.consume_if(TokenType::Comma)?.is_match() {
elements.push(None);
} else {
let target = self.parse_pattern(ctx)?;
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)?;
Node::new(t.loc + close.loc, Syntax::ArrayPattern { elements, rest })
}
_ => return Err(t.error(SyntaxErrorType::ExpectedSyntax("pattern"))),
})
}
}