Skip to content

Commit

Permalink
Migrate iso parsing to boa_temporal
Browse files Browse the repository at this point in the history
  • Loading branch information
nekevss committed Dec 5, 2023
1 parent a9c33cd commit 21bc6fb
Show file tree
Hide file tree
Showing 23 changed files with 442 additions and 521 deletions.
1 change: 0 additions & 1 deletion boa_ast/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ 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"]
temporal = []

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

use boa_interner::{Interner, ToIndentedString, ToInternedString};
Expand Down
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 = ["js"]
annex-b = ["boa_parser/annex-b"]

# Stage 3 proposals
temporal = ["boa_parser/temporal", "dep:icu_calendar"]
temporal = ["dep:icu_calendar"]

# Enable experimental features, like Stage 3 proposals.
experimental = ["temporal"]
Expand Down
1 change: 1 addition & 0 deletions boa_engine/src/builtins/temporal/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ impl From<TemporalError> for JsNativeError {
ErrorKind::Range => JsNativeError::range().with_message(value.message()),
ErrorKind::Type => JsNativeError::typ().with_message(value.message()),
ErrorKind::Generic => JsNativeError::error().with_message(value.message()),
ErrorKind::Syntax => JsNativeError::syntax().with_message(value.message()),
}
}
}
Expand Down
37 changes: 7 additions & 30 deletions boa_engine/src/builtins/temporal/plain_date/mod.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
//! Boa's implementation of the ECMAScript `Temporal.PlainDate` builtin object.
#![allow(dead_code, unused_variables)]

use std::str::FromStr;

use crate::{
builtins::{
options::{get_option, get_options_object},
Expand All @@ -16,14 +14,8 @@ use crate::{
string::{common::StaticJsStrings, utf16},
Context, JsArgs, JsNativeError, JsObject, JsResult, JsString, JsSymbol, JsValue,
};
use boa_parser::temporal::{IsoCursor, TemporalDateTimeString};
use boa_profiler::Profiler;
use boa_temporal::{
calendar::{AvailableCalendars, CalendarSlot},
date::Date as InnerDate,
datetime::DateTime,
options::ArithmeticOverflow,
};
use boa_temporal::{date::Date as InnerDate, datetime::DateTime, options::ArithmeticOverflow};

use super::calendar;

Expand Down Expand Up @@ -517,32 +509,17 @@ pub(crate) fn to_temporal_date(
};

// 6. Let result be ? ParseTemporalDateString(item).
let result = TemporalDateTimeString::parse(
false,
&mut IsoCursor::new(&date_like_string.to_std_string_escaped()),
)
.map_err(|err| JsNativeError::range().with_message(err.to_string()))?;

// 7. Assert: IsValidISODate(result.[[Year]], result.[[Month]], result.[[Day]]) is true.
// 8. Let calendar be result.[[Calendar]].
// 9. If calendar is undefined, set calendar to "iso8601".
let identifier = result.date.calendar.unwrap_or("iso8601".to_string());

// 10. If IsBuiltinCalendar(calendar) is false, throw a RangeError exception.
let _ = AvailableCalendars::from_str(identifier.to_ascii_lowercase().as_str())?;

// 11. Set calendar to the ASCII-lowercase of calendar.
let calendar = CalendarSlot::Identifier(identifier.to_ascii_lowercase());

// 12. Perform ? ToTemporalOverflow(options).
let _ = get_option::<ArithmeticOverflow>(&options_obj, utf16!("overflow"), context)?;

// 13. Return ? CreateTemporalDate(result.[[Year]], result.[[Month]], result.[[Day]], calendar).
Ok(PlainDate::new(InnerDate::new(
result.date.year,
result.date.month,
result.date.day,
calendar,
ArithmeticOverflow::Reject,
)?))
let result = date_like_string
.to_std_string_escaped()
.parse::<InnerDate>()
.map_err(|err| JsNativeError::range().with_message(err.to_string()))?;

Ok(PlainDate::new(result))
}
4 changes: 2 additions & 2 deletions boa_engine/src/builtins/temporal/time_zone/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -364,10 +364,10 @@ pub(super) fn create_temporal_time_zone(
/// [spec]: https://tc39.es/ecma262/#sec-parsetimezoneoffsetstring
#[allow(clippy::unnecessary_wraps, unused)]
fn parse_timezone_offset_string(offset_string: &str, context: &mut Context) -> JsResult<i64> {
use boa_parser::temporal::{IsoCursor, TemporalTimeZoneString};
use boa_temporal::parser::{Cursor, TemporalTimeZoneString};

// 1. Let parseResult be ParseText(StringToCodePoints(offsetString), UTCOffset).
let parse_result = TemporalTimeZoneString::parse(&mut IsoCursor::new(offset_string))?;
let parse_result = TemporalTimeZoneString::parse(&mut Cursor::new(offset_string))?;

// 2. Assert: parseResult is not a List of errors.
// 3. Assert: parseResult contains a TemporalSign Parse Node.
Expand Down
1 change: 0 additions & 1 deletion boa_parser/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ icu_properties.workspace = true

[features]
annex-b = []
temporal = ["boa_ast/temporal"]

[lints]
workspace = true
2 changes: 0 additions & 2 deletions boa_parser/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@ pub mod error;
pub mod lexer;
pub mod parser;
mod source;
#[cfg(feature = "temporal")]
pub mod temporal;

pub use error::Error;
pub use lexer::Lexer;
Expand Down
32 changes: 29 additions & 3 deletions boa_temporal/src/date.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
//! The `PlainDate` representation.
use crate::{
calendar::CalendarSlot,
calendar::{AvailableCalendars, CalendarSlot},
datetime::DateTime,
duration::{DateDuration, Duration},
iso::{IsoDate, IsoDateSlots},
options::{ArithmeticOverflow, TemporalUnit},
TemporalResult,
parser::parse_date_time,
TemporalError, TemporalResult,
};
use std::any::Any;
use std::{any::Any, str::FromStr};

/// The `Temporal.PlainDate` equivalent
#[derive(Debug, Default, Clone)]
Expand Down Expand Up @@ -218,3 +219,28 @@ impl Date {
self.contextual_difference_date(other, largest_unit, &mut ())
}
}

// ==== Trait impls ====

impl FromStr for Date {
type Err = TemporalError;

fn from_str(s: &str) -> Result<Self, Self::Err> {
let parse_record = parse_date_time(s)?;

let calendar = parse_record.calendar.unwrap_or("iso8601".to_owned());
let _ = AvailableCalendars::from_str(calendar.to_ascii_lowercase().as_str())?;

let date = IsoDate::new(
parse_record.date.year,
parse_record.date.month,
parse_record.date.day,
ArithmeticOverflow::Reject,
)?;

Ok(Self::new_unchecked(
date,
CalendarSlot::Identifier(calendar),
))
}
}
41 changes: 40 additions & 1 deletion boa_temporal/src/datetime.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
//! Temporal implementation of `DateTime`
use std::str::FromStr;

use crate::{
calendar::CalendarSlot,
iso::{IsoDate, IsoDateSlots, IsoDateTime, IsoTime},
options::ArithmeticOverflow,
TemporalResult,
parser::parse_date_time,
TemporalError, TemporalResult,
};

/// The `DateTime` struct.
Expand Down Expand Up @@ -93,3 +96,39 @@ impl DateTime {
&self.calendar
}
}

// ==== Trait impls ====

impl FromStr for DateTime {
type Err = TemporalError;

fn from_str(s: &str) -> Result<Self, Self::Err> {
let parse_record = parse_date_time(s)?;

let calendar = parse_record.calendar.unwrap_or("iso8601".to_owned());

let time = if let Some(time) = parse_record.time {
IsoTime::from_components(
i32::from(time.hour),
i32::from(time.minute),
i32::from(time.second),
time.fraction,
)?
} else {
IsoTime::default()
};

let date = IsoDate::new(
parse_record.date.year,
parse_record.date.month,
parse_record.date.day,
ArithmeticOverflow::Reject,
)?;

Ok(Self::new_unchecked(
date,
time,
CalendarSlot::Identifier(calendar),
))
}
}
55 changes: 54 additions & 1 deletion boa_temporal/src/duration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@ use crate::{
date::Date,
datetime::DateTime,
options::{ArithmeticOverflow, TemporalRoundingMode, TemporalUnit},
parser::{duration::parse_duration, Cursor},
utils,
zoneddatetime::ZonedDateTime,
TemporalError, TemporalResult, NS_PER_DAY,
};
use std::any::Any;
use std::{any::Any, str::FromStr};

// ==== `DateDuration` ====

Expand Down Expand Up @@ -1688,3 +1689,55 @@ impl Duration {
Ok(result)
}
}

// ==== FromStr trait impl ====

impl FromStr for Duration {
type Err = TemporalError;

fn from_str(s: &str) -> Result<Self, Self::Err> {
let parse_record = parse_duration(&mut Cursor::new(s))?;

let minutes = if parse_record.time.fhours > 0.0 {
parse_record.time.fhours * 60.0
} else {
f64::from(parse_record.time.minutes)
};

let seconds = if parse_record.time.fminutes > 0.0 {
parse_record.time.fminutes * 60.0
} else if parse_record.time.seconds > 0 {
f64::from(parse_record.time.seconds)
} else {
minutes.rem_euclid(1.0) * 60.0
};

let milliseconds = if parse_record.time.fseconds > 0.0 {
parse_record.time.fseconds * 1000.0
} else {
seconds.rem_euclid(1.0) * 1000.0
};

let micro = milliseconds.rem_euclid(1.0) * 1000.0;
let nano = micro.rem_euclid(1.0) * 1000.0;

let sign = if parse_record.sign { 1f64 } else { -1f64 };

Ok(Self {
date: DateDuration::new(
f64::from(parse_record.date.years) * sign,
f64::from(parse_record.date.months) * sign,
f64::from(parse_record.date.weeks) * sign,
f64::from(parse_record.date.days) * sign,
),
time: TimeDuration::new(
f64::from(parse_record.time.hours) * sign,
minutes.floor() * sign,
seconds.floor() * sign,
milliseconds.floor() * sign,
micro.floor() * sign,
nano.floor() * sign,
),
})
}
}
15 changes: 15 additions & 0 deletions boa_temporal/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ pub enum ErrorKind {
Type,
/// RangeError
Range,
/// SyntaxError
Syntax,
}

impl fmt::Display for ErrorKind {
Expand All @@ -20,6 +22,7 @@ impl fmt::Display for ErrorKind {
Self::Generic => "Error",
Self::Type => "TypeError",
Self::Range => "RangeError",
Self::Syntax => "SyntaxError",
}
.fmt(f)
}
Expand Down Expand Up @@ -61,6 +64,18 @@ impl TemporalError {
Self::new(ErrorKind::Type)
}

/// Create a syntax error.
#[must_use]
pub fn syntax() -> Self {
Self::new(ErrorKind::Syntax)
}

/// Create an abrupt end error.
#[must_use]
pub fn abrupt_end() -> Self {
Self::syntax().with_message("Abrupt end to parsing target.")
}

/// Add a message to the error.
#[must_use]
pub fn with_message<S>(mut self, msg: S) -> Self
Expand Down
34 changes: 28 additions & 6 deletions boa_temporal/src/iso.rs
Original file line number Diff line number Diff line change
Expand Up @@ -209,12 +209,12 @@ impl IsoDate {
/// time slots.
#[derive(Debug, Default, Clone, Copy)]
pub struct IsoTime {
hour: i32, // 0..=23
minute: i32, // 0..=59
second: i32, // 0..=59
millisecond: i32, // 0..=999
microsecond: i32, // 0..=999
nanosecond: i32, // 0..=999
pub(crate) hour: i32, // 0..=23
pub(crate) minute: i32, // 0..=59
pub(crate) second: i32, // 0..=59
pub(crate) millisecond: i32, // 0..=999
pub(crate) microsecond: i32, // 0..=999
pub(crate) nanosecond: i32, // 0..=999
}

impl IsoTime {
Expand Down Expand Up @@ -281,6 +281,28 @@ impl IsoTime {
}
}

/// Returns an `IsoTime` based off parse components.
pub(crate) fn from_components(
hour: i32,
minute: i32,
second: i32,
fraction: f64,
) -> TemporalResult<Self> {
let millisecond = fraction * 1000f64;
let micros = millisecond.rem_euclid(1f64) * 1000f64;
let nanos = micros.rem_euclid(1f64).mul_add(1000f64, 0.5).floor();

Self::new(
hour,
minute,
second,
millisecond as i32,
micros as i32,
nanos as i32,
ArithmeticOverflow::Reject,
)
}

/// Checks if the time is a valid `IsoTime`
pub(crate) fn is_valid(&self) -> bool {
if !(0..=23).contains(&self.hour) {
Expand Down
1 change: 1 addition & 0 deletions boa_temporal/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ pub mod fields;
pub mod iso;
pub mod month_day;
pub mod options;
pub mod parser;
pub mod time;
pub(crate) mod utils;
pub mod year_month;
Expand Down
Loading

0 comments on commit 21bc6fb

Please sign in to comment.