Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Temporal Parser Cleanup/Fixes #3521

Merged
merged 4 commits into from
Dec 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 35 additions & 2 deletions core/temporal/src/components/month_day.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
//! This module implements `MonthDay` and any directly related algorithms.

use std::str::FromStr;

use crate::{
components::calendar::CalendarSlot,
iso::{IsoDate, IsoDateSlots},
options::ArithmeticOverflow,
TemporalResult,
TemporalError, TemporalResult,
};

/// The native Rust implementation of `Temporal.PlainMonthDay`
Expand All @@ -22,8 +24,8 @@ impl MonthDay {
Self { iso, calendar }
}

#[inline]
/// Creates a new valid `MonthDay`.
#[inline]
pub fn new(
month: i32,
day: i32,
Expand All @@ -34,9 +36,23 @@ impl MonthDay {
Ok(Self::new_unchecked(iso, calendar))
}

/// Returns the `month` value of `MonthDay`.
#[inline]
#[must_use]
pub fn month(&self) -> u8 {
HalidOdat marked this conversation as resolved.
Show resolved Hide resolved
self.iso.month()
}

/// Returns the `day` value of `MonthDay`.
#[inline]
#[must_use]
pub fn day(&self) -> u8 {
self.iso.day()
}

/// Returns a reference to `MonthDay`'s `CalendarSlot`
#[inline]
#[must_use]
pub fn calendar(&self) -> &CalendarSlot {
&self.calendar
}
Expand All @@ -49,3 +65,20 @@ impl IsoDateSlots for MonthDay {
self.iso
}
}

impl FromStr for MonthDay {
type Err = TemporalError;

fn from_str(s: &str) -> Result<Self, Self::Err> {
let record = crate::parser::parse_month_day(s)?;

let calendar = record.calendar.unwrap_or("iso8601".into());

Self::new(
record.date.month,
record.date.day,
CalendarSlot::Identifier(calendar),
ArithmeticOverflow::Reject,
)
}
}
36 changes: 35 additions & 1 deletion core/temporal/src/components/year_month.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
//! This module implements `YearMonth` and any directly related algorithms.

use std::str::FromStr;

use crate::{
components::calendar::CalendarSlot,
iso::{IsoDate, IsoDateSlots},
options::ArithmeticOverflow,
TemporalResult,
TemporalError, TemporalResult,
};

/// The native Rust implementation of `Temporal.YearMonth`.
Expand Down Expand Up @@ -36,6 +38,20 @@ impl YearMonth {
Ok(Self::new_unchecked(iso, calendar))
}

/// Returns the `year` value for this `YearMonth`.
#[inline]
#[must_use]
pub fn year(&self) -> i32 {
self.iso.year()
}

/// Returns the `month` value for this `YearMonth`.
#[inline]
#[must_use]
pub fn month(&self) -> u8 {
self.iso.month()
}

#[inline]
#[must_use]
/// Returns a reference to `YearMonth`'s `CalendarSlot`
Expand All @@ -51,3 +67,21 @@ impl IsoDateSlots for YearMonth {
self.iso
}
}

impl FromStr for YearMonth {
type Err = TemporalError;

fn from_str(s: &str) -> Result<Self, Self::Err> {
let record = crate::parser::parse_year_month(s)?;

let calendar = record.calendar.unwrap_or("iso8601".into());

Self::new(
record.date.year,
record.date.month,
None,
CalendarSlot::Identifier(calendar),
ArithmeticOverflow::Reject,
)
}
}
77 changes: 38 additions & 39 deletions core/temporal/src/parser/annotations.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/// Parsing for Temporal's `Annotations`.
use crate::{
assert_syntax,
parser::{
grammar::{
is_a_key_char, is_a_key_leading_char, is_annotation_close,
Expand Down Expand Up @@ -38,10 +39,10 @@ pub(crate) fn parse_annotation_set(
) -> TemporalResult<AnnotationSet> {
// Parse the first annotation.
let tz_annotation = time_zone::parse_ambiguous_tz_annotation(cursor)?;

if tz_annotation.is_none() && zoned {
return Err(TemporalError::syntax()
.with_message("iso8601 ZonedDateTime requires a TimeZoneAnnotation."));
return Err(
TemporalError::syntax().with_message("ZonedDateTime must have a TimeZone annotation.")
);
}

// Parse any `Annotations`
Expand Down Expand Up @@ -97,32 +98,27 @@ pub(crate) fn parse_annotations(cursor: &mut Cursor) -> TemporalResult<Recognize

/// Parse an annotation with an `AnnotationKey`=`AnnotationValue` pair.
fn parse_kv_annotation(cursor: &mut Cursor) -> TemporalResult<KeyValueAnnotation> {
debug_assert!(cursor.check_or(false, is_annotation_open));

let potential_critical = cursor.next().ok_or_else(TemporalError::abrupt_end)?;
let (leading_char, critical) = if is_critical_flag(potential_critical) {
(cursor.next().ok_or_else(TemporalError::abrupt_end)?, true)
} else {
(potential_critical, false)
};
assert_syntax!(
is_annotation_open(cursor.abrupt_next()?),
"Invalid annotation open character."
);

if !is_a_key_leading_char(leading_char) {
return Err(TemporalError::syntax().with_message("Invalid AnnotationKey leading character"));
}
let critical = cursor.check_or(false, is_critical_flag);
cursor.advance_if(critical);

// Parse AnnotationKey.
let annotation_key = parse_annotation_key(cursor)?;

debug_assert!(cursor.check_or(false, is_annotation_key_value_separator));
// Advance past the '=' character.
cursor.advance();
assert_syntax!(
is_annotation_key_value_separator(cursor.abrupt_next()?),
"Invalid annotation key-value separator"
);

// Parse AnnotationValue.
let annotation_value = parse_annotation_value(cursor)?;

// Assert that we are at the annotation close and advance cursor past annotation to close.
debug_assert!(cursor.check_or(false, is_annotation_close));
cursor.advance();
assert_syntax!(
is_annotation_close(cursor.abrupt_next()?),
"Invalid annotion closing character"
);

Ok(KeyValueAnnotation {
key: annotation_key,
Expand All @@ -134,16 +130,22 @@ fn parse_kv_annotation(cursor: &mut Cursor) -> TemporalResult<KeyValueAnnotation
/// Parse an `AnnotationKey`.
fn parse_annotation_key(cursor: &mut Cursor) -> TemporalResult<String> {
let key_start = cursor.pos();
assert_syntax!(
is_a_key_leading_char(cursor.abrupt_next()?),
"Invalid key leading character."
);

while let Some(potential_key_char) = cursor.next() {
// End of key.
if is_annotation_key_value_separator(potential_key_char) {
if cursor.check_or(false, is_annotation_key_value_separator) {
// Return found key
return Ok(cursor.slice(key_start, cursor.pos()));
}

if !is_a_key_char(potential_key_char) {
return Err(TemporalError::syntax().with_message("Invalid AnnotationKey Character"));
}
assert_syntax!(
is_a_key_char(potential_key_char),
"Invalid annotation key character."
);
}

Err(TemporalError::abrupt_end())
Expand All @@ -152,29 +154,26 @@ fn parse_annotation_key(cursor: &mut Cursor) -> TemporalResult<String> {
/// Parse an `AnnotationValue`.
fn parse_annotation_value(cursor: &mut Cursor) -> TemporalResult<String> {
let value_start = cursor.pos();
cursor.advance();
while let Some(potential_value_char) = cursor.next() {
if is_annotation_close(potential_value_char) {
if cursor.check_or(false, is_annotation_close) {
// Return the determined AnnotationValue.
return Ok(cursor.slice(value_start, cursor.pos()));
}

if is_hyphen(potential_value_char) {
if !cursor
.peek_n(1)
.map_or(false, is_annotation_value_component)
{
return Err(TemporalError::syntax()
.with_message("Missing AttributeValueComponent after '-'"));
}
assert_syntax!(
cursor.peek().map_or(false, is_annotation_value_component),
"Missing annotation value compoenent after '-'"
);
cursor.advance();
continue;
}

if !is_annotation_value_component(potential_value_char) {
return Err(
TemporalError::syntax().with_message("Invalid character in AnnotationValue")
);
}
assert_syntax!(
is_annotation_value_component(potential_value_char),
"Invalid annotation value component character."
);
}

Err(TemporalError::abrupt_end())
Expand Down
Loading
Loading