use super::{pat::PatType, util::ExprExt, *};
use swc_common::Spanned;
mod ops;
#[cfg(test)]
mod tests;
mod verifier;
#[parser]
impl<'a, I: Input> Parser<'a, I> {
pub fn parse_expr(&mut self) -> PResult<'a, (Box<Expr>)> {
let expr = self.parse_assignment_expr()?;
let start = expr.span().lo();
if is!(',') {
let mut exprs = vec![expr];
while eat!(',') {
exprs.push(self.parse_assignment_expr()?);
}
let end = exprs.last().unwrap().span().hi();
return Ok(box Expr::Seq(SeqExpr {
span: span!(start),
exprs,
}));
}
Ok(expr)
}
pub(super) fn parse_assignment_expr(&mut self) -> PResult<'a, (Box<Expr>)> {
if self.ctx().in_generator && is!("yield") {
return self.parse_yield_expr();
}
self.state.potential_arrow_start = match *cur!(true)? {
Word(Ident(..)) | tok!('(') | tok!("yield") => Some(cur_pos!()),
_ => None,
};
let start = cur_pos!();
let cond = self.parse_cond_expr()?;
return_if_arrow!(cond);
match *cond {
Expr::Cond(..) | Expr::Bin(..) | Expr::Unary(..) | Expr::Update(..) => return Ok(cond),
_ => {}
}
match cur!(false) {
Ok(&AssignOp(op)) => {
let left = if op == Assign {
self.reparse_expr_as_pat(PatType::AssignPat, cond)
.map(Box::new)
.map(PatOrExpr::Pat)?
} else {
if !cond.is_valid_simple_assignment_target(self.ctx().strict) {
syntax_error!(cond.span(), SyntaxError::NotSimpleAssign)
}
PatOrExpr::Expr(cond)
};
bump!();
let right = self.parse_assignment_expr()?;
Ok(box Expr::Assign(AssignExpr {
span: span!(start),
op,
left,
right,
}))
}
_ => Ok(cond),
}
}
fn parse_cond_expr(&mut self) -> PResult<'a, (Box<Expr>)> {
let start = cur_pos!();
let test = self.parse_bin_expr()?;
return_if_arrow!(test);
if eat!('?') {
let cons = self.include_in_expr(true).parse_assignment_expr()?;
expect!(':');
let alt = self.parse_assignment_expr()?;
Ok(box Expr::Cond(CondExpr {
test,
cons,
alt,
span: span!(start),
}))
} else {
return Ok(test);
}
}
fn parse_primary_expr(&mut self) -> PResult<'a, (Box<Expr>)> {
let _ = cur!(false);
let start = cur_pos!();
let can_be_arrow = self
.state
.potential_arrow_start
.map(|s| s == cur_pos!())
.unwrap_or(false);
if eat!("this") {
return Ok(box Expr::This(ThisExpr { span: span!(start) }));
}
if is!("async") {
if peeked_is!("function") && !self.input.has_linebreak_between_cur_and_peeked() {
return self.parse_async_fn_expr();
}
if can_be_arrow && peeked_is!('(') {
let start = cur_pos!();
expect!("async");
let async_span = span!(start);
return self.parse_paren_expr_or_arrow_fn(can_be_arrow, Some(async_span));
}
}
if is!('[') {
return self.parse_array_lit();
}
if is!('{') {
return self.parse_object();
}
if is!("function") {
return self.parse_fn_expr();
}
if is!("class") {
return self.parse_class_expr();
}
if {
match *cur!(false)? {
tok!("null") | tok!("true") | tok!("false") | Num(..) | Token::Str { .. } => true,
_ => false,
}
} {
return Ok(box Expr::Lit(self.parse_lit()?));
}
if {
match *cur!(false)? {
Regex(..) => true,
_ => false,
}
} {
match bump!() {
Regex(exp, flags) => {
return Ok(box Expr::Lit(Lit::Regex(Regex {
span: span!(start),
exp,
flags,
})));
}
_ => unreachable!(),
}
}
if is!('`') {
return Ok(box Expr::Tpl(self.parse_tpl_lit(None)?));
}
if is!('(') {
return self.parse_paren_expr_or_arrow_fn(can_be_arrow, None);
}
if is!("let") || is!(IdentRef) {
let id = self.parse_ident_ref()?;
if can_be_arrow && id.sym == js_word!("async") && is!(BindingIdent) {
let arg = self.parse_binding_ident().map(Pat::from)?;
let params = vec![arg];
expect!("=>");
let body = self.parse_fn_body(true, false)?;
return Ok(box Expr::Arrow(ArrowExpr {
span: span!(start),
body,
params,
is_async: true,
is_generator: false,
}));
} else if can_be_arrow && !self.input.had_line_break_before_cur() && eat!("=>") {
let params = vec![id.into()];
let body = self.parse_fn_body(false, false)?;
return Ok(box Expr::Arrow(ArrowExpr {
span: span!(start),
body,
params,
is_async: false,
is_generator: false,
}));
} else {
return Ok(box Expr::Ident(id));
}
}
unexpected!()
}
fn parse_array_lit(&mut self) -> PResult<'a, (Box<Expr>)> {
let start = cur_pos!();
assert_and_bump!('[');
let mut elems = vec![];
while !eof!() && !is!(']') {
if is!(',') {
expect!(',');
elems.push(None);
continue;
}
elems.push(
self.include_in_expr(true)
.parse_expr_or_spread()
.map(Some)?,
);
if is!(',') {
expect!(',');
}
}
expect!(']');
let span = span!(start);
Ok(box Expr::Array(ArrayLit { span, elems }))
}
fn parse_member_expr(&mut self) -> PResult<'a, (Box<Expr>)> {
self.parse_member_expr_or_new_expr(false)
}
fn parse_member_expr_or_new_expr(&mut self, is_new_expr: bool) -> PResult<'a, (Box<Expr>)> {
let start = cur_pos!();
if eat!("new") {
let span_of_new = span!(start);
if eat!('.') {
let start_of_target = cur_pos!();
if eat!("target") {
return Ok(box Expr::MetaProp(MetaPropExpr {
meta: Ident {
span: span_of_new,
sym: js_word!("new"),
},
prop: Ident {
span: span!(start_of_target),
sym: js_word!("target"),
},
}));
}
unexpected!()
}
let callee = self.parse_member_expr_or_new_expr(is_new_expr)?;
return_if_arrow!(callee);
if !is_new_expr || is!('(') {
let args = self.parse_args().map(Some)?;
let new_expr = ExprOrSuper::Expr(box Expr::New(NewExpr {
span: span!(start),
callee,
args,
}));
return self.parse_subscripts(new_expr, true);
}
return Ok(box Expr::New(NewExpr {
span: span!(start),
callee,
args: None,
}));
}
if eat!("super") {
let base = ExprOrSuper::Super(span!(start));
return self.parse_subscripts(base, true);
}
let obj = self.parse_primary_expr()?;
return_if_arrow!(obj);
self.parse_subscripts(ExprOrSuper::Expr(obj), true)
}
fn parse_new_expr(&mut self) -> PResult<'a, (Box<Expr>)> {
self.parse_member_expr_or_new_expr(true)
}
pub(super) fn parse_args(&mut self) -> PResult<'a, (Vec<ExprOrSpread>)> {
expect!('(');
let mut first = true;
let mut expr_or_spreads = vec![];
while !eof!() && !is!(')') {
if first {
first = false;
} else {
expect!(',');
if is!(')') {
break;
}
}
expr_or_spreads.push(self.include_in_expr(true).parse_expr_or_spread()?);
}
expect!(')');
Ok(expr_or_spreads)
}
pub(super) fn parse_expr_or_spread(&mut self) -> PResult<'a, ExprOrSpread> {
let start = cur_pos!();
if eat!("...") {
let spread = Some(span!(start));
self.include_in_expr(true)
.parse_assignment_expr()
.map(|expr| ExprOrSpread { spread, expr })
} else {
self.parse_assignment_expr()
.map(|expr| ExprOrSpread { spread: None, expr })
}
}
fn parse_paren_expr_or_arrow_fn(
&mut self,
can_be_arrow: bool,
async_span: Option<Span>,
) -> PResult<'a, (Box<Expr>)> {
let start = cur_pos!();
let expr_or_spreads = self.include_in_expr(true).parse_args_or_pats()?;
if is!("=>") {
if self.input.had_line_break_before_cur() {
syntax_error!(span!(start), SyntaxError::LineBreakBeforeArrow);
}
if !can_be_arrow {
unexpected!();
}
assert_and_bump!("=>");
let params = self.parse_exprs_as_params(expr_or_spreads)?;
let body: BlockStmtOrExpr = self.parse_fn_body(async_span.is_some(), false)?;
return Ok(box Expr::Arrow(ArrowExpr {
span: span!(start),
is_async: async_span.is_some(),
is_generator: false,
params,
body,
}));
}
if let Some(async_span) = async_span {
return Ok(box Expr::Call(CallExpr {
span: span!(start).with_lo(async_span.lo()),
callee: ExprOrSuper::Expr(box Expr::Ident(Ident::new("async".into(), async_span))),
args: expr_or_spreads,
}));
}
if expr_or_spreads.len() == 0 {
syntax_error!(
Span::new(start, last_pos!(), Default::default()),
SyntaxError::EmptyParenExpr
);
}
if expr_or_spreads.len() == 1 {
let expr = match expr_or_spreads.into_iter().next().unwrap() {
ExprOrSpread {
spread: Some(..),
ref expr,
} => syntax_error!(expr.span(), SyntaxError::SpreadInParenExpr),
ExprOrSpread { expr, .. } => expr,
};
return Ok(box Expr::Paren(ParenExpr {
span: span!(start),
expr,
}));
} else {
assert!(expr_or_spreads.len() >= 2);
let mut exprs = Vec::with_capacity(expr_or_spreads.len());
for expr in expr_or_spreads {
match expr {
ExprOrSpread {
spread: Some(..),
ref expr,
} => syntax_error!(expr.span(), SyntaxError::SpreadInParenExpr),
ExprOrSpread { expr, .. } => exprs.push(expr),
}
}
assert!(exprs.len() >= 2);
let seq_expr = box Expr::Seq(SeqExpr {
span: Span::new(
exprs.first().unwrap().span().lo(),
exprs.last().unwrap().span().hi(),
Default::default(),
),
exprs,
});
return Ok(box Expr::Paren(ParenExpr {
span: span!(start),
expr: seq_expr,
}));
}
}
fn parse_tpl_lit(&mut self, tag: Option<(Box<Expr>)>) -> PResult<'a, TplLit> {
let start = cur_pos!();
assert_and_bump!('`');
let is_tagged = tag.is_some();
let mut exprs = vec![];
let cur_elem = self.parse_tpl_element(is_tagged)?;
let mut is_tail = cur_elem.tail;
let mut quasis = vec![cur_elem];
while !is_tail {
expect!("${");
exprs.push(self.include_in_expr(true).parse_expr()?);
expect!('}');
let elem = self.parse_tpl_element(is_tagged)?;
is_tail = elem.tail;
quasis.push(elem);
}
expect!('`');
let span = span!(start);
Ok(TplLit {
span,
tag,
exprs,
quasis,
})
}
fn parse_tpl_element(&mut self, is_tagged: bool) -> PResult<'a, TplElement> {
let start = cur_pos!();
let (raw, cooked) = match *cur!(true)? {
Template { .. } => match bump!() {
Template {
raw,
cooked,
has_escape,
} => (
Str {
span: span!(start),
value: raw,
has_escape,
},
Some(Str {
span: span!(start),
value: cooked,
has_escape,
}),
),
_ => unreachable!(),
},
_ => unexpected!(),
};
let tail = is!('`');
Ok(TplElement {
span: span!(start),
raw,
tail,
cooked,
})
}
fn parse_subscripts(
&mut self,
mut obj: ExprOrSuper,
no_call: bool,
) -> PResult<'a, (Box<Expr>)> {
loop {
obj = match self.parse_subscript(obj, no_call)? {
(expr, false) => return Ok(expr),
(expr, true) => ExprOrSuper::Expr(expr),
}
}
}
fn parse_subscript(
&mut self,
obj: ExprOrSuper,
no_call: bool,
) -> PResult<'a, (Box<Expr>, bool)> {
let _ = cur!(false);
let start = cur_pos!();
if eat!('.') {
let prop: Box<Expr> = box self.parse_ident_name().map(Expr::from)?;
return Ok((
box Expr::Member(MemberExpr {
span: span!(start),
obj,
prop,
computed: false,
}),
true,
));
}
if eat!('[') {
let prop = self.include_in_expr(true).parse_expr()?;
expect!(']');
return Ok((
box Expr::Member(MemberExpr {
span: span!(start),
obj,
prop,
computed: true,
}),
true,
));
}
if !no_call && is!('(') {
let args = self.parse_args()?;
return Ok((
box Expr::Call(CallExpr {
span: span!(start),
callee: obj,
args,
}),
true,
));
}
match obj {
ExprOrSuper::Expr(expr) => {
if is!('`') {
let tpl = self.parse_tpl_lit(Some(expr))?;
return Ok((box Expr::Tpl(tpl), true));
}
Ok((expr, false))
}
ExprOrSuper::Super(..) => {
if no_call {
unexpected!()
}
unexpected!()
}
}
}
pub(super) fn parse_lhs_expr(&mut self) -> PResult<'a, (Box<Expr>)> {
let start = cur_pos!();
if eat!("super") {
let obj = ExprOrSuper::Super(span!(start));
return self.parse_subscripts(obj, false);
}
let callee = self.parse_new_expr()?;
return_if_arrow!(callee);
match *callee {
Expr::New(NewExpr { args: None, .. }) => {
assert_ne!(
cur!(false).ok(),
Some(&LParen),
"parse_new_expr() should eat paren if it exists"
);
return Ok(callee);
}
_ => {}
}
if is!('(') {
let args = self.parse_args()?;
let call_expr = box Expr::Call(CallExpr {
span: span!(start),
callee: ExprOrSuper::Expr(callee),
args,
});
return self.parse_subscripts(ExprOrSuper::Expr(call_expr), false);
}
Ok(callee)
}
pub(super) fn parse_expr_or_pat(&mut self) -> PResult<'a, (Box<Expr>)> {
self.parse_expr()
}
pub(super) fn parse_args_or_pats(&mut self) -> PResult<'a, (Vec<ExprOrSpread>)> {
self.parse_args()
}
}
#[parser]
impl<'a, I: Input> Parser<'a, I> {
fn parse_yield_expr(&mut self) -> PResult<'a, (Box<Expr>)> {
let start = cur_pos!();
assert_and_bump!("yield");
assert!(self.ctx().in_generator);
if self.ctx().in_parameters {
syntax_error!(self.input.prev_span(), SyntaxError::YieldParamInGen)
}
if is!(';') || (!is!('*') && !cur!(false).map(Token::starts_expr).unwrap_or(true)) {
Ok(box Expr::Yield(YieldExpr {
span: span!(start),
arg: None,
delegate: false,
}))
} else {
let has_star = eat!('*');
let arg = self.parse_assignment_expr()?;
Ok(box Expr::Yield(YieldExpr {
span: span!(start),
arg: Some(arg),
delegate: has_star,
}))
}
}
fn parse_lit(&mut self) -> PResult<'a, Lit> {
let start = cur_pos!();
let v = match *cur!(true)? {
Word(Null) => {
bump!();
let span = span!(start);
Lit::Null(Null { span })
}
Word(True) | Word(False) => {
let value = is!("true");
bump!();
let span = span!(start);
Lit::Bool(Bool { span, value })
}
Token::Str { .. } => match bump!() {
Token::Str { value, has_escape } => Lit::Str(Str {
span: span!(start),
value,
has_escape,
}),
_ => unreachable!(),
},
Num(..) => match bump!() {
Num(value) => Lit::Num(Number {
span: span!(start),
value,
}),
_ => unreachable!(),
},
_ => unreachable!("parse_lit should not be called"),
};
Ok(v)
}
}