Skip to content

Commit

Permalink
Editorial: Collapse nonterminals only used for disambiguation
Browse files Browse the repository at this point in the history
Prefer language like "the first |TemporalDecimalFraction|, if present"
rather than defining several nonterminals such as |TimeFraction|,
|DurationHoursFraction|, etc.

Closes: #2006
  • Loading branch information
ptomato committed Jun 13, 2024
1 parent 31298fc commit 3765bf6
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 124 deletions.
131 changes: 73 additions & 58 deletions polyfill/test/validStrings.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -225,26 +225,11 @@ const dateYear = withCode(
const dateMonth = withCode(zeroPaddedInclusive(1, 12, 2), (data, result) => (data.month = +result));
const dateDay = withCode(zeroPaddedInclusive(1, 31, 2), (data, result) => (data.day = +result));

function saveHour(data, result) {
data.hour = +result;
}
function saveMinute(data, result) {
data.minute = +result;
}
function saveSecond(data, result) {
data.second = +result;
if (data.second === 60) data.second = 59;
}
const timeHour = withCode(hour, saveHour);
const timeMinute = withCode(minuteSecond, saveMinute);
const timeSecond = withCode(choice(minuteSecond, '60'), saveSecond);
const timeFraction = withCode(temporalDecimalFraction, (data, result) => {
result = result.slice(1);
const fraction = result.padEnd(9, '0');
data.millisecond = +fraction.slice(0, 3);
data.microsecond = +fraction.slice(3, 6);
data.nanosecond = +fraction.slice(6, 9);
});

function saveOffset(data, result) {
data.offset = ES.ParseDateTimeUTCOffset(result);
Expand All @@ -266,10 +251,9 @@ const utcOffset = (subMinutePrecision) =>
]);
const dateTimeUTCOffset = (z) =>
z ? choice(utcDesignator, withCode(utcOffset(true), saveOffset)) : withCode(utcOffset(true), saveOffset);
const timeZoneUTCOffsetName = utcOffset(false);
const timeZoneIANAName = choice(...timezoneNames);
const timeZoneIdentifier = withCode(
choice(timeZoneUTCOffsetName, timeZoneIANAName),
choice(utcOffset(false), timeZoneIANAName),
(data, result) => (data.tzAnnotation = result)
);
const timeZoneAnnotation = seq('[', [annotationCriticalFlag], timeZoneIdentifier, ']');
Expand All @@ -291,7 +275,26 @@ const annotations = withSyntaxConstraints(oneOrMore(choice(calendarAnnotation, a
}
});
const timeSpec = (extended) =>
seq(timeHour, [timeSeparator(extended), timeMinute, [timeSeparator(extended), timeSecond, [timeFraction]]]);
seq(
withCode(hour, (data, result) => (data.hour = +result)),
[
timeSeparator(extended),
withCode(minuteSecond, (data, result) => (data.minute = +result)),
[
timeSeparator(extended),
timeSecond,
[
withCode(temporalDecimalFraction, (data, result) => {
result = result.slice(1);
const fraction = result.padEnd(9, '0');
data.millisecond = +fraction.slice(0, 3);
data.microsecond = +fraction.slice(3, 6);
data.nanosecond = +fraction.slice(6, 9);
})
]
]
]
);
const time = choice(timeSpec(true), timeSpec(false));

function validateDayOfMonth(result, { year, month, day }) {
Expand Down Expand Up @@ -349,72 +352,84 @@ const annotatedMonthDay = withSyntaxConstraints(
}
);

const durationSecondsFraction = withCode(temporalDecimalFraction, (data, result) => {
result = result.slice(1);
const fraction = result.padEnd(9, '0');
data.milliseconds = +fraction.slice(0, 3) * data.factor;
data.microseconds = +fraction.slice(3, 6) * data.factor;
data.nanoseconds = +fraction.slice(6, 9) * data.factor;
});
const durationMinutesFraction = withCode(temporalDecimalFraction, (data, result) => {
result = result.slice(1);
const ns = +result.padEnd(9, '0') * 60;
data.seconds = Math.trunc(ns / 1e9) * data.factor;
data.milliseconds = Math.trunc((ns % 1e9) / 1e6) * data.factor;
data.microseconds = Math.trunc((ns % 1e6) / 1e3) * data.factor;
data.nanoseconds = Math.trunc(ns % 1e3) * data.factor;
});
const durationHoursFraction = withCode(temporalDecimalFraction, (data, result) => {
result = result.slice(1);
const ns = +result.padEnd(9, '0') * 3600;
data.minutes = Math.trunc(ns / 6e10) * data.factor;
data.seconds = Math.trunc((ns % 6e10) / 1e9) * data.factor;
data.milliseconds = Math.trunc((ns % 1e9) / 1e6) * data.factor;
data.microseconds = Math.trunc((ns % 1e6) / 1e3) * data.factor;
data.nanoseconds = Math.trunc(ns % 1e3) * data.factor;
});

const uint32Digits = withSyntaxConstraints(between(1, 10, digit()), (result) => {
if (+result >= 2 ** 32) throw new SyntaxError('try again for an uint32');
});
const timeDurationDigits = (factor) =>
withSyntaxConstraints(between(1, 16, digit()), (result) => {
if (!Number.isSafeInteger(+result * factor)) throw new SyntaxError('try again on unsafe integer');
});
const durationSeconds = seq(
const durationSecondsPart = seq(
withCode(timeDurationDigits(1), (data, result) => (data.seconds = +result * data.factor)),
[durationSecondsFraction],
[
withCode(temporalDecimalFraction, (data, result) => {
result = result.slice(1);
const fraction = result.padEnd(9, '0');
data.milliseconds = +fraction.slice(0, 3) * data.factor;
data.microseconds = +fraction.slice(3, 6) * data.factor;
data.nanoseconds = +fraction.slice(6, 9) * data.factor;
})
],
secondsDesignator
);
const durationMinutes = seq(
const durationMinutesPart = seq(
withCode(timeDurationDigits(60), (data, result) => (data.minutes = +result * data.factor)),
choice(seq(minutesDesignator, [durationSeconds]), seq(durationMinutesFraction, minutesDesignator))
choice(
seq(minutesDesignator, [durationSecondsPart]),
seq(
withCode(temporalDecimalFraction, (data, result) => {
result = result.slice(1);
const ns = +result.padEnd(9, '0') * 60;
data.seconds = Math.trunc(ns / 1e9) * data.factor;
data.milliseconds = Math.trunc((ns % 1e9) / 1e6) * data.factor;
data.microseconds = Math.trunc((ns % 1e6) / 1e3) * data.factor;
data.nanoseconds = Math.trunc(ns % 1e3) * data.factor;
}),
minutesDesignator
)
)
);
const durationHours = seq(
const durationHoursPart = seq(
withCode(timeDurationDigits(3600), (data, result) => (data.hours = +result * data.factor)),
choice(seq(hoursDesignator, [choice(durationMinutes, durationSeconds)]), seq(durationHoursFraction, hoursDesignator))
choice(
seq(hoursDesignator, [choice(durationMinutesPart, durationSecondsPart)]),
seq(
withCode(temporalDecimalFraction, (data, result) => {
result = result.slice(1);
const ns = +result.padEnd(9, '0') * 3600;
data.minutes = Math.trunc(ns / 6e10) * data.factor;
data.seconds = Math.trunc((ns % 6e10) / 1e9) * data.factor;
data.milliseconds = Math.trunc((ns % 1e9) / 1e6) * data.factor;
data.microseconds = Math.trunc((ns % 1e6) / 1e3) * data.factor;
data.nanoseconds = Math.trunc(ns % 1e3) * data.factor;
}),
hoursDesignator
)
)
);
const durationTime = seq(timeDesignator, choice(durationHours, durationMinutes, durationSeconds));
const durationDays = seq(
const durationTime = seq(timeDesignator, choice(durationHoursPart, durationMinutesPart, durationSecondsPart));
const durationDaysPart = seq(
withCode(timeDurationDigits(86400), (data, result) => (data.days = +result * data.factor)),
daysDesignator
);
const durationWeeks = seq(
const durationWeeksPart = seq(
withCode(uint32Digits, (data, result) => (data.weeks = +result * data.factor)),
weeksDesignator,
[durationDays]
[durationDaysPart]
);
const durationMonths = seq(
const durationMonthsPart = seq(
withCode(uint32Digits, (data, result) => (data.months = +result * data.factor)),
monthsDesignator,
[choice(durationWeeks, durationDays)]
[choice(durationWeeksPart, durationDaysPart)]
);
const durationYears = seq(
const durationYearsPart = seq(
withCode(uint32Digits, (data, result) => (data.years = +result * data.factor)),
yearsDesignator,
[choice(durationMonths, durationWeeks, durationDays)]
[choice(durationMonthsPart, durationWeeksPart, durationDaysPart)]
);
const durationDate = seq(choice(durationYears, durationMonths, durationWeeks, durationDays), [durationTime]);
const durationDate = seq(choice(durationYearsPart, durationMonthsPart, durationWeeksPart, durationDaysPart), [
durationTime
]);
const duration = withSyntaxConstraints(
seq(
withCode([temporalSign], (data, result) => (data.factor = result === '-' || result === '\u2212' ? -1 : 1)),
Expand Down
121 changes: 60 additions & 61 deletions spec/abstractops.html
Original file line number Diff line number Diff line change
Expand Up @@ -1265,19 +1265,10 @@ <h1>ISO 8601 grammar</h1>
DateSpec[+Extended]
DateSpec[~Extended]

TimeHour :::
Hour

TimeMinute :::
MinuteSecond

TimeSecond :::
MinuteSecond
`60`

TimeFraction :::
TemporalDecimalFraction

NormalizedUTCOffset :::
ASCIISign Hour TimeSeparator[+Extended] MinuteSecond

Expand All @@ -1292,9 +1283,6 @@ <h1>ISO 8601 grammar</h1>
[+Z] UTCDesignator
UTCOffset[+SubMinutePrecision]

TimeZoneUTCOffsetName :::
UTCOffset[~SubMinutePrecision]

TZLeadingChar :::
Alpha
`.`
Expand All @@ -1315,7 +1303,7 @@ <h1>ISO 8601 grammar</h1>
TimeZoneIANAName `/` TimeZoneIANANameComponent

TimeZoneIdentifier :::
TimeZoneUTCOffsetName
UTCOffset[~SubMinutePrecision]
TimeZoneIANAName

TimeZoneAnnotation :::
Expand Down Expand Up @@ -1349,9 +1337,9 @@ <h1>ISO 8601 grammar</h1>
Annotation Annotations?

TimeSpec[Extended] :::
TimeHour
TimeHour TimeSeparator[?Extended] TimeMinute
TimeHour TimeSeparator[?Extended] TimeMinute TimeSeparator[?Extended] TimeSecond TimeFraction?
Hour
Hour TimeSeparator[?Extended] MinuteSecond
Hour TimeSeparator[?Extended] MinuteSecond TimeSeparator[?Extended] TimeSecond TemporalDecimalFraction?

Time :::
TimeSpec[+Extended]
Expand All @@ -1375,67 +1363,37 @@ <h1>ISO 8601 grammar</h1>
AnnotatedMonthDay :::
DateSpecMonthDay TimeZoneAnnotation? Annotations?

DurationWholeSeconds :::
DecimalDigits[~Sep]

DurationSecondsFraction :::
TimeFraction

DurationSecondsPart :::
DurationWholeSeconds DurationSecondsFraction? SecondsDesignator

DurationWholeMinutes :::
DecimalDigits[~Sep]

DurationMinutesFraction :::
TimeFraction
DecimalDigits[~Sep] TemporalDecimalFraction? SecondsDesignator

DurationMinutesPart :::
DurationWholeMinutes DurationMinutesFraction MinutesDesignator
DurationWholeMinutes MinutesDesignator DurationSecondsPart?

DurationWholeHours :::
DecimalDigits[~Sep]

DurationHoursFraction :::
TimeFraction
DecimalDigits[~Sep] TemporalDecimalFraction MinutesDesignator
DecimalDigits[~Sep] MinutesDesignator DurationSecondsPart?

DurationHoursPart :::
DurationWholeHours DurationHoursFraction HoursDesignator
DurationWholeHours HoursDesignator DurationMinutesPart
DurationWholeHours HoursDesignator DurationSecondsPart?
DecimalDigits[~Sep] TemporalDecimalFraction HoursDesignator
DecimalDigits[~Sep] HoursDesignator DurationMinutesPart
DecimalDigits[~Sep] HoursDesignator DurationSecondsPart?

DurationTime :::
TimeDesignator DurationHoursPart
TimeDesignator DurationMinutesPart
TimeDesignator DurationSecondsPart

DurationDays :::
DecimalDigits[~Sep]

DurationDaysPart :::
DurationDays DaysDesignator

DurationWeeks :::
DecimalDigits[~Sep]
DecimalDigits[~Sep] DaysDesignator

DurationWeeksPart :::
DurationWeeks WeeksDesignator DurationDaysPart?

DurationMonths :::
DecimalDigits[~Sep]
DecimalDigits[~Sep] WeeksDesignator DurationDaysPart?

DurationMonthsPart :::
DurationMonths MonthsDesignator DurationWeeksPart
DurationMonths MonthsDesignator DurationDaysPart?

DurationYears :::
DecimalDigits[~Sep]
DecimalDigits[~Sep] MonthsDesignator DurationWeeksPart
DecimalDigits[~Sep] MonthsDesignator DurationDaysPart?

DurationYearsPart :::
DurationYears YearsDesignator DurationMonthsPart
DurationYears YearsDesignator DurationWeeksPart
DurationYears YearsDesignator DurationDaysPart?
DecimalDigits[~Sep] YearsDesignator DurationMonthsPart
DecimalDigits[~Sep] YearsDesignator DurationWeeksPart
DecimalDigits[~Sep] YearsDesignator DurationDaysPart?

DurationDate :::
DurationYearsPart DurationTime?
Expand Down Expand Up @@ -1579,7 +1537,7 @@ <h1>
1. If _goal_ is |TemporalMonthDayString| or |TemporalYearMonthString|, _calendar_ is not ~empty~, and the ASCII-lowercase of _calendar_ is not *"iso8601"*, throw a *RangeError* exception.
1. If _parseResult_ is not a Parse Node, throw a *RangeError* exception.
1. NOTE: Applications of StringToNumber below do not lose precision, since each of the parsed values is guaranteed to be a sufficiently short string of decimal digits.
1. Let each of _year_, _month_, _day_, _hour_, _minute_, _second_, and _fSeconds_ be the source text matched by the respective |DateYear|, |DateMonth|, |DateDay|, |TimeHour|, |TimeMinute|, |TimeSecond|, and |TimeFraction| Parse Node contained within _parseResult_, or an empty sequence of code points if not present.
1. Let each of _year_, _month_, _day_, _hour_, _minute_, _second_, and _fSeconds_ be the source text matched by the respective |DateYear|, |DateMonth|, |DateDay|, the first |Hour|, the first |MinuteSecond|, |TimeSecond|, and the first |TemporalDecimalFraction| Parse Node contained within _parseResult_, or an empty sequence of code points if not present.
1. If the first code point of _year_ is U+2212 (MINUS SIGN), replace the first code point with U+002D (HYPHEN-MINUS).
1. Let _yearMV_ be ℝ(StringToNumber(CodePointsToString(_year_))).
1. If _month_ is empty, then
Expand Down Expand Up @@ -1766,7 +1724,48 @@ <h1>
<emu-alg>
1. Let _duration_ be ParseText(StringToCodePoints(_isoString_), |TemporalDurationString|).
1. If _duration_ is a List of errors, throw a *RangeError* exception.
1. Let each of _sign_, _years_, _months_, _weeks_, _days_, _hours_, _fHours_, _minutes_, _fMinutes_, _seconds_, and _fSeconds_ be the source text matched by the respective |TemporalSign|, |DurationYears|, |DurationMonths|, |DurationWeeks|, |DurationDays|, |DurationWholeHours|, |DurationHoursFraction|, |DurationWholeMinutes|, |DurationMinutesFraction|, |DurationWholeSeconds|, and |DurationSecondsFraction| Parse Node contained within _duration_, or an empty sequence of code points if not present.
1. Let _sign_ be the source text matched by the |TemporalSign| Parse Node contained within _duration_, or an empty sequence of code points if not present.
1. If _duration_ contains a |DurationYearsPart| Parse Node, then
1. Let _yearsNode_ be that |DurationYearsPart| Parse Node contained within _duration_.
1. Let _years_ be the source text matched by the |DecimalDigits| Parse Node contained within _yearsNode_.
1. Else,
1. Let _years_ be an empty sequence of code points.
1. If _duration_ contains a |DurationMonthsPart| Parse Node, then
1. Let _monthsNode_ be the |DurationMonthsPart| Parse Node contained within _duration_.
1. Let _months_ be the source text matched by the |DecimalDigits| Parse Node contained within _monthsNode_.
1. Else,
1. Let _months_ be an empty sequence of code points.
1. If _duration_ contains a |DurationWeeksPart| Parse Node, then
1. Let _weeksNode_ be the |DurationWeeksPart| Parse Node contained within _duration_.
1. Let _weeks_ be the source text matched by the |DecimalDigits| Parse Node contained within _weeksNode_.
1. Else,
1. Let _weeks_ be an empty sequence of code points.
1. If _duration_ contains a |DurationDaysPart| Parse Node, then
1. Let _daysNode_ be the |DurationDaysPart| Parse Node contained within _duration_.
1. Let _days_ be the source text matched by the |DecimalDigits| Parse Node contained within _daysNode_.
1. Else,
1. Let _days_ be an empty sequence of code points.
1. If _duration_ contains a |DurationHoursPart| Parse Node, then
1. Let _hoursNode_ be the |DurationHoursPart| Parse Node contained within _duration_.
1. Let _hours_ be the source text matched by the |DecimalDigits| Parse Node contained within _hoursNode_.
1. Let _fHours_ be the source text matched by the |TemporalDecimalFraction| Parse Node contained within _hoursNode_, or an empty sequence of code points if not present.
1. Else,
1. Let _hours_ be an empty sequence of code points.
1. Let _fHours_ be an empty sequence of code points.
1. If _duration_ contains a |DurationMinutesPart| Parse Node, then
1. Let _minutesNode_ be the |DurationMinutesPart| Parse Node contained within _duration_.
1. Let _minutes_ be the source text matched by the |DecimalDigits| Parse Node contained within _minutesNode_.
1. Let _fMinutes_ be the source text matched by the |TemporalDecimalFraction| Parse Node contained within _minutesNode_, or an empty sequence of code points if not present.
1. Else,
1. Let _minutes_ be an empty sequence of code points.
1. Let _fMinutes_ be an empty sequence of code points.
1. If _duration_ contains a |DurationSecondsPart| Parse Node, then
1. Let _secondsNode_ be the |DurationSecondsPart| Parse Node contained within _duration_.
1. Let _seconds_ be the source text matched by the |DecimalDigits| Parse Node contained within _secondsNode_.
1. Let _fSeconds_ be the source text matched by the |TemporalDecimalFraction| Parse Node contained within _secondsNode_, or an empty sequence of code points if not present.
1. Else,
1. Let _seconds_ be an empty sequence of code points.
1. Let _fSeconds_ be an empty sequence of code points.
1. Let _yearsMV_ be ? ToIntegerWithTruncation(CodePointsToString(_years_)).
1. Let _monthsMV_ be ? ToIntegerWithTruncation(CodePointsToString(_months_)).
1. Let _weeksMV_ be ? ToIntegerWithTruncation(CodePointsToString(_weeks_)).
Expand Down
Loading

0 comments on commit 3765bf6

Please sign in to comment.