Skip to content

Commit

Permalink
refactor(ecmascript): remove NumberValue (#6519)
Browse files Browse the repository at this point in the history
  • Loading branch information
Boshen committed Oct 13, 2024
1 parent 51fc63d commit 6d041fb
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 222 deletions.
17 changes: 5 additions & 12 deletions crates/oxc_ecmascript/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,10 @@ mod to_number;
mod to_string;

pub use self::{
bound_names::BoundNames,
is_simple_parameter_list::IsSimpleParameterList,
private_bound_identifiers::PrivateBoundIdentifiers,
prop_name::PropName,
string_char_at::StringCharAt,
string_index_of::StringIndexOf,
string_last_index_of::StringLastIndexOf,
string_to_big_int::StringToBigInt,
to_big_int::ToBigInt,
to_boolean::ToBoolean,
to_int_32::ToInt32,
to_number::{NumberValue, ToNumber},
bound_names::BoundNames, is_simple_parameter_list::IsSimpleParameterList,
private_bound_identifiers::PrivateBoundIdentifiers, prop_name::PropName,
string_char_at::StringCharAt, string_index_of::StringIndexOf,
string_last_index_of::StringLastIndexOf, string_to_big_int::StringToBigInt,
to_big_int::ToBigInt, to_boolean::ToBoolean, to_int_32::ToInt32, to_number::ToNumber,
to_string::ToJsString,
};
5 changes: 3 additions & 2 deletions crates/oxc_ecmascript/src/to_boolean.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use num_traits::Zero;
#[allow(clippy::wildcard_imports)]
use oxc_ast::ast::*;

use crate::{NumberValue, ToNumber};
use crate::ToNumber;

/// `ToBoolean`
///
Expand Down Expand Up @@ -93,7 +94,7 @@ impl<'a> ToBoolean<'a> for Expression<'a> {
// +1 -> true
// +0 -> false
// -0 -> false
self.to_number().map(|value| value != NumberValue::Number(0_f64))
self.to_number().map(|value| !value.is_zero())
} else if unary_expr.operator == UnaryOperator::LogicalNot {
// !true -> false
unary_expr.argument.to_boolean().map(|b| !b)
Expand Down
209 changes: 27 additions & 182 deletions crates/oxc_ecmascript/src/to_number.rs
Original file line number Diff line number Diff line change
@@ -1,214 +1,59 @@
use num_traits::Zero;

#[allow(clippy::wildcard_imports)]
use oxc_ast::ast::*;
use oxc_syntax::operator::UnaryOperator;

use crate::ToBoolean;

#[derive(Debug, Clone, Copy, PartialEq)]
pub enum NumberValue {
Number(f64),
PositiveInfinity,
NegativeInfinity,
NaN,
}

impl NumberValue {
pub fn is_nan(&self) -> bool {
matches!(self, Self::NaN)
}
}

impl Zero for NumberValue {
fn zero() -> Self {
Self::Number(0.0)
}

fn is_zero(&self) -> bool {
matches!(self, Self::Number(num) if num.is_zero())
}
}

impl std::ops::Add<Self> for NumberValue {
type Output = Self;

fn add(self, other: Self) -> Self {
match self {
Self::Number(num) => match other {
Self::Number(other_num) => Self::Number(num + other_num),
Self::PositiveInfinity => Self::PositiveInfinity,
Self::NegativeInfinity => Self::NegativeInfinity,
Self::NaN => Self::NaN,
},
Self::NaN => Self::NaN,
Self::PositiveInfinity => match other {
Self::NaN | Self::NegativeInfinity => Self::NaN,
_ => Self::PositiveInfinity,
},
Self::NegativeInfinity => match other {
Self::NaN | Self::PositiveInfinity => Self::NaN,
_ => Self::NegativeInfinity,
},
}
}
}

impl std::ops::Sub<Self> for NumberValue {
type Output = Self;

fn sub(self, other: Self) -> Self {
self + (-other)
}
}

impl std::ops::Mul<Self> for NumberValue {
type Output = Self;

fn mul(self, other: Self) -> Self {
match self {
Self::Number(num) => match other {
Self::Number(other_num) => Self::Number(num * other_num),
Self::PositiveInfinity | Self::NegativeInfinity if num.is_zero() => Self::NaN,
Self::PositiveInfinity => Self::PositiveInfinity,
Self::NegativeInfinity => Self::NegativeInfinity,
Self::NaN => Self::NaN,
},
Self::NaN => Self::NaN,
Self::PositiveInfinity | Self::NegativeInfinity => match other {
Self::Number(num) if num > 0.0 => self,
Self::Number(num) if num < 0.0 => -self,
Self::PositiveInfinity => self,
Self::NegativeInfinity => -self,
_ => Self::NaN,
},
}
}
}

impl std::ops::Div<Self> for NumberValue {
type Output = Self;

fn div(self, other: Self) -> Self {
match self {
Self::Number(num) => match other {
Self::Number(other_num) if other_num.is_zero() => Self::NaN,
Self::Number(other_num) => Self::Number(num / other_num),
Self::PositiveInfinity | Self::NegativeInfinity if num < 0.0 => -other,
Self::PositiveInfinity | Self::NegativeInfinity if num > 0.0 => other,
_ => Self::NaN,
},
Self::NaN => Self::NaN,
Self::PositiveInfinity | Self::NegativeInfinity => match other {
Self::Number(num) if num > 0.0 => self,
Self::Number(num) if num < 0.0 => -self,
_ => Self::NaN,
},
}
}
}

impl std::ops::Rem<Self> for NumberValue {
type Output = Self;

fn rem(self, other: Self) -> Self {
match self {
Self::Number(num) => match other {
Self::Number(other_num) if other_num.is_zero() => Self::NaN,
Self::Number(other_num) => Self::Number(num % other_num),
Self::PositiveInfinity | Self::NegativeInfinity if num.is_zero() => Self::NaN,
Self::PositiveInfinity | Self::NegativeInfinity => self,
Self::NaN => Self::NaN,
},
Self::NaN => Self::NaN,
Self::PositiveInfinity | Self::NegativeInfinity => match other {
Self::Number(num) if !num.is_zero() => self,
_ => Self::NaN,
},
}
}
}

impl std::ops::Neg for NumberValue {
type Output = Self;

fn neg(self) -> Self {
match self {
Self::Number(num) => Self::Number(-num),
Self::PositiveInfinity => Self::NegativeInfinity,
Self::NegativeInfinity => Self::PositiveInfinity,
Self::NaN => Self::NaN,
}
}
}

impl TryFrom<NumberValue> for f64 {
type Error = ();

fn try_from(value: NumberValue) -> Result<Self, Self::Error> {
match value {
NumberValue::Number(num) => Ok(num),
NumberValue::PositiveInfinity => Ok(Self::INFINITY),
NumberValue::NegativeInfinity => Ok(Self::NEG_INFINITY),
NumberValue::NaN => Err(()),
}
}
}

/// `ToNumber`
///
/// <https://tc39.es/ecma262/#sec-tonumber>
pub trait ToNumber<'a> {
fn to_number(&self) -> Option<NumberValue>;
fn to_number(&self) -> Option<f64>;
}

impl<'a> ToNumber<'a> for Expression<'a> {
fn to_number(&self) -> Option<NumberValue> {
fn to_number(&self) -> Option<f64> {
match self {
Expression::NumericLiteral(number_literal) => {
Some(NumberValue::Number(number_literal.value))
}
Expression::NumericLiteral(number_literal) => Some(number_literal.value),
Expression::UnaryExpression(unary_expr) => match unary_expr.operator {
UnaryOperator::UnaryPlus => unary_expr.argument.to_number(),
UnaryOperator::UnaryNegation => unary_expr.argument.to_number().map(|v| -v),
UnaryOperator::BitwiseNot => {
unary_expr.argument.to_number().map(|value| {
match value {
NumberValue::Number(num) => NumberValue::Number(f64::from(
!NumericLiteral::ecmascript_to_int32(num),
)),
// ~Infinity -> -1
// ~-Infinity -> -1
// ~NaN -> -1
_ => NumberValue::Number(-1_f64),
}
})
// UnaryOperator::BitwiseNot => {
// unary_expr.argument.to_number().map(|value| {
// match value {
// NumberValue::Number(num) => NumberValue::Number(f64::from(
// !NumericLiteral::ecmascript_to_int32(num),
// )),
// // ~Infinity -> -1
// // ~-Infinity -> -1
// // ~NaN -> -1
// _ => NumberValue::Number(-1_f64),
// }
// })
// }
UnaryOperator::LogicalNot => {
self.to_boolean().map(|tri| if tri { 1_f64 } else { 0_f64 })
}
UnaryOperator::LogicalNot => self
.to_boolean()
.map(|tri| if tri { 1_f64 } else { 0_f64 })
.map(NumberValue::Number),
UnaryOperator::Void => Some(NumberValue::NaN),
UnaryOperator::Void => Some(f64::NAN),
_ => None,
},
Expression::BooleanLiteral(bool_literal) => {
if bool_literal.value {
Some(NumberValue::Number(1.0))
Some(1.0)
} else {
Some(NumberValue::Number(0.0))
Some(0.0)
}
}
Expression::NullLiteral(_) => Some(NumberValue::Number(0.0)),
Expression::NullLiteral(_) => Some(0.0),
Expression::Identifier(ident) => match ident.name.as_str() {
"Infinity" => Some(NumberValue::PositiveInfinity),
"NaN" | "undefined" => Some(NumberValue::NaN),
"Infinity" => Some(f64::INFINITY),
"NaN" | "undefined" => Some(f64::NAN),
_ => None,
},
// TODO: will be implemented in next PR, just for test pass now.
Expression::StringLiteral(string_literal) => string_literal
.value
.parse::<f64>()
.map_or(Some(NumberValue::NaN), |num| Some(NumberValue::Number(num))),
Expression::StringLiteral(string_literal) => {
string_literal.value.parse::<f64>().map_or(Some(f64::NAN), Some)
}
_ => None,
}
}
Expand Down
Loading

0 comments on commit 6d041fb

Please sign in to comment.