use super::{util::ExprExt, *};
use swc_common::Spanned;
#[parser]
impl<'a, I: Input> Parser<'a, I> {
pub(super) fn parse_bin_expr(&mut self) -> PResult<'a, (Box<Expr>)> {
let left = self.parse_unary_expr()?;
return_if_arrow!(left);
self.parse_bin_op_recursively(left, 0)
}
fn parse_bin_op_recursively(
&mut self,
left: Box<Expr>,
min_prec: u8,
) -> PResult<'a, (Box<Expr>)> {
let op = match {
match cur!(false) {
Ok(cur) => cur,
Err(..) => return Ok(left),
}
} {
&Word(Keyword(In)) if self.ctx().include_in_expr => op!("in"),
&Word(Keyword(InstanceOf)) => op!("instanceof"),
&BinOp(op) => op.into(),
_ => {
return Ok(left);
}
};
if op.precedence() <= min_prec {
trace!(
"returning {:?} without parsing {:?} because min_prec={}, prec={}",
left,
op,
min_prec,
op.precedence()
);
return Ok(left);
}
bump!();
trace!(
"parsing binary op {:?} min_prec={}, prec={}",
op,
min_prec,
op.precedence()
);
match *left {
Expr::Unary { .. } if op == op!("**") => {
syntax_error!(SyntaxError::UnaryInExp {
left: format!("{:?}", left),
left_span: left.span(),
})
}
_ => {}
}
let right = {
let left_of_right = self.parse_unary_expr()?;
self.parse_bin_op_recursively(
left_of_right,
if op == op!("**") {
op.precedence() - 1
} else {
op.precedence()
},
)?
};
let node = box Expr::Bin(BinExpr {
span: span!(left.span().lo()),
op,
left,
right,
});
let expr = self.parse_bin_op_recursively(node, min_prec)?;
Ok(expr)
}
fn parse_unary_expr(&mut self) -> PResult<'a, (Box<Expr>)> {
let start = cur_pos!();
if is!("++") || is!("--") {
let op = if bump!() == PlusPlus {
op!("++")
} else {
op!("--")
};
let arg = self.parse_unary_expr()?;
if !arg.is_valid_simple_assignment_target(self.ctx().strict) {
syntax_error!(arg.span(), SyntaxError::NotSimpleAssign)
}
return Ok(box Expr::Update(UpdateExpr {
span: span!(start),
prefix: true,
op,
arg,
}));
}
if is_one_of!("delete", "void", "typeof", '+', '-', '~', '!') {
let op = match bump!() {
Word(Keyword(Delete)) => op!("delete"),
Word(Keyword(Void)) => op!("void"),
Word(Keyword(TypeOf)) => op!("typeof"),
BinOp(Add) => op!(unary, "+"),
BinOp(Sub) => op!(unary, "-"),
Tilde => op!("~"),
Bang => op!("!"),
_ => unreachable!(),
};
let arg = self.parse_unary_expr()?;
return Ok(box Expr::Unary(UnaryExpr {
span: span!(start),
op,
arg,
}));
}
if self.ctx().in_async && is!("await") {
return self.parse_await_expr();
}
let expr = self.parse_lhs_expr()?;
return_if_arrow!(expr);
if self.input.had_line_break_before_cur() {
return Ok(expr);
}
if is_one_of!("++", "--") {
if !expr.is_valid_simple_assignment_target(self.ctx().strict) {
syntax_error!(expr.span(), SyntaxError::NotSimpleAssign)
}
let start = cur_pos!();
let op = if bump!() == PlusPlus {
op!("++")
} else {
op!("--")
};
return Ok(box Expr::Update(UpdateExpr {
span: span!(start),
prefix: false,
op,
arg: expr,
}));
}
Ok(expr)
}
fn parse_await_expr(&mut self) -> PResult<'a, (Box<Expr>)> {
let start = cur_pos!();
assert_and_bump!("await");
assert!(self.ctx().in_async);
if is!('*') {
syntax_error!(SyntaxError::AwaitStar);
}
let arg = self.parse_unary_expr()?;
Ok(box Expr::Await(AwaitExpr {
span: span!(start),
arg,
}))
}
}
#[cfg(test)]
mod tests {
use super::*;
use swc_common::DUMMY_SP;
fn bin(s: &'static str) -> Box<Expr> {
test_parser(s, |p| {
p.parse_bin_expr()
.unwrap_or_else(|()| panic!("failed to parse '{}' as a binary expression", s))
})
}
#[allow(non_upper_case_globals)]
const span: Span = DUMMY_SP;
#[test]
fn simple() {
assert_eq_ignore_span!(
bin("5 + 4 * 7"),
box Expr::Bin(BinExpr {
span,
op: op!(bin, "+"),
left: bin("5"),
right: bin("4 * 7"),
})
);
}
#[test]
fn same_prec() {
assert_eq_ignore_span!(
bin("5 + 4 + 7"),
box Expr::Bin(BinExpr {
span,
op: op!(bin, "+"),
left: bin("5 + 4"),
right: bin("7"),
})
);
}
}