use crate::ast::{ArrayPatternElement, ClassOrObjectMemberKey, NodeId, Syntax};
use crate::error::{SyntaxErrorType, SyntaxResult};
use crate::parse::expr::parse_expr_until_either;
use crate::parse::literal::parse_class_or_object_member_key;
use crate::parse::parser::Parser;
use crate::symbol::{ScopeId, Symbol};
use crate::token::{TokenType, UNRESERVED_KEYWORDS};
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum ParsePatternAction {
None,
AddToBlockScope,
AddToClosureScope,
}
pub struct ParsePatternSyntax {
pub await_allowed: bool,
pub yield_allowed: bool,
}
pub fn is_valid_pattern_identifier(typ: TokenType, syntax: &ParsePatternSyntax) -> bool {
match typ {
TokenType::Identifier => true,
TokenType::KeywordAwait if syntax.await_allowed => true,
TokenType::KeywordYield if syntax.yield_allowed => true,
t if UNRESERVED_KEYWORDS.contains(&t) => true,
_ => false,
}
}
fn parse_pattern_identifier(
scope: ScopeId,
parser: &mut Parser,
action: ParsePatternAction,
syntax: &ParsePatternSyntax,
) -> SyntaxResult<NodeId> {
if !is_valid_pattern_identifier(parser.peek()?.typ(), syntax) {
return Err(parser
.peek()?
.error(SyntaxErrorType::ExpectedSyntax("identifier")));
}
let t = parser.next()?;
let node_id = parser.create_node(
scope,
t.loc().clone(),
Syntax::IdentifierPattern {
name: t.loc().clone(),
},
);
match action {
ParsePatternAction::None => {}
ParsePatternAction::AddToBlockScope => {
let scope = &mut parser[scope];
scope.add_block_symbol(t.loc().clone(), Symbol::new(node_id))?;
}
ParsePatternAction::AddToClosureScope => {
if let Some(closure_id) = parser[scope].self_or_ancestor_closure() {
parser[closure_id].add_symbol(t.loc().clone(), Symbol::new(node_id))?;
};
}
};
Ok(node_id)
}
pub fn parse_pattern(
scope: ScopeId,
parser: &mut Parser,
action: ParsePatternAction,
syntax: &ParsePatternSyntax,
) -> SyntaxResult<NodeId> {
let checkpoint = parser.checkpoint();
let t = parser.next()?;
Ok(match t.typ() {
t if is_valid_pattern_identifier(t, syntax) => {
parser.restore_checkpoint(checkpoint);
parse_pattern_identifier(scope, parser, action, syntax)?
}
TokenType::BraceOpen => {
let mut properties = Vec::<NodeId>::new();
let mut rest = None;
loop {
if parser.peek()?.typ() == TokenType::BraceClose {
break;
};
let mut loc = parser.peek()?.loc_take();
if parser.consume_if(TokenType::DotDotDot)?.is_match() {
rest = Some(parse_pattern_identifier(scope, parser, action, syntax)?);
break;
};
let key = parse_class_or_object_member_key(scope, parser, syntax)?;
let target = if parser.consume_if(TokenType::Colon)?.is_match() {
Some(parse_pattern(scope, parser, action, syntax)?)
} else {
if let ClassOrObjectMemberKey::Computed(name) = key {
return Err(parser[name].error(SyntaxErrorType::ExpectedSyntax(
"object pattern property subpattern",
)));
};
None
};
let default_value = if parser.consume_if(TokenType::Equals)?.is_match() {
Some(parse_expr_until_either(
scope,
parser,
TokenType::Comma,
TokenType::BraceClose,
syntax,
)?)
} else {
None
};
if let Some(n) = default_value.or(target) {
loc.extend(parser[n].loc());
};
let direct_key_name = match &key {
ClassOrObjectMemberKey::Direct(name) => Some(name.clone()),
_ => None,
};
let property = parser.create_node(
scope,
loc,
Syntax::ObjectPatternProperty {
key,
target,
default_value,
},
);
properties.push(property);
match (direct_key_name, target, action) {
(Some(name), None, ParsePatternAction::AddToBlockScope) => {
parser[scope].add_block_symbol(name, Symbol::new(property))?;
}
(Some(name), None, ParsePatternAction::AddToClosureScope) => {
if let Some(closure_id) = parser[scope].self_or_ancestor_closure() {
parser[closure_id].add_symbol(name, Symbol::new(property))?;
}
}
_ => {}
};
if !parser.consume_if(TokenType::Comma)?.is_match() {
break;
};
}
let close = parser.require(TokenType::BraceClose)?;
parser.create_node(
scope,
t.loc() + close.loc(),
Syntax::ObjectPattern { properties, rest },
)
}
TokenType::BracketOpen => {
let mut elements = Vec::<Option<ArrayPatternElement>>::new();
let mut rest = None;
loop {
if parser.consume_if(TokenType::BracketClose)?.is_match() {
break;
};
if parser.consume_if(TokenType::DotDotDot)?.is_match() {
rest = Some(parse_pattern(scope, parser, action, syntax)?);
break;
};
if parser.consume_if(TokenType::Comma)?.is_match() {
elements.push(None);
} else {
let target = parse_pattern(scope, parser, action, syntax)?;
let default_value = if parser.consume_if(TokenType::Equals)?.is_match() {
Some(parse_expr_until_either(
scope,
parser,
TokenType::Comma,
TokenType::BracketClose,
syntax,
)?)
} else {
None
};
elements.push(Some(ArrayPatternElement {
target,
default_value,
}));
if !parser.consume_if(TokenType::Comma)?.is_match() {
break;
};
};
}
let close = parser.require(TokenType::BracketClose)?;
parser.create_node(
scope,
t.loc() + close.loc(),
Syntax::ArrayPattern { elements, rest },
)
}
_ => return Err(t.error(SyntaxErrorType::ExpectedSyntax("pattern"))),
})
}