parse_js/parse/
pattern.rs

1use super::ParseCtx;
2use super::Parser;
3use crate::ast::ArrayPatternElement;
4use crate::ast::ClassOrObjectMemberKey;
5use crate::ast::Node;
6use crate::ast::Syntax;
7use crate::error::SyntaxErrorType;
8use crate::error::SyntaxResult;
9use crate::token::TokenType;
10use crate::token::UNRESERVED_KEYWORDS;
11
12#[derive(Clone, Copy)]
13pub struct ParsePatternRules {
14  // `await` is not allowed as an arrow function parameter or a parameter/variable inside an async function.
15  pub await_allowed: bool,
16  // `yield` is not allowed as a parameter/variable inside a generator function.
17  pub yield_allowed: bool,
18}
19
20impl ParsePatternRules {
21  pub fn with_await_allowed(&self, await_allowed: bool) -> ParsePatternRules {
22    Self {
23      await_allowed,
24      ..*self
25    }
26  }
27
28  pub fn with_yield_allowed(&self, yield_allowed: bool) -> ParsePatternRules {
29    Self {
30      yield_allowed,
31      ..*self
32    }
33  }
34}
35
36pub fn is_valid_pattern_identifier(typ: TokenType, rules: ParsePatternRules) -> bool {
37  match typ {
38    TokenType::Identifier => true,
39    TokenType::KeywordAwait => rules.await_allowed,
40    TokenType::KeywordYield => rules.yield_allowed,
41    t => UNRESERVED_KEYWORDS.contains(&t),
42  }
43}
44
45impl<'a> Parser<'a> {
46  fn parse_pattern_identifier(&mut self, ctx: ParseCtx) -> SyntaxResult<Node> {
47    if !is_valid_pattern_identifier(self.peek()?.typ, ctx.rules) {
48      return Err(
49        self
50          .peek()?
51          .error(SyntaxErrorType::ExpectedSyntax("identifier")),
52      );
53    }
54    let t = self.next()?;
55    let node = Node::new(t.loc, Syntax::IdentifierPattern {
56      name: self.string(t.loc),
57    });
58    Ok(node)
59  }
60
61  pub fn parse_pattern(&mut self, ctx: ParseCtx) -> SyntaxResult<Node> {
62    let checkpoint = self.checkpoint();
63    let t = self.next()?;
64    Ok(match t.typ {
65      t if is_valid_pattern_identifier(t, ctx.rules) => {
66        self.restore_checkpoint(checkpoint);
67        self.parse_pattern_identifier(ctx)?
68      }
69      TokenType::BraceOpen => {
70        let mut properties = Vec::new();
71        let mut rest = None;
72        loop {
73          if self.peek()?.typ == TokenType::BraceClose {
74            break;
75          };
76          let mut loc = self.peek()?.loc;
77          // Check inside loop to ensure that it must come first or after a comma.
78          if self.consume_if(TokenType::DotDotDot)?.is_match() {
79            rest = Some(self.parse_pattern_identifier(ctx)?);
80            break;
81          };
82
83          let (key_loc, key) = if self.consume_if(TokenType::BracketOpen)?.is_match() {
84            let expr = self.parse_expr(ctx, TokenType::BracketClose)?;
85            self.require(TokenType::BracketClose)?;
86            (expr.loc, ClassOrObjectMemberKey::Computed(expr))
87          } else {
88            let name = self.next()?;
89            if !is_valid_pattern_identifier(name.typ, ctx.rules) {
90              return Err(name.error(SyntaxErrorType::ExpectedNotFound));
91            };
92            (
93              name.loc,
94              ClassOrObjectMemberKey::Direct(self.string(name.loc)),
95            )
96          };
97          let (shorthand, target) = if self.consume_if(TokenType::Colon)?.is_match() {
98            (false, self.parse_pattern(ctx)?)
99          } else {
100            match &key {
101              ClassOrObjectMemberKey::Computed(name) => {
102                return Err(name.error(SyntaxErrorType::ExpectedSyntax(
103                  "object pattern property subpattern",
104                )));
105              }
106              ClassOrObjectMemberKey::Direct(name) => (
107                true,
108                Node::new(key_loc, Syntax::IdentifierPattern { name: name.clone() }),
109              ),
110            }
111          };
112          let default_value = if self.consume_if(TokenType::Equals)?.is_match() {
113            Some(self.parse_expr_until_either(ctx, TokenType::Comma, TokenType::BraceClose)?)
114          } else {
115            None
116          };
117          loc.extend(default_value.as_ref().unwrap_or(&target).loc);
118          let direct_key_name = match &key {
119            ClassOrObjectMemberKey::Direct(name) => Some(name.clone()),
120            _ => None,
121          };
122          let property = Node::new(loc, Syntax::ObjectPatternProperty {
123            key,
124            target,
125            default_value,
126            shorthand,
127          });
128          properties.push(property);
129          // This will break if `}`.
130          if !self.consume_if(TokenType::Comma)?.is_match() {
131            break;
132          };
133        }
134        let close = self.require(TokenType::BraceClose)?;
135        Node::new(t.loc + close.loc, Syntax::ObjectPattern {
136          properties,
137          rest,
138        })
139      }
140      TokenType::BracketOpen => {
141        let mut elements = Vec::<Option<ArrayPatternElement>>::new();
142        let mut rest = None;
143        loop {
144          if self.consume_if(TokenType::BracketClose)?.is_match() {
145            break;
146          };
147          // Check inside loop to ensure that it must come first or after a comma.
148          if self.consume_if(TokenType::DotDotDot)?.is_match() {
149            rest = Some(self.parse_pattern(ctx)?);
150            break;
151          };
152
153          // An unnamed element is allowed to ignore that element.
154          if self.consume_if(TokenType::Comma)?.is_match() {
155            elements.push(None);
156          } else {
157            let target = self.parse_pattern(ctx)?;
158            let default_value = if self.consume_if(TokenType::Equals)?.is_match() {
159              Some(self.parse_expr_until_either(ctx, TokenType::Comma, TokenType::BracketClose)?)
160            } else {
161              None
162            };
163            elements.push(Some(ArrayPatternElement {
164              target,
165              default_value,
166            }));
167            // This will break if `]`.
168            if !self.consume_if(TokenType::Comma)?.is_match() {
169              break;
170            };
171          };
172        }
173        let close = self.require(TokenType::BracketClose)?;
174        Node::new(t.loc + close.loc, Syntax::ArrayPattern { elements, rest })
175      }
176      _ => return Err(t.error(SyntaxErrorType::ExpectedSyntax("pattern"))),
177    })
178  }
179}