diff --git a/src/lib/exec.rs b/src/lib/exec.rs index cf4210cdef..f20aee9970 100644 --- a/src/lib/exec.rs +++ b/src/lib/exec.rs @@ -266,6 +266,15 @@ impl Executor for Interpreter { UnaryOp::Minus => to_value(-v_a.to_num()), UnaryOp::Plus => to_value(v_a.to_num()), UnaryOp::Not => Gc::new(!v_a), + UnaryOp::Tilde => { + let num_v_a = v_a.to_num(); + // NOTE: possible UB: https://github.com/rust-lang/rust/issues/10184 + to_value(if num_v_a.is_nan() { + -1 + } else { + !(num_v_a as i32) + }) + } _ => unreachable!(), }) } @@ -751,4 +760,42 @@ mod tests { "#; assert_eq!(exec(non_num_key_wont_affect_length), String::from("3")); } + + #[test] + fn test_tilde_operator() { + let float = r#" + let f = -1.2; + ~f + "#; + assert_eq!(exec(float), String::from("0")); + + let numeric = r#" + let f = 1789; + ~f + "#; + assert_eq!(exec(numeric), String::from("-1790")); + + // TODO: enable test after we have NaN + // let nan = r#" + // var m = NaN; + // ~m + // "#; + // assert_eq!(exec(nan), String::from("-1")); + + let object = r#" + let m = {}; + ~m + "#; + assert_eq!(exec(object), String::from("-1")); + + let boolean_true = r#" + ~true + "#; + assert_eq!(exec(boolean_true), String::from("-2")); + + let boolean_false = r#" + ~false + "#; + assert_eq!(exec(boolean_false), String::from("-1")); + } } diff --git a/src/lib/syntax/ast/expr.rs b/src/lib/syntax/ast/expr.rs index eac8c040ae..cfc094718d 100644 --- a/src/lib/syntax/ast/expr.rs +++ b/src/lib/syntax/ast/expr.rs @@ -100,6 +100,7 @@ impl Operator for ExprDef { | ExprDef::UnaryOp(UnaryOp::DecrementPost, _) | ExprDef::UnaryOp(UnaryOp::DecrementPre, _) => 3, ExprDef::UnaryOp(UnaryOp::Not, _) + | ExprDef::UnaryOp(UnaryOp::Tilde, _) | ExprDef::UnaryOp(UnaryOp::Minus, _) | ExprDef::TypeOf(_) => 4, ExprDef::BinOp(op, _, _) => op.get_precedence(), diff --git a/src/lib/syntax/ast/op.rs b/src/lib/syntax/ast/op.rs index 83a2858670..4c9c0f8449 100644 --- a/src/lib/syntax/ast/op.rs +++ b/src/lib/syntax/ast/op.rs @@ -64,6 +64,8 @@ pub enum UnaryOp { Plus, /// `!a` - get the opposite of the boolean value Not, + /// `~a` - bitwise-not of the value + Tilde, } impl Display for UnaryOp { @@ -77,6 +79,7 @@ impl Display for UnaryOp { UnaryOp::Plus => "+", UnaryOp::Minus => "-", UnaryOp::Not => "!", + UnaryOp::Tilde => "~", } ) } diff --git a/src/lib/syntax/parser.rs b/src/lib/syntax/parser.rs index e882c1d142..0a37da60eb 100644 --- a/src/lib/syntax/parser.rs +++ b/src/lib/syntax/parser.rs @@ -566,6 +566,10 @@ impl Parser { self, ExprDef::UnaryOp(UnaryOp::Not, Box::new(self.parse()?)) ), + TokenData::Punctuator(Punctuator::Neg) => mk!( + self, + ExprDef::UnaryOp(UnaryOp::Tilde, Box::new(self.parse()?)) + ), TokenData::Punctuator(Punctuator::Inc) => mk!( self, ExprDef::UnaryOp(UnaryOp::IncrementPre, Box::new(self.parse()?))