mod class;
mod cook;
mod function;
mod module;
mod stmt;
#[cfg(test)]
mod tests;
use crate::ast::{
Argument, ArrayElement, AssignOp, BinaryOp, Expr, Ident, LogicalOp, ObjectMember, PropertyKey,
TemplateElement, TemplateLiteral, UnaryOp, UpdateOp,
};
use crate::common::Span;
use crate::error::{Error, Result};
use crate::lexer::{Keyword as Kw, Lexer, Token, TokenKind};
use alloc::boxed::Box;
use alloc::format;
use alloc::vec::Vec;
const MAX_PARSE_DEPTH: u32 = crate::limits::DEFAULT_MAX_PARSE_DEPTH;
pub struct Parser<'src> {
source: &'src str,
tokens: Vec<Token>,
pos: usize,
no_in: bool,
in_generator: bool,
in_async: bool,
depth: u32,
}
struct DepthGuard<'a, 'src> {
parser: &'a mut Parser<'src>,
}
impl Drop for DepthGuard<'_, '_> {
fn drop(&mut self) {
self.parser.depth -= 1;
}
}
impl<'src> Parser<'src> {
pub fn new(source: &'src str) -> Result<Self> {
let tokens = Lexer::new(source).tokenize()?;
Ok(Self {
source,
tokens,
pos: 0,
no_in: false,
in_generator: false,
in_async: false,
depth: 0,
})
}
pub fn parse_expression_entry(source: &'src str) -> Result<Expr> {
let mut p = Parser::new(source)?;
let expr = p.parse_expression()?;
p.expect_eof()?;
Ok(expr)
}
#[inline]
fn peek(&self) -> TokenKind {
self.tokens[self.pos].kind
}
#[inline]
fn peek_tok(&self) -> Token {
self.tokens[self.pos]
}
#[inline]
fn nth_kind(&self, n: usize) -> TokenKind {
self.tokens
.get(self.pos + n)
.map_or(TokenKind::Eof, |t| t.kind)
}
#[inline]
fn nth_newline(&self, n: usize) -> bool {
self.tokens
.get(self.pos + n)
.is_some_and(|t| t.newline_before)
}
#[inline]
fn at(&self, kind: TokenKind) -> bool {
self.peek() == kind
}
#[inline]
fn cur_span(&self) -> Span {
self.tokens[self.pos].span
}
#[inline]
fn prev_span(&self) -> Span {
self.tokens[self.pos.saturating_sub(1)].span
}
fn bump(&mut self) -> Token {
let tok = self.tokens[self.pos];
if tok.kind != TokenKind::Eof {
self.pos += 1;
}
tok
}
fn eat(&mut self, kind: TokenKind) -> bool {
if self.at(kind) {
self.bump();
true
} else {
false
}
}
fn expect(&mut self, kind: TokenKind) -> Result<Token> {
if self.at(kind) {
Ok(self.bump())
} else {
Err(self.err(format!("expected {kind:?}, found {:?}", self.peek())))
}
}
fn expect_eof(&self) -> Result<()> {
if self.at(TokenKind::Eof) {
Ok(())
} else {
Err(self.err(format!("unexpected trailing token {:?}", self.peek())))
}
}
fn err(&self, message: impl Into<alloc::string::String>) -> Error {
Error::syntax(message, self.cur_span())
}
fn enter_recursion(&mut self) -> Result<DepthGuard<'_, 'src>> {
if self.depth >= MAX_PARSE_DEPTH {
return Err(self.err("maximum parser nesting depth exceeded"));
}
self.depth += 1;
Ok(DepthGuard { parser: self })
}
fn err_at(&self, span: Span, message: impl Into<alloc::string::String>) -> Error {
Error::syntax(message, span)
}
pub(crate) fn parse_expression(&mut self) -> Result<Expr> {
let first = self.parse_assignment()?;
if !self.at(TokenKind::Comma) {
return Ok(first);
}
let start = first.span();
let mut expressions = alloc::vec![first];
while self.eat(TokenKind::Comma) {
expressions.push(self.parse_assignment()?);
}
let span = start.to(self.prev_span());
Ok(Expr::Sequence { expressions, span })
}
fn parse_assignment(&mut self) -> Result<Expr> {
let guard = self.enter_recursion()?;
guard.parser.parse_assignment_inner()
}
fn parse_assignment_inner(&mut self) -> Result<Expr> {
if self.in_generator && self.at(TokenKind::Keyword(Kw::Yield)) {
return self.parse_yield();
}
if self.at_arrow_head() {
return self.parse_arrow();
}
let left = self.parse_conditional()?;
let Some(op) = assign_op(self.peek()) else {
return Ok(left);
};
if !left.is_assignment_target() {
return Err(self.err_at(left.span(), "invalid assignment target"));
}
self.bump();
let value = self.parse_assignment()?;
let span = left.span().to(value.span());
Ok(Expr::Assign {
op,
target: Box::new(left),
value: Box::new(value),
span,
})
}
fn parse_conditional(&mut self) -> Result<Expr> {
let test = self.parse_short_circuit()?;
if !self.at(TokenKind::Question) {
return Ok(test);
}
self.bump();
let consequent = self.without_no_in(Self::parse_assignment)?;
self.expect(TokenKind::Colon)?;
let alternate = self.parse_assignment()?;
let span = test.span().to(alternate.span());
Ok(Expr::Conditional {
test: Box::new(test),
consequent: Box::new(consequent),
alternate: Box::new(alternate),
span,
})
}
fn parse_short_circuit(&mut self) -> Result<Expr> {
let first = self.parse_bitor()?;
match self.peek() {
TokenKind::QuestionQuestion => self.parse_coalesce(first),
TokenKind::AmpAmp | TokenKind::PipePipe => self.parse_logical_or(first),
_ => Ok(first),
}
}
fn parse_coalesce(&mut self, mut left: Expr) -> Result<Expr> {
while self.at(TokenKind::QuestionQuestion) {
self.bump();
let right = self.parse_bitor()?;
left = logical(LogicalOp::Nullish, left, right);
}
if self.at(TokenKind::AmpAmp) || self.at(TokenKind::PipePipe) {
return Err(self.err("`??` cannot be mixed with `||`/`&&` without parentheses"));
}
Ok(left)
}
fn parse_logical_or(&mut self, first: Expr) -> Result<Expr> {
let mut left = self.continue_logical_and(first)?;
while self.at(TokenKind::PipePipe) {
self.bump();
let right_first = self.parse_bitor()?;
let right = self.continue_logical_and(right_first)?;
left = logical(LogicalOp::Or, left, right);
}
if self.at(TokenKind::QuestionQuestion) {
return Err(self.err("`??` cannot be mixed with `||`/`&&` without parentheses"));
}
Ok(left)
}
fn continue_logical_and(&mut self, mut left: Expr) -> Result<Expr> {
while self.at(TokenKind::AmpAmp) {
self.bump();
let right = self.parse_bitor()?;
left = logical(LogicalOp::And, left, right);
}
Ok(left)
}
fn parse_bitor(&mut self) -> Result<Expr> {
let mut left = self.parse_bitxor()?;
while self.at(TokenKind::Pipe) {
self.bump();
let right = self.parse_bitxor()?;
left = binary(BinaryOp::BitOr, left, right);
}
Ok(left)
}
fn parse_bitxor(&mut self) -> Result<Expr> {
let mut left = self.parse_bitand()?;
while self.at(TokenKind::Caret) {
self.bump();
let right = self.parse_bitand()?;
left = binary(BinaryOp::BitXor, left, right);
}
Ok(left)
}
fn parse_bitand(&mut self) -> Result<Expr> {
let mut left = self.parse_equality()?;
while self.at(TokenKind::Amp) {
self.bump();
let right = self.parse_equality()?;
left = binary(BinaryOp::BitAnd, left, right);
}
Ok(left)
}
fn parse_equality(&mut self) -> Result<Expr> {
let mut left = self.parse_relational()?;
loop {
let op = match self.peek() {
TokenKind::EqEq => BinaryOp::EqEq,
TokenKind::BangEq => BinaryOp::NotEq,
TokenKind::EqEqEq => BinaryOp::EqEqEq,
TokenKind::BangEqEq => BinaryOp::NotEqEq,
_ => break,
};
self.bump();
let right = self.parse_relational()?;
left = binary(op, left, right);
}
Ok(left)
}
fn parse_relational(&mut self) -> Result<Expr> {
let mut left = self.parse_shift()?;
loop {
let op = match self.peek() {
TokenKind::Lt => BinaryOp::Lt,
TokenKind::Gt => BinaryOp::Gt,
TokenKind::LtEq => BinaryOp::LtEq,
TokenKind::GtEq => BinaryOp::GtEq,
TokenKind::Keyword(Kw::Instanceof) => BinaryOp::Instanceof,
TokenKind::Keyword(Kw::In) if !self.no_in => BinaryOp::In,
_ => break,
};
self.bump();
let right = self.parse_shift()?;
left = binary(op, left, right);
}
Ok(left)
}
fn parse_shift(&mut self) -> Result<Expr> {
let mut left = self.parse_additive()?;
loop {
let op = match self.peek() {
TokenKind::Shl => BinaryOp::Shl,
TokenKind::Shr => BinaryOp::Shr,
TokenKind::Ushr => BinaryOp::Ushr,
_ => break,
};
self.bump();
let right = self.parse_additive()?;
left = binary(op, left, right);
}
Ok(left)
}
fn parse_additive(&mut self) -> Result<Expr> {
let mut left = self.parse_multiplicative()?;
loop {
let op = match self.peek() {
TokenKind::Plus => BinaryOp::Add,
TokenKind::Minus => BinaryOp::Sub,
_ => break,
};
self.bump();
let right = self.parse_multiplicative()?;
left = binary(op, left, right);
}
Ok(left)
}
fn parse_multiplicative(&mut self) -> Result<Expr> {
let mut left = self.parse_exponent()?;
loop {
let op = match self.peek() {
TokenKind::Star => BinaryOp::Mul,
TokenKind::Slash => BinaryOp::Div,
TokenKind::Percent => BinaryOp::Mod,
_ => break,
};
self.bump();
let right = self.parse_exponent()?;
left = binary(op, left, right);
}
Ok(left)
}
fn parse_exponent(&mut self) -> Result<Expr> {
let guard = self.enter_recursion()?;
guard.parser.parse_exponent_inner()
}
fn parse_exponent_inner(&mut self) -> Result<Expr> {
let unary_lead = self.at_unary_operator();
let base = self.parse_unary()?;
if self.at(TokenKind::StarStar) {
if unary_lead {
return Err(self.err_at(
base.span(),
"unary operator before `**` must be parenthesized",
));
}
self.bump();
let exp = self.parse_exponent()?;
let span = base.span().to(exp.span());
return Ok(Expr::Binary {
op: BinaryOp::Exp,
left: Box::new(base),
right: Box::new(exp),
span,
});
}
Ok(base)
}
fn at_unary_operator(&self) -> bool {
matches!(
self.peek(),
TokenKind::Plus
| TokenKind::Minus
| TokenKind::Bang
| TokenKind::Tilde
| TokenKind::Keyword(Kw::Typeof)
| TokenKind::Keyword(Kw::Void)
| TokenKind::Keyword(Kw::Delete)
)
}
fn parse_unary(&mut self) -> Result<Expr> {
let guard = self.enter_recursion()?;
guard.parser.parse_unary_inner()
}
fn parse_unary_inner(&mut self) -> Result<Expr> {
let tok = self.peek_tok();
if self.in_async && tok.kind == TokenKind::Keyword(Kw::Await) {
self.bump();
let argument = self.parse_unary()?;
let span = tok.span.to(argument.span());
return Ok(Expr::Await {
argument: Box::new(argument),
span,
});
}
let unary = match tok.kind {
TokenKind::Plus => Some(UnaryOp::Plus),
TokenKind::Minus => Some(UnaryOp::Minus),
TokenKind::Bang => Some(UnaryOp::Not),
TokenKind::Tilde => Some(UnaryOp::BitNot),
TokenKind::Keyword(Kw::Typeof) => Some(UnaryOp::Typeof),
TokenKind::Keyword(Kw::Void) => Some(UnaryOp::Void),
TokenKind::Keyword(Kw::Delete) => Some(UnaryOp::Delete),
_ => None,
};
if let Some(op) = unary {
self.bump();
let argument = self.parse_unary()?;
let span = tok.span.to(argument.span());
return Ok(Expr::Unary {
op,
argument: Box::new(argument),
span,
});
}
if let Some(op) = update_op(tok.kind) {
self.bump();
let argument = self.parse_unary()?;
let span = tok.span.to(argument.span());
return Ok(Expr::Update {
op,
prefix: true,
argument: Box::new(argument),
span,
});
}
self.parse_postfix()
}
fn parse_postfix(&mut self) -> Result<Expr> {
let expr = self.parse_lhs()?;
let tok = self.peek_tok();
if !tok.newline_before
&& let Some(op) = update_op(tok.kind)
{
self.bump();
let span = expr.span().to(tok.span);
return Ok(Expr::Update {
op,
prefix: false,
argument: Box::new(expr),
span,
});
}
Ok(expr)
}
fn parse_lhs(&mut self) -> Result<Expr> {
let head = if self.at(TokenKind::Keyword(Kw::New)) {
self.parse_new()?
} else {
self.parse_primary()?
};
self.parse_tails(head, true)
}
fn parse_new(&mut self) -> Result<Expr> {
let guard = self.enter_recursion()?;
guard.parser.parse_new_inner()
}
fn parse_new_inner(&mut self) -> Result<Expr> {
let start = self.cur_span();
self.bump(); if self.at(TokenKind::Dot) {
self.bump(); if self.at(TokenKind::Keyword(Kw::Target)) {
self.bump();
return Ok(Expr::NewTarget(start.to(self.prev_span())));
}
return Err(self.err("expected `target` after `new.`"));
}
let callee = if self.at(TokenKind::Keyword(Kw::New)) {
self.parse_new()?
} else {
let primary = self.parse_primary()?;
self.parse_tails(primary, false)?
};
let arguments = if self.at(TokenKind::LParen) {
self.parse_arguments()?
} else {
Vec::new()
};
let span = start.to(self.prev_span());
Ok(Expr::New {
callee: Box::new(callee),
arguments,
span,
})
}
fn parse_tails(&mut self, mut expr: Expr, allow_call: bool) -> Result<Expr> {
let start = expr.span();
let mut saw_optional = false;
loop {
match self.peek() {
TokenKind::Dot => {
self.bump();
let (property, end) = self.parse_member_property()?;
let span = expr.span().to(end);
expr = Expr::Member {
object: Box::new(expr),
property,
optional: false,
span,
};
}
TokenKind::LBracket => {
self.bump();
let index = self.without_no_in(Self::parse_expression)?;
let end = self.expect(TokenKind::RBracket)?.span;
let span = expr.span().to(end);
expr = Expr::Member {
object: Box::new(expr),
property: PropertyKey::Computed(Box::new(index)),
optional: false,
span,
};
}
TokenKind::QuestionDot => {
self.bump();
saw_optional = true;
expr = self.parse_optional_tail(expr, allow_call)?;
}
TokenKind::LParen if allow_call => {
let arguments = self.parse_arguments()?;
let span = expr.span().to(self.prev_span());
expr = Expr::Call {
callee: Box::new(expr),
arguments,
optional: false,
span,
};
}
TokenKind::NoSubstitutionTemplate | TokenKind::TemplateHead if allow_call => {
let quasi = self.parse_template_literal()?;
let span = expr.span().to(quasi.span);
expr = Expr::TaggedTemplate {
tag: Box::new(expr),
quasi,
span,
};
}
_ => {
if saw_optional {
let span = start.to(expr.span());
expr = Expr::OptChain {
expr: Box::new(expr),
span,
};
}
return Ok(expr);
}
}
}
}
fn parse_optional_tail(&mut self, object: Expr, allow_call: bool) -> Result<Expr> {
match self.peek() {
TokenKind::LParen if allow_call => {
let arguments = self.parse_arguments()?;
let span = object.span().to(self.prev_span());
Ok(Expr::Call {
callee: Box::new(object),
arguments,
optional: true,
span,
})
}
TokenKind::LBracket => {
self.bump();
let index = self.without_no_in(Self::parse_expression)?;
let end = self.expect(TokenKind::RBracket)?.span;
let span = object.span().to(end);
Ok(Expr::Member {
object: Box::new(object),
property: PropertyKey::Computed(Box::new(index)),
optional: true,
span,
})
}
_ => {
let (property, end) = self.parse_member_property()?;
let span = object.span().to(end);
Ok(Expr::Member {
object: Box::new(object),
property,
optional: true,
span,
})
}
}
}
fn parse_member_property(&mut self) -> Result<(PropertyKey, Span)> {
let tok = self.peek_tok();
match tok.kind {
TokenKind::Identifier => {
self.bump();
Ok((PropertyKey::Ident(tok.text(self.source).into()), tok.span))
}
TokenKind::Keyword(kw) => {
self.bump();
Ok((PropertyKey::Ident(kw.as_str().into()), tok.span))
}
TokenKind::PrivateName => {
self.bump();
let name = &tok.text(self.source)[1..];
Ok((PropertyKey::Private(name.into()), tok.span))
}
_ => Err(self.err(format!("expected a property name, found {:?}", tok.kind))),
}
}
fn parse_arguments(&mut self) -> Result<Vec<Argument>> {
self.expect(TokenKind::LParen)?;
let mut args = Vec::new();
while !self.at(TokenKind::RParen) {
if self.eat(TokenKind::DotDotDot) {
args.push(Argument::Spread(self.parse_assignment()?));
} else {
args.push(Argument::Item(self.parse_assignment()?));
}
if !self.eat(TokenKind::Comma) {
break;
}
}
self.expect(TokenKind::RParen)?;
Ok(args)
}
fn parse_primary(&mut self) -> Result<Expr> {
let tok = self.peek_tok();
match tok.kind {
TokenKind::Number => {
self.bump();
Ok(Expr::Number {
value: cook::number(tok.text(self.source)),
span: tok.span,
})
}
TokenKind::BigInt => {
self.bump();
Ok(Expr::BigInt {
digits: cook::bigint(tok.text(self.source)).into(),
span: tok.span,
})
}
TokenKind::String => {
self.bump();
Ok(Expr::Str {
value: cook::string(tok.text(self.source), tok.span)?.into(),
span: tok.span,
})
}
TokenKind::Regex => {
self.bump();
let (pattern, flags) = split_regex(tok.text(self.source));
Ok(Expr::Regex {
pattern: pattern.into(),
flags: flags.into(),
span: tok.span,
})
}
TokenKind::NoSubstitutionTemplate | TokenKind::TemplateHead => {
Ok(Expr::Template(self.parse_template_literal()?))
}
TokenKind::Keyword(Kw::True) => {
self.bump();
Ok(Expr::Bool {
value: true,
span: tok.span,
})
}
TokenKind::Keyword(Kw::False) => {
self.bump();
Ok(Expr::Bool {
value: false,
span: tok.span,
})
}
TokenKind::Keyword(Kw::Null) => {
self.bump();
Ok(Expr::Null(tok.span))
}
TokenKind::Keyword(Kw::This) => {
self.bump();
Ok(Expr::This(tok.span))
}
TokenKind::Keyword(Kw::Super) => {
self.bump();
Ok(Expr::Super(tok.span))
}
TokenKind::Identifier => {
self.bump();
Ok(Expr::Ident(Ident::new(tok.text(self.source), tok.span)))
}
TokenKind::Keyword(Kw::Async)
if self.nth_kind(1) == TokenKind::Keyword(Kw::Function) && !self.nth_newline(1) =>
{
self.bump(); self.parse_function_expr(true, tok.span)
}
TokenKind::Keyword(kw) if kw.is_contextual() => {
self.bump();
Ok(Expr::Ident(Ident::new(kw.as_str(), tok.span)))
}
TokenKind::LParen => self.parse_paren(),
TokenKind::LBracket => self.parse_array(),
TokenKind::LBrace => self.parse_object(),
TokenKind::Keyword(Kw::Function) => self.parse_function_expr(false, tok.span),
TokenKind::Keyword(Kw::Class) => self.parse_class_expr(),
TokenKind::PrivateName => {
self.bump();
let name = &tok.text(self.source)[1..];
Ok(Expr::PrivateName(name.into(), tok.span))
}
_ => Err(self.err(format!("unexpected token {:?}", tok.kind))),
}
}
fn parse_paren(&mut self) -> Result<Expr> {
self.expect(TokenKind::LParen)?;
if self.at(TokenKind::RParen) {
return Err(self.err("unexpected `)` (empty parentheses)"));
}
let expr = self.without_no_in(Self::parse_expression)?;
self.expect(TokenKind::RParen)?;
Ok(expr)
}
fn parse_array(&mut self) -> Result<Expr> {
let start = self.expect(TokenKind::LBracket)?.span;
let mut elements = Vec::new();
while !self.at(TokenKind::RBracket) {
if self.at(TokenKind::Comma) {
self.bump();
elements.push(ArrayElement::Hole);
continue;
}
if self.eat(TokenKind::DotDotDot) {
elements.push(ArrayElement::Spread(self.parse_assignment()?));
} else {
elements.push(ArrayElement::Item(self.parse_assignment()?));
}
if !self.at(TokenKind::RBracket) {
self.expect(TokenKind::Comma)?;
}
}
let end = self.expect(TokenKind::RBracket)?.span;
Ok(Expr::Array {
elements,
span: start.to(end),
})
}
fn parse_object(&mut self) -> Result<Expr> {
let start = self.expect(TokenKind::LBrace)?.span;
let mut members = Vec::new();
while !self.at(TokenKind::RBrace) {
members.push(self.parse_object_member()?);
if !self.at(TokenKind::RBrace) {
self.expect(TokenKind::Comma)?;
}
}
let end = self.expect(TokenKind::RBrace)?.span;
Ok(Expr::Object {
members,
span: start.to(end),
})
}
fn parse_object_member(&mut self) -> Result<ObjectMember> {
let start = self.cur_span();
if self.eat(TokenKind::DotDotDot) {
let value = self.parse_assignment()?;
let span = start.to(value.span());
return Ok(ObjectMember::Spread {
value: Box::new(value),
span,
});
}
if self.at(TokenKind::Keyword(Kw::Async))
&& !self.nth_newline(1)
&& !self.modifier_is_name(1)
&& !matches!(self.nth_kind(1), TokenKind::Comma | TokenKind::Colon)
{
self.bump(); let is_gen = self.eat(TokenKind::Star);
let key = self.parse_class_key()?;
let func = self.parse_method_tail(true, is_gen)?;
return Ok(ObjectMember::Property {
key,
value: Box::new(Expr::Function(func)),
shorthand: false,
span: start.to(self.prev_span()),
});
}
if self.at(TokenKind::Star) {
self.bump();
let key = self.parse_class_key()?;
let func = self.parse_method_tail(false, true)?;
return Ok(ObjectMember::Property {
key,
value: Box::new(Expr::Function(func)),
shorthand: false,
span: start.to(self.prev_span()),
});
}
if let TokenKind::Keyword(kw @ (Kw::Get | Kw::Set)) = self.peek()
&& !matches!(
self.nth_kind(1),
TokenKind::LParen
| TokenKind::Colon
| TokenKind::Comma
| TokenKind::RBrace
| TokenKind::Eq
)
{
self.bump(); let key = self.parse_class_key()?;
let func = self.parse_method_tail(false, false)?;
return Ok(ObjectMember::Accessor {
is_getter: kw == Kw::Get,
key,
value: func,
span: start.to(self.prev_span()),
});
}
if self.at(TokenKind::LBracket) {
self.bump();
let key_expr = self.without_no_in(Self::parse_assignment)?;
self.expect(TokenKind::RBracket)?;
if self.at(TokenKind::LParen) {
let func = self.parse_method_tail(false, false)?;
let span = start.to(self.prev_span());
return Ok(ObjectMember::Property {
key: PropertyKey::Computed(Box::new(key_expr)),
value: Box::new(Expr::Function(func)),
shorthand: false,
span,
});
}
self.expect(TokenKind::Colon)?;
let value = self.parse_assignment()?;
let span = start.to(value.span());
return Ok(ObjectMember::Property {
key: PropertyKey::Computed(Box::new(key_expr)),
value: Box::new(value),
shorthand: false,
span,
});
}
let tok = self.peek_tok();
if matches!(tok.kind, TokenKind::String | TokenKind::Number) {
self.bump();
let key = if tok.kind == TokenKind::String {
PropertyKey::Str(cook::string_key(tok.text(self.source), tok.span)?.into())
} else {
PropertyKey::Number(cook::number(tok.text(self.source)))
};
self.expect(TokenKind::Colon)?;
let value = self.parse_assignment()?;
let span = start.to(value.span());
return Ok(ObjectMember::Property {
key,
value: Box::new(value),
shorthand: false,
span,
});
}
let (name, can_shorthand): (Box<str>, bool) = match tok.kind {
TokenKind::Identifier => (tok.text(self.source).into(), true),
TokenKind::Keyword(kw) if kw.is_contextual() => (kw.as_str().into(), true),
TokenKind::Keyword(kw) => (kw.as_str().into(), false),
_ => return Err(self.err(format!("expected a property key, found {:?}", tok.kind))),
};
self.bump();
if self.at(TokenKind::LParen) {
let func = self.parse_method_tail(false, false)?;
let span = start.to(self.prev_span());
return Ok(ObjectMember::Property {
key: PropertyKey::Ident(name),
value: Box::new(Expr::Function(func)),
shorthand: false,
span,
});
}
if self.eat(TokenKind::Colon) {
let value = self.parse_assignment()?;
let span = start.to(value.span());
return Ok(ObjectMember::Property {
key: PropertyKey::Ident(name),
value: Box::new(value),
shorthand: false,
span,
});
}
if !can_shorthand {
return Err(self.err_at(
tok.span,
"reserved word cannot be used as a shorthand property",
));
}
let ident_expr = Expr::Ident(Ident::new(name.clone(), tok.span));
if self.eat(TokenKind::Eq) {
let default = self.parse_assignment()?;
let span = start.to(default.span());
return Ok(ObjectMember::Property {
key: PropertyKey::Ident(name),
value: Box::new(Expr::Assign {
op: crate::ast::AssignOp::Assign,
target: Box::new(ident_expr),
value: Box::new(default),
span,
}),
shorthand: true,
span,
});
}
Ok(ObjectMember::Property {
key: PropertyKey::Ident(name),
value: Box::new(ident_expr),
shorthand: true,
span: tok.span,
})
}
fn parse_template_literal(&mut self) -> Result<TemplateLiteral> {
let start = self.cur_span();
let mut quasis = Vec::new();
let mut expressions = Vec::new();
let head = self.bump();
if head.kind == TokenKind::NoSubstitutionTemplate {
quasis.push(self.template_element(head, TemplatePart::NoSub));
return Ok(TemplateLiteral {
quasis,
expressions,
span: start.to(head.span),
});
}
quasis.push(self.template_element(head, TemplatePart::Head));
loop {
expressions.push(self.without_no_in(Self::parse_expression)?);
let part = self.bump();
match part.kind {
TokenKind::TemplateMiddle => {
quasis.push(self.template_element(part, TemplatePart::Middle));
}
TokenKind::TemplateTail => {
quasis.push(self.template_element(part, TemplatePart::Tail));
return Ok(TemplateLiteral {
quasis,
expressions,
span: start.to(part.span),
});
}
_ => {
return Err(self.err_at(
part.span,
format!("expected a template continuation, found {:?}", part.kind),
));
}
}
}
}
fn template_element(&self, tok: Token, part: TemplatePart) -> TemplateElement {
let text = tok.text(self.source);
let inner = match part {
TemplatePart::NoSub | TemplatePart::Tail => &text[1..text.len() - 1],
TemplatePart::Head | TemplatePart::Middle => &text[1..text.len() - 2],
};
let cooked = cook::decode_escapes(inner, tok.span).ok().map(Into::into);
TemplateElement {
raw: inner.into(),
cooked,
span: tok.span,
}
}
fn without_no_in<T>(&mut self, f: impl FnOnce(&mut Self) -> Result<T>) -> Result<T> {
let saved = self.no_in;
self.no_in = false;
let r = f(self);
self.no_in = saved;
r
}
fn with_no_in<T>(&mut self, f: impl FnOnce(&mut Self) -> Result<T>) -> Result<T> {
let saved = self.no_in;
self.no_in = true;
let r = f(self);
self.no_in = saved;
r
}
fn parse_yield(&mut self) -> Result<Expr> {
let start = self.bump().span; let delegate = self.eat(TokenKind::Star);
let argument = if delegate || self.yield_has_argument() {
Some(Box::new(self.parse_assignment()?))
} else {
None
};
let span = start.to(self.prev_span());
Ok(Expr::Yield {
argument,
delegate,
span,
})
}
fn yield_has_argument(&self) -> bool {
if self.peek_tok().newline_before {
return false;
}
!matches!(
self.peek(),
TokenKind::RParen
| TokenKind::RBracket
| TokenKind::RBrace
| TokenKind::Comma
| TokenKind::Semicolon
| TokenKind::Colon
| TokenKind::Eof
)
}
pub(crate) fn in_function_context<T>(
&mut self,
is_generator: bool,
is_async: bool,
f: impl FnOnce(&mut Self) -> Result<T>,
) -> Result<T> {
let saved = (self.in_generator, self.in_async);
self.in_generator = is_generator;
self.in_async = is_async;
let r = f(self);
(self.in_generator, self.in_async) = saved;
r
}
}
fn binary(op: BinaryOp, left: Expr, right: Expr) -> Expr {
let span = left.span().to(right.span());
Expr::Binary {
op,
left: Box::new(left),
right: Box::new(right),
span,
}
}
fn logical(op: LogicalOp, left: Expr, right: Expr) -> Expr {
let span = left.span().to(right.span());
Expr::Logical {
op,
left: Box::new(left),
right: Box::new(right),
span,
}
}
fn assign_op(kind: TokenKind) -> Option<AssignOp> {
Some(match kind {
TokenKind::Eq => AssignOp::Assign,
TokenKind::PlusEq => AssignOp::AddAssign,
TokenKind::MinusEq => AssignOp::SubAssign,
TokenKind::StarEq => AssignOp::MulAssign,
TokenKind::SlashEq => AssignOp::DivAssign,
TokenKind::PercentEq => AssignOp::ModAssign,
TokenKind::StarStarEq => AssignOp::ExpAssign,
TokenKind::ShlEq => AssignOp::ShlAssign,
TokenKind::ShrEq => AssignOp::ShrAssign,
TokenKind::UshrEq => AssignOp::UshrAssign,
TokenKind::AmpEq => AssignOp::BitAndAssign,
TokenKind::PipeEq => AssignOp::BitOrAssign,
TokenKind::CaretEq => AssignOp::BitXorAssign,
TokenKind::AmpAmpEq => AssignOp::AndAssign,
TokenKind::PipePipeEq => AssignOp::OrAssign,
TokenKind::QuestionQuestionEq => AssignOp::NullishAssign,
_ => return None,
})
}
fn update_op(kind: TokenKind) -> Option<UpdateOp> {
match kind {
TokenKind::PlusPlus => Some(UpdateOp::Inc),
TokenKind::MinusMinus => Some(UpdateOp::Dec),
_ => None,
}
}
fn split_regex(text: &str) -> (&str, &str) {
let close = text.rfind('/').unwrap_or(text.len() - 1);
(&text[1..close], &text[close + 1..])
}
#[derive(Clone, Copy)]
enum TemplatePart {
NoSub,
Head,
Middle,
Tail,
}