Skip to content

Commit

Permalink
First portion of the Temporal implementation (boa-dev#3277)
Browse files Browse the repository at this point in the history
* Started with the Temporal implementation

* Implemented some useful functions

* Updaating some spec references

* Initial work on TimeZone and Instant

* More work completed on Temporal.Duration and Temporal.Instant

* General scaffolding and heavy work on Instant and Duration complete

* ZonedDateTime and Calendar started with further work on duration abstract ops

* Further work on temporal work and clippy fixes

* Post rebase fixes/reverts

* Add BuiltinCalendar and begin IsoCalendar impl

* More work completed on calendar/date/yearmonth/monthday

* Calendar and iso impl close to completion - no datelike parsing

* Initial work on temporal ISO8601 parsing and grammar

* Post rebase fixes and updates

* More on parser/Duration and work through clippy lints

* Fix bug on peek_n and add temporal cfg

* Fix clippy lints on parser tests

* Build out calendar with icu_calendar, add some tests, and misc.

* Fix spec hyperlinks

* Parser clean up and invalid annotations

* Add Duration and Temporal Parsing

* Remove IsoYearMonthRecord

* Post rebase update

* Fix and add to ISO Parser docs

* Parser/ast cleanup and duration refactor/additions

* Review feedback, options update, and duration changes

* Review changes, general cleanup, and post rebase fixes

* Fix time zone parsing issue/test logic

* Clean up parse output nodes

* Apply review feedback and various fixes

* Review feedback and get_option changes

* Review feedback

---------

Co-authored-by: Iban Eguia Moraza <[email protected]>
Co-authored-by: José Julián Espina <[email protected]>
  • Loading branch information
3 people authored and sam-finch-tezos committed Nov 29, 2023
1 parent 94abcda commit ae31e50
Show file tree
Hide file tree
Showing 49 changed files with 13,531 additions and 7 deletions.
1 change: 1 addition & 0 deletions boa_ast/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ rust-version.workspace = true
[features]
serde = ["dep:serde", "boa_interner/serde", "bitflags/serde", "num-bigint/serde"]
arbitrary = ["dep:arbitrary", "boa_interner/arbitrary", "num-bigint/arbitrary"]
experimental = []

[dependencies]
boa_interner.workspace = true
Expand Down
2 changes: 2 additions & 0 deletions boa_ast/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ pub mod operations;
pub mod pattern;
pub mod property;
pub mod statement;
#[cfg(feature = "experimental")]
pub mod temporal;
pub mod visitor;

use boa_interner::{Interner, ToIndentedString, ToInternedString};
Expand Down
113 changes: 113 additions & 0 deletions boa_ast/src/temporal/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
//! AST nodes for Temporal's implementation of ISO8601 grammar.
/// An ISO Date Node consisting of non-validated date fields and calendar value.
#[derive(Default, Debug)]
pub struct IsoDate {
/// Date Year
pub year: i32,
/// Date Month
pub month: i32,
/// Date Day
pub day: i32,
/// The calendar value.
pub calendar: Option<String>,
}

/// The `IsoTime` node consists of non-validated time fields.
#[derive(Default, Debug, Clone, Copy)]
pub struct IsoTime {
/// An hour value between 0-23
pub hour: u8,
/// A minute value between 0-59
pub minute: u8,
/// A second value between 0-60
pub second: u8,
/// A millisecond value between 0-999
pub millisecond: u16,
/// A microsecond value between 0-999
pub microsecond: u16,
/// A nanosecond value between 0-999
pub nanosecond: u16,
}

impl IsoTime {
#[must_use]
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
/// A utility initialization function to create `ISOTime` from the `TimeSpec` components.
pub fn from_components(hour: u8, minute: u8, second: u8, fraction: f64) -> Self {
// Note: Precision on nanoseconds drifts, so opting for round over floor or ceil for now.
// e.g. 0.329402834 becomes 329.402833.999
let millisecond = fraction * 1000.0;
let micros = millisecond.rem_euclid(1.0) * 1000.0;
let nanos = micros.rem_euclid(1.0) * 1000.0;

Self {
hour,
minute,
second,
millisecond: millisecond.floor() as u16,
microsecond: micros.floor() as u16,
nanosecond: nanos.round() as u16,
}
}
}

/// The `IsoDateTime` node output by the ISO parser
#[derive(Default, Debug)]
pub struct IsoDateTime {
/// The `ISODate` record
pub date: IsoDate,
/// The `ISOTime` record
pub time: IsoTime,
/// The `TimeZone` value for this `ISODateTime`
pub tz: Option<TimeZone>,
}

/// `TimeZone` data
#[derive(Default, Debug, Clone)]
pub struct TimeZone {
/// TimeZoneIANAName
pub name: Option<String>,
/// TimeZoneOffset
pub offset: Option<UTCOffset>,
}

/// A full precision `UtcOffset`
#[derive(Debug, Clone, Copy)]
pub struct UTCOffset {
/// The `+`/`-` sign of this `UtcOffset`
pub sign: i8,
/// The hour value of the `UtcOffset`
pub hour: u8,
/// The minute value of the `UtcOffset`.
pub minute: u8,
/// The second value of the `UtcOffset`.
pub second: u8,
/// Any sub second components of the `UTCOffset`
pub fraction: f64,
}

/// An `IsoDuration` Node output by the ISO parser.
#[derive(Debug, Default, Clone, Copy)]
pub struct IsoDuration {
/// Years value.
pub years: i32,
/// Months value.
pub months: i32,
/// Weeks value.
pub weeks: i32,
/// Days value.
pub days: i32,
/// Hours value.
pub hours: i32,
/// Minutes value.
pub minutes: f64,
/// Seconds value.
pub seconds: f64,
/// Milliseconds value.
pub milliseconds: f64,
/// Microseconds value.
pub microseconds: f64,
/// Nanoseconds value.
pub nanoseconds: f64,
}
2 changes: 1 addition & 1 deletion boa_engine/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ trace = []
annex-b = ["boa_parser/annex-b"]

# Enable experimental features, like Stage 3 proposals.
experimental = []
experimental = ["boa_parser/experimental", "dep:icu_calendar"]

[dependencies]
boa_interner.workspace = true
Expand Down
11 changes: 11 additions & 0 deletions boa_engine/src/bigint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,17 @@ impl JsBigInt {
Self::new(x.inner.as_ref().clone().add(y.inner.as_ref()))
}

/// Utility function for performing `+` operation on more than two values.
#[inline]
#[cfg(feature = "experimental")]
pub(crate) fn add_n(values: &[Self]) -> Self {
let mut result = Self::zero();
for big_int in values {
result = Self::add(&result, big_int);
}
result
}

/// Performs the `-` operation.
#[inline]
#[must_use]
Expand Down
2 changes: 1 addition & 1 deletion boa_engine/src/builtins/date/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
//! [spec]: https://tc39.es/ecma262/#sec-date-objects
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date
mod utils;
pub(crate) mod utils;
use utils::{make_date, make_day, make_time, replace_params, time_clip, DateParameters};

#[cfg(test)]
Expand Down
6 changes: 3 additions & 3 deletions boa_engine/src/builtins/date/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ pub(super) const fn day_from_year(year: i64) -> i64 {
/// Abstract operation [`MakeTime`][spec].
///
/// [spec]: https://tc39.es/ecma262/multipage/numbers-and-dates.html#sec-maketime
pub(super) fn make_time(hour: i64, min: i64, sec: i64, ms: i64) -> Option<i64> {
pub(crate) fn make_time(hour: i64, min: i64, sec: i64, ms: i64) -> Option<i64> {
// 1. If hour is not finite or min is not finite or sec is not finite or ms is not finite, return NaN.
// 2. Let h be 𝔽(! ToIntegerOrInfinity(hour)).
// 3. Let m be 𝔽(! ToIntegerOrInfinity(min)).
Expand All @@ -59,7 +59,7 @@ pub(super) fn make_time(hour: i64, min: i64, sec: i64, ms: i64) -> Option<i64> {
/// Abstract operation [`MakeDay`][spec].
///
/// [spec]: https://tc39.es/ecma262/multipage/numbers-and-dates.html#sec-makeday
pub(super) fn make_day(mut year: i64, mut month: i64, date: i64) -> Option<i64> {
pub(crate) fn make_day(mut year: i64, mut month: i64, date: i64) -> Option<i64> {
// 1. If year is not finite or month is not finite or date is not finite, return NaN.
// 2. Let y be 𝔽(! ToIntegerOrInfinity(year)).
// 3. Let m be 𝔽(! ToIntegerOrInfinity(month)).
Expand Down Expand Up @@ -101,7 +101,7 @@ pub(super) fn make_day(mut year: i64, mut month: i64, date: i64) -> Option<i64>
/// Abstract operation [`MakeDate`][spec].
///
/// [spec]: https://tc39.es/ecma262/multipage/numbers-and-dates.html#sec-makedate
pub(super) fn make_date(day: i64, time: i64) -> Option<i64> {
pub(crate) fn make_date(day: i64, time: i64) -> Option<i64> {
// 1. If day is not finite or time is not finite, return NaN.
// 2. Let tv be day × msPerDay + time.
// 3. If tv is not finite, return NaN.
Expand Down
24 changes: 24 additions & 0 deletions boa_engine/src/builtins/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ pub mod intl;
#[cfg(any(feature = "intl", feature = "experimental"))]
pub(crate) mod options;

#[cfg(feature = "experimental")]
pub mod temporal;

pub(crate) use self::{
array::Array,
async_function::AsyncFunction,
Expand Down Expand Up @@ -275,6 +278,22 @@ impl Realm {
intl::segmenter::SegmentIterator::init(self);
intl::PluralRules::init(self);
}

#[cfg(feature = "experimental")]
{
temporal::TimeZone::init(self);
temporal::Temporal::init(self);
temporal::Now::init(self);
temporal::Instant::init(self);
temporal::Duration::init(self);
temporal::PlainDate::init(self);
temporal::PlainTime::init(self);
temporal::PlainDateTime::init(self);
temporal::PlainMonthDay::init(self);
temporal::PlainYearMonth::init(self);
temporal::ZonedDateTime::init(self);
temporal::Calendar::init(self);
}
}
}

Expand Down Expand Up @@ -374,6 +393,11 @@ pub(crate) fn set_default_global_bindings(context: &mut Context<'_>) -> JsResult
#[cfg(feature = "intl")]
global_binding::<intl::Intl>(context)?;

#[cfg(feature = "experimental")]
{
global_binding::<temporal::Temporal>(context)?;
}

Ok(())
}

Expand Down
47 changes: 47 additions & 0 deletions boa_engine/src/builtins/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,53 @@ pub(crate) enum RoundingMode {
HalfEven,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum UnsignedRoundingMode {
Infinity,
Zero,
HalfInfinity,
HalfZero,
HalfEven,
}

impl RoundingMode {
pub(crate) const fn negate(self) -> Self {
use RoundingMode::{
Ceil, Expand, Floor, HalfCeil, HalfEven, HalfExpand, HalfFloor, HalfTrunc, Trunc,
};

match self {
Ceil => Self::Floor,
Floor => Self::Ceil,
HalfCeil => Self::HalfFloor,
HalfFloor => Self::HalfCeil,
Trunc => Self::Trunc,
Expand => Self::Expand,
HalfTrunc => Self::HalfTrunc,
HalfExpand => Self::HalfExpand,
HalfEven => Self::HalfEven,
}
}

pub(crate) const fn get_unsigned_round_mode(self, is_negative: bool) -> UnsignedRoundingMode {
use RoundingMode::{
Ceil, Expand, Floor, HalfCeil, HalfEven, HalfExpand, HalfFloor, HalfTrunc, Trunc,
};

match self {
Ceil if !is_negative => UnsignedRoundingMode::Infinity,
Ceil => UnsignedRoundingMode::Zero,
Floor if !is_negative => UnsignedRoundingMode::Zero,
Floor | Trunc | Expand => UnsignedRoundingMode::Infinity,
HalfCeil if !is_negative => UnsignedRoundingMode::HalfInfinity,
HalfCeil | HalfTrunc => UnsignedRoundingMode::HalfZero,
HalfFloor if !is_negative => UnsignedRoundingMode::HalfZero,
HalfFloor | HalfExpand => UnsignedRoundingMode::HalfInfinity,
HalfEven => UnsignedRoundingMode::HalfEven,
}
}
}

#[derive(Debug)]
pub(crate) struct ParseRoundingModeError;

Expand Down
Loading

0 comments on commit ae31e50

Please sign in to comment.