Skip to content

Commit

Permalink
Temporal Parser Cleanup/Fixes (#3521)
Browse files Browse the repository at this point in the history
* Update/fix iso grammar parser

* Revert handling of no tz-anno && zoned

* cargo clippy --all-features

* Apply review, small fix on MonthDay/YearMonth, more tests
  • Loading branch information
nekevss authored Dec 20, 2023
1 parent af9d395 commit c2f145c
Show file tree
Hide file tree
Showing 9 changed files with 482 additions and 390 deletions.
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 {
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

0 comments on commit c2f145c

Please sign in to comment.