use super::Context;
use super::errors::{ParseError, ParseErrorKind, ParseResult};
use super::operators::{to_unary_op, to_update_op};
use super::precedence::prec;
use crate::compiler::ir::{IrNode, IrSpan};
use crate::compiler::parser::Parser;
use crate::compiler::syntax::SyntaxKind;
impl Parser {
pub(super) fn parse_primary_expr(&mut self) -> ParseResult<IrNode> {
self.skip_whitespace();
let Some(token) = self.current() else {
return Err(ParseError::unexpected_eof(
self.current_byte_offset(),
"expression",
));
};
let kind = token.kind;
let text = token.text.clone();
match kind {
SyntaxKind::At => self.parse_interpolated_ident(),
SyntaxKind::PlusPlus | SyntaxKind::MinusMinus => self.parse_prefix_update(),
SyntaxKind::TypeofKw | SyntaxKind::VoidKw | SyntaxKind::DeleteKw => {
self.parse_unary_keyword_expr()
}
SyntaxKind::Exclaim => self.parse_unary_expr(),
SyntaxKind::NewKw => self.parse_new_expr(),
SyntaxKind::AwaitKw => self.parse_await_expr(),
SyntaxKind::YieldKw => self.parse_yield_expr(),
SyntaxKind::FunctionKw => self.parse_function_expr(),
SyntaxKind::AsyncKw => self.parse_async_expr(),
SyntaxKind::ClassKw => self.parse_class_expr(),
SyntaxKind::LParen => self.parse_paren_or_arrow(),
SyntaxKind::LBracket => self.parse_array_literal(),
SyntaxKind::LBrace => self.parse_object_literal(),
SyntaxKind::Backtick => self.parse_template_literal(),
SyntaxKind::DoubleQuote | SyntaxKind::SingleQuote => self.parse_string_literal(),
SyntaxKind::ThisKw => {
let token = self.consume().unwrap();
Ok(IrNode::this_expr(&token))
}
SyntaxKind::SuperKw => {
let token = self.consume().unwrap();
Ok(IrNode::super_expr(&token))
}
SyntaxKind::NullKw => {
let token = self.consume().unwrap();
Ok(IrNode::null_lit(&token))
}
SyntaxKind::TrueKw => {
let token = self.consume().unwrap();
Ok(IrNode::bool_lit(&token))
}
SyntaxKind::FalseKw => {
let token = self.consume().unwrap();
Ok(IrNode::bool_lit(&token))
}
SyntaxKind::UndefinedKw => {
let token = self.consume().unwrap();
Ok(IrNode::ident_with(&token, "undefined"))
}
SyntaxKind::Hash => self.parse_private_name(),
SyntaxKind::Ident => self.parse_identifier_expr(),
SyntaxKind::Text => {
if matches!(text.as_str(), "-" | "+" | "!" | "~") {
self.parse_unary_expr()
} else if text
.chars()
.next()
.map(|c| c.is_ascii_digit())
.unwrap_or(false)
{
self.parse_numeric_literal()
} else {
self.parse_identifier_expr()
}
}
_ if kind.is_ts_keyword() => self.parse_identifier_expr(),
SyntaxKind::BraceHashIf => self.parse_if_expr(),
SyntaxKind::BraceHashFor => self.parse_for_expr(),
SyntaxKind::BraceHashWhile => self.parse_while_expr(),
SyntaxKind::BraceHashMatch => self.parse_match_expr(),
_ => Err(ParseError::expected_expression_found(
self.current_byte_offset(),
kind,
)),
}
}
fn parse_prefix_update(&mut self) -> ParseResult<IrNode> {
let start_byte = self.current_byte_offset();
let token = self.consume().ok_or_else(|| {
ParseError::new(ParseErrorKind::UnexpectedEof, self.current_byte_offset())
.with_context("prefix update operator")
})?;
let op = to_update_op(token.kind).ok_or_else(|| {
ParseError::new(
ParseErrorKind::InvalidPrefixOperator,
self.current_byte_offset(),
)
.with_found(&token.text)
})?;
self.skip_whitespace();
let arg = self.parse_unary_operand()?;
Ok(IrNode::UpdateExpr {
span: IrSpan::new(start_byte, self.current_byte_offset()),
op,
prefix: true,
arg: Box::new(arg),
})
}
fn parse_unary_keyword_expr(&mut self) -> ParseResult<IrNode> {
let start_byte = self.current_byte_offset();
let token = self.consume().ok_or_else(|| {
ParseError::new(ParseErrorKind::UnexpectedEof, self.current_byte_offset())
.with_context("unary keyword operator")
})?;
let op = to_unary_op(token.kind, &token.text).ok_or_else(|| {
ParseError::new(
ParseErrorKind::InvalidPrefixOperator,
self.current_byte_offset(),
)
.with_found(&token.text)
})?;
self.skip_whitespace();
let arg = self.parse_unary_operand()?;
Ok(IrNode::UnaryExpr {
span: IrSpan::new(start_byte, self.current_byte_offset()),
op,
arg: Box::new(arg),
})
}
fn parse_unary_expr(&mut self) -> ParseResult<IrNode> {
let start_byte = self.current_byte_offset();
let token = self.consume().ok_or_else(|| {
ParseError::new(ParseErrorKind::UnexpectedEof, self.current_byte_offset())
.with_context("unary operator")
})?;
let op = to_unary_op(token.kind, &token.text).ok_or_else(|| {
ParseError::new(
ParseErrorKind::InvalidPrefixOperator,
self.current_byte_offset(),
)
.with_found(&token.text)
})?;
self.skip_whitespace();
let arg = self.parse_unary_operand()?;
Ok(IrNode::UnaryExpr {
span: IrSpan::new(start_byte, self.current_byte_offset()),
op,
arg: Box::new(arg),
})
}
fn parse_unary_operand(&mut self) -> ParseResult<IrNode> {
self.skip_whitespace();
let Some(token) = self.current() else {
return Err(ParseError::new(
ParseErrorKind::MissingOperand,
self.current_byte_offset(),
)
.with_context("unary expression"));
};
match token.kind {
SyntaxKind::PlusPlus | SyntaxKind::MinusMinus => self.parse_prefix_update(),
SyntaxKind::TypeofKw | SyntaxKind::VoidKw | SyntaxKind::DeleteKw => {
self.parse_unary_keyword_expr()
}
SyntaxKind::Exclaim => self.parse_unary_expr(),
SyntaxKind::Text if matches!(token.text.as_str(), "-" | "+" | "~") => {
self.parse_unary_expr()
}
SyntaxKind::AwaitKw => self.parse_await_expr(),
_ => self.parse_primary_expr(),
}
}
fn parse_new_expr(&mut self) -> ParseResult<IrNode> {
let start_byte = self.current_byte_offset();
self.expect(SyntaxKind::NewKw).ok_or_else(|| {
ParseError::new(ParseErrorKind::UnexpectedToken, self.current_byte_offset())
.with_expected(&["new"])
})?;
self.skip_whitespace();
let callee = if self.at(SyntaxKind::NewKw) {
self.parse_new_expr()?
} else {
self.parse_primary_expr()?
};
self.skip_whitespace();
let type_args = self.parse_optional_type_args()?;
let args = if self.at(SyntaxKind::LParen) {
self.parse_call_arguments()?
} else {
Vec::new()
};
Ok(IrNode::NewExpr {
span: IrSpan::new(start_byte, self.current_byte_offset()),
callee: Box::new(callee),
type_args: type_args.map(Box::new),
args,
})
}
fn parse_await_expr(&mut self) -> ParseResult<IrNode> {
let start_byte = self.current_byte_offset();
self.expect(SyntaxKind::AwaitKw).ok_or_else(|| {
ParseError::new(ParseErrorKind::UnexpectedToken, self.current_byte_offset())
.with_expected(&["await"])
})?;
self.skip_whitespace();
let arg = self.parse_unary_operand()?;
Ok(IrNode::AwaitExpr {
span: IrSpan::new(start_byte, self.current_byte_offset()),
arg: Box::new(arg),
})
}
fn parse_yield_expr(&mut self) -> ParseResult<IrNode> {
let start_byte = self.current_byte_offset();
self.expect(SyntaxKind::YieldKw).ok_or_else(|| {
ParseError::new(ParseErrorKind::UnexpectedToken, self.current_byte_offset())
.with_expected(&["yield"])
})?;
self.skip_whitespace();
let delegate = if self.at(SyntaxKind::Star) {
self.consume();
self.skip_whitespace();
true
} else {
false
};
let arg = if self.is_at_expression_start() {
Some(Box::new(self.parse_unary_operand()?))
} else {
None
};
Ok(IrNode::YieldExpr {
span: IrSpan::new(start_byte, self.current_byte_offset()),
delegate,
arg,
})
}
fn parse_function_expr(&mut self) -> ParseResult<IrNode> {
let start_byte = self.current_byte_offset();
self.expect(SyntaxKind::FunctionKw).ok_or_else(|| {
ParseError::new(ParseErrorKind::UnexpectedToken, self.current_byte_offset())
.with_expected(&["function"])
})?;
self.skip_whitespace();
let generator = if self.at(SyntaxKind::Star) {
self.consume();
self.skip_whitespace();
true
} else {
false
};
let name = if self.at(SyntaxKind::Ident) {
let token = self.consume().ok_or_else(|| {
ParseError::unexpected_eof(self.current_byte_offset(), "function name")
})?;
Some(Box::new(IrNode::ident(&token)))
} else {
None
};
self.skip_whitespace();
let type_params = self.parse_optional_type_params();
let params = self.parse_function_params()?;
self.skip_whitespace();
let return_type = self.parse_optional_return_type()?;
self.skip_whitespace();
let body = if self.at(SyntaxKind::LBrace) {
Some(Box::new(self.parse_block_stmt()?))
} else {
None
};
Ok(IrNode::FnExpr {
span: IrSpan::new(start_byte, self.current_byte_offset()),
async_: false,
generator,
name,
type_params,
params,
return_type,
body,
})
}
fn parse_async_expr(&mut self) -> ParseResult<IrNode> {
let start_byte = self.current_byte_offset();
self.expect(SyntaxKind::AsyncKw).ok_or_else(|| {
ParseError::new(ParseErrorKind::UnexpectedToken, self.current_byte_offset())
.with_expected(&["async"])
})?;
self.skip_whitespace();
if self.at(SyntaxKind::FunctionKw) {
self.consume();
self.skip_whitespace();
let generator = if self.at(SyntaxKind::Star) {
self.consume();
self.skip_whitespace();
true
} else {
false
};
let name = if self.at(SyntaxKind::Ident) {
let token = self.consume().ok_or_else(|| {
ParseError::unexpected_eof(self.current_byte_offset(), "async function name")
})?;
Some(Box::new(IrNode::ident(&token)))
} else {
None
};
self.skip_whitespace();
let type_params = self.parse_optional_type_params();
let params = self.parse_function_params()?;
self.skip_whitespace();
let return_type = self.parse_optional_return_type()?;
self.skip_whitespace();
let body = if self.at(SyntaxKind::LBrace) {
Some(Box::new(self.parse_block_stmt()?))
} else {
None
};
Ok(IrNode::FnExpr {
span: IrSpan::new(start_byte, self.current_byte_offset()),
async_: true,
generator,
name,
type_params,
params,
return_type,
body,
})
} else {
self.parse_async_arrow_function_from(start_byte)
}
}
fn parse_async_arrow_function_from(&mut self, start_byte: usize) -> ParseResult<IrNode> {
self.skip_whitespace();
let type_params = self.parse_optional_type_params();
let params = self.parse_arrow_params()?;
self.skip_whitespace();
let return_type = self.parse_optional_return_type()?;
self.skip_whitespace();
if !self.at_text("=>") {
return Err(ParseError::new(
ParseErrorKind::MissingArrowBody,
self.current_byte_offset(),
)
.with_expected(&["=>"]));
}
self.consume();
self.skip_whitespace();
let body = if self.at(SyntaxKind::LBrace) {
self.parse_block_stmt()?
} else {
self.parse_expression_with_precedence(prec::ASSIGN.right)?
};
Ok(IrNode::ArrowExpr {
span: IrSpan::new(start_byte, self.current_byte_offset()),
async_: true,
type_params,
params,
return_type,
body: Box::new(body),
})
}
fn parse_class_expr(&mut self) -> ParseResult<IrNode> {
let start_byte = self.current_byte_offset();
self.expect(SyntaxKind::ClassKw).ok_or_else(|| {
ParseError::new(ParseErrorKind::UnexpectedToken, self.current_byte_offset())
.with_expected(&["class"])
})?;
self.skip_whitespace();
let name = if self.at(SyntaxKind::Ident) {
let token = self.consume().ok_or_else(|| {
ParseError::unexpected_eof(self.current_byte_offset(), "class name")
})?;
Some(Box::new(IrNode::ident(&token)))
} else {
None
};
self.skip_whitespace();
let type_params = self.parse_optional_type_params();
self.skip_whitespace();
let extends = if self.at(SyntaxKind::ExtendsKw) {
self.consume();
self.skip_whitespace();
Some(Box::new(self.parse_primary_expr()?))
} else {
None
};
self.skip_whitespace();
let implements = if self.at(SyntaxKind::ImplementsKw) {
self.consume();
self.skip_whitespace();
self.parse_implements_list()?
} else {
Vec::new()
};
self.skip_whitespace();
let body = self.parse_class_body()?;
Ok(IrNode::ClassExpr {
span: IrSpan::new(start_byte, self.current_byte_offset()),
name,
type_params,
extends,
implements,
body,
})
}
fn parse_paren_or_arrow(&mut self) -> ParseResult<IrNode> {
let start_pos = self.pos;
let start_byte = self.current_byte_offset();
self.expect(SyntaxKind::LParen).ok_or_else(|| {
ParseError::new(ParseErrorKind::UnexpectedToken, self.current_byte_offset())
.with_expected(&["("])
})?;
self.skip_whitespace();
if self.at(SyntaxKind::RParen) {
self.consume();
self.skip_whitespace();
if self.at_text("=>") {
return self.parse_arrow_after_params_from(Vec::new(), start_pos);
}
return Err(ParseError::new(
ParseErrorKind::ExpectedExpression,
self.current_byte_offset(),
)
.with_context("parenthesized expression"));
}
let first = self.parse_expression_with_precedence(0)?;
self.skip_whitespace();
if self.at(SyntaxKind::Colon) {
return self.parse_arrow_params_from_first(first, start_byte);
}
if self.at(SyntaxKind::Comma) {
let mut items = vec![first];
while self.at(SyntaxKind::Comma) {
self.consume();
self.skip_whitespace();
if self.at(SyntaxKind::RParen) {
break; }
let item = self.parse_expression_with_precedence(0)?;
self.skip_whitespace();
if self.at(SyntaxKind::Colon) {
items.push(item);
return self.parse_arrow_params_from_items(items, start_byte);
}
items.push(item);
self.skip_whitespace();
}
if !self.at(SyntaxKind::RParen) {
return Err(ParseError::missing_closing(
ParseErrorKind::MissingClosingParen,
self.current_byte_offset(),
start_pos,
));
}
self.consume();
self.skip_whitespace();
if self.at_text("=>") {
let params = self.exprs_to_params(items)?;
return self.parse_arrow_after_params_from(params, start_byte);
}
let end_byte = self.current_byte_offset();
if items.len() == 1 {
return Ok(IrNode::ParenExpr {
span: IrSpan::new(start_byte, end_byte),
expr: Box::new(items.remove(0)),
});
} else {
return Ok(IrNode::ParenExpr {
span: IrSpan::new(start_byte, end_byte),
expr: Box::new(IrNode::SeqExpr {
span: IrSpan::new(start_byte, end_byte),
exprs: items,
}),
});
}
}
if !self.at(SyntaxKind::RParen) {
return Err(ParseError::missing_closing(
ParseErrorKind::MissingClosingParen,
self.current_byte_offset(),
start_pos,
));
}
self.consume();
self.skip_whitespace();
if self.at_text("=>") {
let params = self.exprs_to_params(vec![first])?;
return self.parse_arrow_after_params_from(params, start_byte);
}
Ok(IrNode::ParenExpr {
span: IrSpan::new(start_byte, self.current_byte_offset()),
expr: Box::new(first),
})
}
fn parse_arrow_after_params_from(
&mut self,
params: Vec<IrNode>,
start_byte: usize,
) -> ParseResult<IrNode> {
self.skip_whitespace();
if !self.at_text("=>") {
return Err(ParseError::new(
ParseErrorKind::MissingArrowBody,
self.current_byte_offset(),
)
.with_expected(&["=>"]));
}
self.consume();
self.skip_whitespace();
let return_type = self.parse_optional_return_type()?;
self.skip_whitespace();
let body = if self.at(SyntaxKind::LBrace) {
self.parse_block_stmt()?
} else {
self.parse_expression_with_precedence(prec::ASSIGN.right)?
};
Ok(IrNode::ArrowExpr {
span: IrSpan::new(start_byte, self.current_byte_offset()),
async_: false,
type_params: None,
params,
return_type,
body: Box::new(body),
})
}
fn parse_arrow_params_from_first(
&mut self,
first: IrNode,
start_byte: usize,
) -> ParseResult<IrNode> {
let first_param = self.expr_to_typed_param(first)?;
let mut params = vec![first_param];
self.skip_whitespace();
while self.at(SyntaxKind::Comma) {
self.consume();
self.skip_whitespace();
if self.at(SyntaxKind::RParen) {
break; }
let param = self.parse_arrow_param()?;
params.push(param);
self.skip_whitespace();
}
if !self.at(SyntaxKind::RParen) {
return Err(ParseError::missing_closing(
ParseErrorKind::MissingClosingParen,
self.current_byte_offset(),
start_byte,
));
}
self.consume();
self.skip_whitespace();
let return_type = self.parse_optional_return_type()?;
self.skip_whitespace();
if !self.at_text("=>") {
return Err(ParseError::new(
ParseErrorKind::MissingArrowBody,
self.current_byte_offset(),
)
.with_expected(&["=>"]));
}
self.consume();
self.skip_whitespace();
let body = if self.at(SyntaxKind::LBrace) {
self.parse_block_stmt()?
} else {
self.parse_expression_with_precedence(prec::ASSIGN.right)?
};
Ok(IrNode::ArrowExpr {
span: IrSpan::new(start_byte, self.current_byte_offset()),
async_: false,
type_params: None,
params,
return_type,
body: Box::new(body),
})
}
fn parse_arrow_params_from_items(
&mut self,
items: Vec<IrNode>,
start_byte: usize,
) -> ParseResult<IrNode> {
let mut params = Vec::new();
let len = items.len();
for (i, item) in items.into_iter().enumerate() {
if i == len - 1 {
let typed_param = self.expr_to_typed_param(item)?;
params.push(typed_param);
} else {
let p = self.exprs_to_params(vec![item])?;
params.extend(p);
}
}
self.skip_whitespace();
while self.at(SyntaxKind::Comma) {
self.consume();
self.skip_whitespace();
if self.at(SyntaxKind::RParen) {
break; }
let param = self.parse_arrow_param()?;
params.push(param);
self.skip_whitespace();
}
if !self.at(SyntaxKind::RParen) {
return Err(ParseError::missing_closing(
ParseErrorKind::MissingClosingParen,
self.current_byte_offset(),
start_byte,
));
}
self.consume();
self.skip_whitespace();
let return_type = self.parse_optional_return_type()?;
self.skip_whitespace();
if !self.at_text("=>") {
return Err(ParseError::new(
ParseErrorKind::MissingArrowBody,
self.current_byte_offset(),
)
.with_expected(&["=>"]));
}
self.consume();
self.skip_whitespace();
let body = if self.at(SyntaxKind::LBrace) {
self.parse_block_stmt()?
} else {
self.parse_expression_with_precedence(prec::ASSIGN.right)?
};
Ok(IrNode::ArrowExpr {
span: IrSpan::new(start_byte, self.current_byte_offset()),
async_: false,
type_params: None,
params,
return_type,
body: Box::new(body),
})
}
fn expr_to_typed_param(&mut self, expr: IrNode) -> ParseResult<IrNode> {
let _span = expr.span();
self.expect(SyntaxKind::Colon).ok_or_else(|| {
ParseError::new(ParseErrorKind::UnexpectedToken, self.current_byte_offset())
})?;
self.skip_whitespace();
self.push_context(Context::type_annotation([
SyntaxKind::Comma,
SyntaxKind::RParen,
SyntaxKind::Eq,
]));
let type_ann = self.parse_type()?;
self.pop_context();
self.skip_whitespace();
let _default_value = if self.at(SyntaxKind::Eq) {
self.consume();
self.skip_whitespace();
Some(Box::new(
self.parse_expression_with_precedence(prec::ASSIGN.right)?,
))
} else {
None
};
let binding = match expr {
IrNode::Ident { span, value } => IrNode::BindingIdent {
span,
name: Box::new(IrNode::Ident { span, value }),
type_ann: Some(Box::new(type_ann)),
optional: false,
},
IrNode::Placeholder { span, kind, expr } => IrNode::BindingIdent {
span,
name: Box::new(IrNode::Placeholder { span, kind, expr }),
type_ann: Some(Box::new(type_ann)),
optional: false,
},
other => {
IrNode::BindingIdent {
span: other.span(),
name: Box::new(other),
type_ann: Some(Box::new(type_ann)),
optional: false,
}
}
};
Ok(binding)
}
fn exprs_to_params(&self, exprs: Vec<IrNode>) -> ParseResult<Vec<IrNode>> {
let mut params = Vec::new();
for expr in exprs {
let param = match expr {
IrNode::Ident { span, value } => IrNode::BindingIdent {
span,
name: Box::new(IrNode::Ident { span, value }),
type_ann: None,
optional: false,
},
IrNode::Placeholder { span, kind, expr } => {
IrNode::Placeholder { span, kind, expr }
}
other => other,
};
params.push(param);
}
Ok(params)
}
fn parse_arrow_param(&mut self) -> ParseResult<IrNode> {
let span_start = self.current_byte_offset();
let name = self.parse_ts_ident_or_placeholder().ok_or_else(|| {
ParseError::unexpected_eof(self.current_byte_offset(), "arrow parameter name")
})?;
self.skip_whitespace();
let optional = if self.at(SyntaxKind::Question) {
self.consume();
self.skip_whitespace();
true
} else {
false
};
let type_ann = if self.at(SyntaxKind::Colon) {
self.consume();
self.skip_whitespace();
self.push_context(Context::type_annotation([
SyntaxKind::Comma,
SyntaxKind::RParen,
SyntaxKind::Eq,
]));
let ty = self.parse_type()?;
self.pop_context();
self.skip_whitespace();
Some(Box::new(ty))
} else {
None
};
if self.at(SyntaxKind::Eq) {
self.consume();
self.skip_whitespace();
let default_value = self.parse_expression_with_precedence(prec::ASSIGN.right)?;
let span = IrSpan::new(span_start, self.current_byte_offset());
let binding = IrNode::BindingIdent {
span: name.span(),
name: Box::new(name),
type_ann,
optional,
};
Ok(IrNode::AssignPat {
span,
left: Box::new(binding),
right: Box::new(default_value),
})
} else {
Ok(IrNode::BindingIdent {
span: name.span(),
name: Box::new(name),
type_ann,
optional,
})
}
}
fn parse_array_literal(&mut self) -> ParseResult<IrNode> {
let start_pos = self.pos;
let start_byte = self.current_byte_offset();
self.expect(SyntaxKind::LBracket).ok_or_else(|| {
ParseError::new(ParseErrorKind::UnexpectedToken, self.current_byte_offset())
.with_expected(&["["])
})?;
let mut elems = Vec::new();
loop {
self.skip_whitespace();
if self.at(SyntaxKind::RBracket) {
break;
}
if self.at(SyntaxKind::DotDotDot) {
let spread_start = self.current_byte_offset();
self.consume();
self.skip_whitespace();
let expr = self.parse_expression_with_precedence(prec::ASSIGN.right)?;
elems.push(IrNode::SpreadElement {
span: IrSpan::new(spread_start, self.current_byte_offset()),
expr: Box::new(expr),
});
} else if self.at(SyntaxKind::Comma) {
} else {
let expr = self.parse_expression_with_precedence(prec::ASSIGN.right)?;
elems.push(expr);
}
self.skip_whitespace();
if self.at(SyntaxKind::Comma) {
self.consume();
} else {
break;
}
}
if !self.at(SyntaxKind::RBracket) {
return Err(ParseError::missing_closing(
ParseErrorKind::MissingClosingBracket,
self.current_byte_offset(),
start_pos,
));
}
self.consume();
Ok(IrNode::ArrayLit {
span: IrSpan::new(start_byte, self.current_byte_offset()),
elems,
})
}
fn parse_object_literal(&mut self) -> ParseResult<IrNode> {
let start_pos = self.pos;
let start_byte = self.current_byte_offset();
self.expect(SyntaxKind::LBrace).ok_or_else(|| {
ParseError::new(ParseErrorKind::UnexpectedToken, self.current_byte_offset())
.with_expected(&["{"])
})?;
let mut props = Vec::new();
loop {
self.skip_whitespace();
if self.at(SyntaxKind::RBrace) {
break;
}
let prop = self.parse_object_property()?;
props.push(prop);
self.skip_whitespace();
if self.at(SyntaxKind::Comma) {
self.consume();
} else {
break;
}
}
if !self.at(SyntaxKind::RBrace) {
return Err(ParseError::missing_closing(
ParseErrorKind::MissingClosingBrace,
self.current_byte_offset(),
start_pos,
));
}
self.consume();
Ok(IrNode::ObjectLit {
span: IrSpan::new(start_byte, self.current_byte_offset()),
props,
})
}
fn parse_object_property(&mut self) -> ParseResult<IrNode> {
self.skip_whitespace();
let start_byte = self.current_byte_offset();
match self.current_kind() {
Some(SyntaxKind::BraceHashIf) => return self.parse_if_expr(),
Some(SyntaxKind::BraceHashFor) => return self.parse_for_expr(),
Some(SyntaxKind::BraceHashWhile) => return self.parse_while_expr(),
Some(SyntaxKind::BraceHashMatch) => return self.parse_match_expr(),
_ => {}
}
if self.at(SyntaxKind::DotDotDot) {
self.consume();
self.skip_whitespace();
let expr = self.parse_expression_with_precedence(prec::ASSIGN.right)?;
return Ok(IrNode::SpreadElement {
span: IrSpan::new(start_byte, self.current_byte_offset()),
expr: Box::new(expr),
});
}
if self.at(SyntaxKind::At) {
let placeholder = self.parse_interpolation()?;
self.skip_whitespace();
if self.at(SyntaxKind::LParen) || self.at(SyntaxKind::Lt) {
return self.parse_method_prop_from(placeholder, false, false, start_byte);
}
if self.at(SyntaxKind::Colon) {
self.consume();
self.skip_whitespace();
let value = self.parse_expression_with_precedence(prec::ASSIGN.right)?;
return Ok(IrNode::KeyValueProp {
span: IrSpan::new(start_byte, self.current_byte_offset()),
key: Box::new(placeholder),
value: Box::new(value),
});
} else {
return Ok(IrNode::ShorthandProp {
span: IrSpan::new(start_byte, self.current_byte_offset()),
key: Box::new(placeholder),
});
}
}
if self.at(SyntaxKind::GetKw) || self.at(SyntaxKind::SetKw) {
return self.parse_getter_setter_prop_from(start_byte);
}
if self.at(SyntaxKind::AsyncKw) {
return self.parse_async_method_prop_from(start_byte);
}
if self.at(SyntaxKind::Star) {
return self.parse_generator_method_prop_from(start_byte);
}
if self.at(SyntaxKind::LBracket) {
return self.parse_computed_property_from(start_byte);
}
let key = self.parse_property_name()?;
self.skip_whitespace();
if self.at(SyntaxKind::LParen) || self.at(SyntaxKind::Lt) {
return self.parse_method_prop_from(key, false, false, start_byte);
}
if self.at(SyntaxKind::Colon) {
self.consume();
self.skip_whitespace();
let value = self.parse_expression_with_precedence(prec::ASSIGN.right)?;
return Ok(IrNode::KeyValueProp {
span: IrSpan::new(start_byte, self.current_byte_offset()),
key: Box::new(key),
value: Box::new(value),
});
}
Ok(IrNode::ShorthandProp {
span: IrSpan::new(start_byte, self.current_byte_offset()),
key: Box::new(key),
})
}
pub(in crate::compiler::parser) fn parse_property_name(&mut self) -> ParseResult<IrNode> {
self.skip_whitespace();
let Some(token) = self.current() else {
return Err(ParseError::unexpected_eof(
self.current_byte_offset(),
"property name",
));
};
match token.kind {
SyntaxKind::Ident => {
let t = self.consume().ok_or_else(|| {
ParseError::unexpected_eof(
self.current_byte_offset(),
"property name identifier",
)
})?;
Ok(IrNode::ident(&t))
}
SyntaxKind::DoubleQuote | SyntaxKind::SingleQuote => self.parse_string_literal(),
SyntaxKind::Text
if token
.text
.chars()
.next()
.map(|c| c.is_ascii_digit())
.unwrap_or(false) =>
{
self.parse_numeric_literal()
}
SyntaxKind::At => {
self.parse_interpolation()
}
SyntaxKind::LBracket => {
self.consume();
self.skip_whitespace();
let expr = self.parse_expression_with_precedence(0)?;
self.skip_whitespace();
self.expect(SyntaxKind::RBracket).ok_or_else(|| {
ParseError::new(
ParseErrorKind::MissingClosingBracket,
self.current_byte_offset(),
)
.with_context("computed property name")
})?;
Ok(IrNode::ComputedPropName {
span: IrSpan::empty(),
expr: Box::new(expr),
})
}
_ if token.kind.is_ts_keyword() => {
let t = self.consume().ok_or_else(|| {
ParseError::unexpected_eof(self.current_byte_offset(), "property name keyword")
})?;
Ok(IrNode::ident(&t))
}
_ => Err(ParseError::new(
ParseErrorKind::InvalidPropertyName,
self.current_byte_offset(),
)
.with_found(&token.text)),
}
}
fn parse_getter_setter_prop_from(&mut self, start_byte: usize) -> ParseResult<IrNode> {
let is_getter = self.at(SyntaxKind::GetKw);
self.consume();
self.skip_whitespace();
let name = self.parse_property_name()?;
self.skip_whitespace();
if is_getter {
self.expect(SyntaxKind::LParen);
self.skip_whitespace();
self.expect(SyntaxKind::RParen);
self.skip_whitespace();
let type_ann = self.parse_optional_return_type()?;
self.skip_whitespace();
let body = self.parse_block_stmt()?;
Ok(IrNode::GetterProp {
span: IrSpan::new(start_byte, self.current_byte_offset()),
name: Box::new(name),
type_ann,
body: Box::new(body),
})
} else {
self.expect(SyntaxKind::LParen);
self.skip_whitespace();
let param = self.parse_single_param()?;
self.skip_whitespace();
self.expect(SyntaxKind::RParen);
self.skip_whitespace();
let body = self.parse_block_stmt()?;
Ok(IrNode::SetterProp {
span: IrSpan::new(start_byte, self.current_byte_offset()),
name: Box::new(name),
param: Box::new(param),
body: Box::new(body),
})
}
}
fn parse_async_method_prop_from(&mut self, start_byte: usize) -> ParseResult<IrNode> {
self.consume(); self.skip_whitespace();
let generator = if self.at(SyntaxKind::Star) {
self.consume();
self.skip_whitespace();
true
} else {
false
};
let name = self.parse_property_name()?;
self.parse_method_prop_from(name, true, generator, start_byte)
}
fn parse_generator_method_prop_from(&mut self, start_byte: usize) -> ParseResult<IrNode> {
self.consume(); self.skip_whitespace();
let name = self.parse_property_name()?;
self.parse_method_prop_from(name, false, true, start_byte)
}
fn parse_computed_property_from(&mut self, start_byte: usize) -> ParseResult<IrNode> {
let start_pos = self.pos;
self.expect(SyntaxKind::LBracket);
self.skip_whitespace();
let expr = self.parse_expression_with_precedence(0)?;
self.skip_whitespace();
if !self.at(SyntaxKind::RBracket) {
return Err(ParseError::missing_closing(
ParseErrorKind::MissingClosingBracket,
self.current_byte_offset(),
start_pos,
));
}
self.consume();
self.skip_whitespace();
let key = IrNode::ComputedPropName {
span: IrSpan::new(start_byte, self.current_byte_offset()),
expr: Box::new(expr),
};
if self.at(SyntaxKind::LParen) || self.at(SyntaxKind::Lt) {
return self.parse_method_prop_from(key, false, false, start_byte);
}
if self.at(SyntaxKind::Colon) {
self.consume();
self.skip_whitespace();
let value = self.parse_expression_with_precedence(prec::ASSIGN.right)?;
return Ok(IrNode::KeyValueProp {
span: IrSpan::new(start_byte, self.current_byte_offset()),
key: Box::new(key),
value: Box::new(value),
});
}
Err(ParseError::new(
ParseErrorKind::InvalidComputedProperty,
self.current_byte_offset(),
))
}
fn parse_method_prop_from(
&mut self,
name: IrNode,
async_: bool,
generator: bool,
start_byte: usize,
) -> ParseResult<IrNode> {
let type_params = self.parse_optional_type_params();
let params = self.parse_function_params()?;
self.skip_whitespace();
let return_type = self.parse_optional_return_type()?;
self.skip_whitespace();
let body = self.parse_block_stmt()?;
Ok(IrNode::MethodProp {
span: IrSpan::new(start_byte, self.current_byte_offset()),
async_,
generator,
name: Box::new(name),
type_params,
params,
return_type,
body: Box::new(body),
})
}
pub(in crate::compiler::parser) fn parse_template_literal(&mut self) -> ParseResult<IrNode> {
let start_pos = self.pos;
let start_offset = self.current_byte_offset();
self.expect(SyntaxKind::Backtick).ok_or_else(|| {
ParseError::new(ParseErrorKind::UnexpectedToken, self.current_byte_offset())
.with_expected(&["`"])
})?;
let mut quasis = Vec::new();
let mut exprs = Vec::new();
let mut current_text = String::new();
loop {
if self.at_eof() {
return Err(ParseError::new(
ParseErrorKind::UnterminatedTemplateLiteral,
start_pos,
));
}
if self.at(SyntaxKind::Backtick) {
quasis.push(current_text);
self.consume();
break;
}
if self.at(SyntaxKind::DollarBrace) {
quasis.push(std::mem::take(&mut current_text));
self.consume();
self.skip_whitespace();
let expr = self.parse_expression_with_precedence(0)?;
exprs.push(expr);
self.skip_whitespace();
if !self.at(SyntaxKind::RBrace) {
return Err(ParseError::missing_closing(
ParseErrorKind::MissingClosingBrace,
self.current_byte_offset(),
start_pos,
));
}
self.consume(); } else if self.at(SyntaxKind::At) {
quasis.push(std::mem::take(&mut current_text));
let placeholder = self.parse_interpolation()?;
exprs.push(placeholder);
} else {
if let Some(token) = self.consume() {
current_text.push_str(&token.text);
}
}
}
let end_offset = self.current_byte_offset();
Ok(IrNode::TplLit {
span: IrSpan::new(start_offset, end_offset),
quasis,
exprs,
})
}
pub(in crate::compiler::parser) fn parse_string_literal(&mut self) -> ParseResult<IrNode> {
let start_pos = self.pos;
let start_offset = self.current_byte_offset();
let quote_kind = self.current_kind().ok_or_else(|| {
ParseError::unexpected_eof(self.current_byte_offset(), "string literal")
})?;
let quote_char = if quote_kind == SyntaxKind::DoubleQuote {
'"'
} else {
'\''
};
self.consume();
let mut content = String::new();
let mut parts: Vec<IrNode> = Vec::new();
let mut has_interpolation = false;
loop {
if self.at_eof() {
return Err(ParseError::new(
ParseErrorKind::UnterminatedStringLiteral,
start_pos,
));
}
if self.at(quote_kind) {
if has_interpolation {
if !content.is_empty() {
parts.push(IrNode::StrLit {
span: IrSpan::empty(),
value: std::mem::take(&mut content),
});
}
}
self.consume();
break;
}
if self.at(SyntaxKind::At) {
has_interpolation = true;
if !content.is_empty() {
parts.push(IrNode::StrLit {
span: IrSpan::empty(),
value: std::mem::take(&mut content),
});
}
let placeholder = self.parse_interpolation()?;
parts.push(placeholder);
} else {
if let Some(token) = self.consume() {
content.push_str(&token.text);
}
}
}
let end_offset = self.current_byte_offset();
let span = IrSpan::new(start_offset, end_offset);
if has_interpolation {
if !content.is_empty() {
parts.push(IrNode::StrLit {
span: IrSpan::empty(),
value: content,
});
}
Ok(IrNode::StringInterp {
span,
quote: quote_char,
parts,
})
} else {
Ok(IrNode::StrLit {
span,
value: content,
})
}
}
pub(in crate::compiler::parser) fn parse_numeric_literal(&mut self) -> ParseResult<IrNode> {
let token = self.consume().ok_or_else(|| {
ParseError::unexpected_eof(self.current_byte_offset(), "numeric literal")
})?;
let text = &token.text;
if text.ends_with('n') {
Ok(IrNode::bigint_lit(&token))
} else {
Ok(IrNode::num_lit(&token))
}
}
pub(in crate::compiler::parser) fn parse_private_name(&mut self) -> ParseResult<IrNode> {
self.expect(SyntaxKind::Hash).ok_or_else(|| {
ParseError::new(ParseErrorKind::UnexpectedToken, self.current_byte_offset())
.with_expected(&["#"])
})?;
let name_token = self.expect(SyntaxKind::Ident).ok_or_else(|| {
ParseError::new(
ParseErrorKind::ExpectedIdentifier,
self.current_byte_offset(),
)
.with_context("private name")
})?;
Ok(IrNode::private_name(&name_token))
}
fn parse_identifier_expr(&mut self) -> ParseResult<IrNode> {
let token = self
.consume()
.ok_or_else(|| ParseError::unexpected_eof(self.current_byte_offset(), "identifier"))?;
Ok(IrNode::ident(&token))
}
fn is_at_expression_start(&self) -> bool {
let Some(token) = self.current() else {
return false;
};
matches!(
token.kind,
SyntaxKind::Ident
| SyntaxKind::Text
| SyntaxKind::At
| SyntaxKind::LParen
| SyntaxKind::LBracket
| SyntaxKind::LBrace
| SyntaxKind::Backtick
| SyntaxKind::DoubleQuote
| SyntaxKind::SingleQuote
| SyntaxKind::PlusPlus
| SyntaxKind::MinusMinus
| SyntaxKind::Exclaim
| SyntaxKind::Hash
| SyntaxKind::NewKw
| SyntaxKind::FunctionKw
| SyntaxKind::ClassKw
| SyntaxKind::AsyncKw
| SyntaxKind::AwaitKw
| SyntaxKind::YieldKw
| SyntaxKind::TypeofKw
| SyntaxKind::VoidKw
| SyntaxKind::DeleteKw
| SyntaxKind::ThisKw
| SyntaxKind::SuperKw
| SyntaxKind::NullKw
| SyntaxKind::TrueKw
| SyntaxKind::FalseKw
| SyntaxKind::BraceHashIf
| SyntaxKind::BraceHashFor
| SyntaxKind::BraceHashWhile
| SyntaxKind::BraceHashMatch
) || matches!(token.text.as_str(), "-" | "+" | "~")
}
pub(in crate::compiler::parser) fn at_text(&self, text: &str) -> bool {
self.current()
.map(|t| t.text.as_str() == text)
.unwrap_or(false)
}
pub(in crate::compiler::parser) fn current_text(&self) -> Option<&str> {
self.current().map(|t| t.text.as_str())
}
}
#[cfg(test)]
mod tests {
}