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

Small parser refactor / cleanup #30

Merged
merged 1 commit into from
Feb 29, 2024
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
63 changes: 18 additions & 45 deletions src/parser/datetime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,23 +225,18 @@ pub(crate) fn parse_month_day(cursor: &mut Cursor) -> TemporalResult<(i32, i32)>

// ==== Unit Parsers ====

#[inline]
fn parse_date_year(cursor: &mut Cursor) -> TemporalResult<i32> {
if cursor.check_or(false, is_sign) {
let sign = if cursor.expect_next() == '+' { 1 } else { -1 };
let year_start = cursor.pos();

for _ in 0..6 {
let year_digit = cursor.abrupt_next()?;
assert_syntax!(
year_digit.is_ascii_digit(),
"Year must be made up of digits."
);
}

let year_value = cursor
.slice(year_start, cursor.pos())
.parse::<i32>()
.map_err(|e| TemporalError::syntax().with_message(e.to_string()))?;
let first = cursor.next_digit()? as i32 * 100_000;
let second = cursor.next_digit()? as i32 * 10_000;
let third = cursor.next_digit()? as i32 * 1000;
let fourth = cursor.next_digit()? as i32 * 100;
let fifth = cursor.next_digit()? as i32 * 10;

let year_value = first + second + third + fourth + fifth + cursor.next_digit()? as i32;

// 13.30.1 Static Semantics: Early Errors
//
Expand All @@ -260,50 +255,28 @@ fn parse_date_year(cursor: &mut Cursor) -> TemporalResult<i32> {
return Ok(year);
}

let year_start = cursor.pos();

for _ in 0..4 {
let year_digit = cursor.abrupt_next()?;
assert_syntax!(
year_digit.is_ascii_digit(),
"Year must be made up of digits."
);
}

let year_value = cursor
.slice(year_start, cursor.pos())
.parse::<i32>()
.map_err(|e| TemporalError::syntax().with_message(e.to_string()))?;
let first = cursor.next_digit()? as i32 * 1000;
let second = cursor.next_digit()? as i32 * 100;
let third = cursor.next_digit()? as i32 * 10;
let year_value = first + second + third + cursor.next_digit()? as i32;

Ok(year_value)
}

#[inline]
fn parse_date_month(cursor: &mut Cursor) -> TemporalResult<i32> {
let start = cursor.pos();
for _ in 0..2 {
let digit = cursor.abrupt_next()?;
assert_syntax!(digit.is_ascii_digit(), "Month must be a digit");
}
let month_value = cursor
.slice(start, cursor.pos())
.parse::<i32>()
.map_err(|e| TemporalError::syntax().with_message(e.to_string()))?;
let first = cursor.next_digit()? as i32;
let month_value = first * 10 + cursor.next_digit()? as i32;
if !(1..=12).contains(&month_value) {
return Err(TemporalError::syntax().with_message("DateMonth must be in a range of 1-12"));
}
Ok(month_value)
}

#[inline]
fn parse_date_day(cursor: &mut Cursor) -> TemporalResult<i32> {
let start = cursor.pos();
for _ in 0..2 {
let digit = cursor.abrupt_next()?;
assert_syntax!(digit.is_ascii_digit(), "Date must be a digit");
}
let day_value = cursor
.slice(start, cursor.pos())
.parse::<i32>()
.map_err(|e| TemporalError::syntax().with_message(e.to_string()))?;
let first = cursor.next_digit()? as i32;
let day_value = first * 10 + cursor.next_digit()? as i32;
if !(1..=31).contains(&day_value) {
return Err(TemporalError::syntax().with_message("DateDay must be in a range of 1-31"));
}
Expand Down
14 changes: 14 additions & 0 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,20 @@ impl Cursor {
result
}

/// Returns the next value as a digit.
///
/// # Errors
/// - Returns a SyntaxError if value is not an ascii digit
/// - Returns an AbruptEnd error if cursor ends.
fn next_digit(&mut self) -> TemporalResult<u8> {
let p_digit = self.abrupt_next()?.to_digit(10);
let Some(digit) = p_digit else {
return Err(TemporalError::syntax()
.with_message("Expected decimal digit, found non-digit character."));
};
Ok(digit as u8)
}

/// Utility method that returns next charactor unwrapped char
///
/// # Panics
Expand Down
28 changes: 8 additions & 20 deletions src/parser/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,35 +66,23 @@ pub(crate) fn parse_time_spec(cursor: &mut Cursor) -> TemporalResult<TimeSpec> {
})
}

/// Parse an hour value.
#[inline]
pub(crate) fn parse_hour(cursor: &mut Cursor) -> TemporalResult<u8> {
let start = cursor.pos();
for _ in 0..2 {
let digit = cursor.abrupt_next()?;
assert_syntax!(digit.is_ascii_digit(), "Hour must be a digit.");
}
let hour_value = cursor
.slice(start, cursor.pos())
.parse::<u8>()
.map_err(|e| TemporalError::syntax().with_message(e.to_string()))?;
let first = cursor.next_digit()?;
let hour_value = first * 10 + cursor.next_digit()?;
if !(0..=23).contains(&hour_value) {
return Err(TemporalError::syntax().with_message("Hour must be in a range of 0-23"));
}
Ok(hour_value)
}

// NOTE: `TimeSecond` is a 60 inclusive `MinuteSecond`.
/// Parse `MinuteSecond`
/// Parse `MinuteSecond` value.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you link to the corresponding grammar rule?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can do. Better documentation all the way around is definitely needed. Should we update the grammar rules as a whole in a follow-up PR or do you mind if I add them in on this PR?

Copy link
Member

@jedel1043 jedel1043 Feb 29, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you plan on doing a PR only for documentation, you can postpone this change for that PR.

#[inline]
pub(crate) fn parse_minute_second(cursor: &mut Cursor, inclusive: bool) -> TemporalResult<u8> {
let start = cursor.pos();
for _ in 0..2 {
let digit = cursor.abrupt_next()?;
assert_syntax!(digit.is_ascii_digit(), "MinuteSecond must be a digit.");
}
let min_sec_value = cursor
.slice(start, cursor.pos())
.parse::<u8>()
.map_err(|e| TemporalError::syntax().with_message(e.to_string()))?;

let first = cursor.next_digit()?;
let min_sec_value = first * 10 + cursor.next_digit()?;
let valid_range = if inclusive { 0..=60 } else { 0..=59 };
if !valid_range.contains(&min_sec_value) {
return Err(TemporalError::syntax().with_message("MinuteSecond must be in a range of 0-59"));
Expand Down