ISO8601.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465
  1. /*
  2. * Copyright (c) 2021-2022, Linus Groh <linusg@serenityos.org>
  3. * Copyright (c) 2024, Tim Flynn <trflynn89@ladybird.org>
  4. *
  5. * SPDX-License-Identifier: BSD-2-Clause
  6. */
  7. #include <AK/CharacterTypes.h>
  8. #include <AK/GenericLexer.h>
  9. #include <LibJS/Runtime/Temporal/ISO8601.h>
  10. namespace JS::Temporal {
  11. // 13.30 ISO 8601 grammar, https://tc39.es/proposal-temporal/#sec-temporal-iso8601grammar
  12. class ISO8601Parser {
  13. public:
  14. explicit ISO8601Parser(StringView input)
  15. : m_input(input)
  16. , m_state({
  17. .lexer = GenericLexer { input },
  18. .parse_result = {},
  19. })
  20. {
  21. }
  22. [[nodiscard]] GenericLexer const& lexer() const { return m_state.lexer; }
  23. [[nodiscard]] ParseResult const& parse_result() const { return m_state.parse_result; }
  24. enum class Separator {
  25. No,
  26. Yes,
  27. };
  28. // https://tc39.es/proposal-temporal/#prod-TemporalDurationString
  29. [[nodiscard]] bool parse_temporal_duration_string()
  30. {
  31. // TemporalDurationString :
  32. // Duration
  33. return parse_duration();
  34. }
  35. // https://tc39.es/proposal-temporal/#prod-DurationDate
  36. [[nodiscard]] bool parse_duration_date()
  37. {
  38. // DurationDate :
  39. // DurationYearsPart DurationTime[opt]
  40. // DurationMonthsPart DurationTime[opt]
  41. // DurationWeeksPart DurationTime[opt]
  42. // DurationDaysPart DurationTime[opt]
  43. auto success = parse_duration_years_part() || parse_duration_months_part() || parse_duration_weeks_part() || parse_duration_days_part();
  44. if (!success)
  45. return false;
  46. (void)parse_duration_time();
  47. return true;
  48. }
  49. // https://tc39.es/proposal-temporal/#prod-Duration
  50. [[nodiscard]] bool parse_duration()
  51. {
  52. StateTransaction transaction { *this };
  53. // Duration :::
  54. // ASCIISign[opt] DurationDesignator DurationDate
  55. // ASCIISign[opt] DurationDesignator DurationTime
  56. (void)parse_ascii_sign();
  57. if (!parse_duration_designator())
  58. return false;
  59. auto success = parse_duration_date() || parse_duration_time();
  60. if (!success)
  61. return false;
  62. transaction.commit();
  63. return true;
  64. }
  65. // https://tc39.es/proposal-temporal/#prod-DurationYearsPart
  66. [[nodiscard]] bool parse_duration_years_part()
  67. {
  68. StateTransaction transaction { *this };
  69. // DurationYearsPart :
  70. // DecimalDigits[~Sep] YearsDesignator DurationMonthsPart
  71. // DecimalDigits[~Sep] YearsDesignator DurationWeeksPart
  72. // DecimalDigits[~Sep] YearsDesignator DurationDaysPart[opt]
  73. if (!parse_decimal_digits(Separator::No, m_state.parse_result.duration_years))
  74. return false;
  75. if (!parse_years_designator())
  76. return false;
  77. (void)(parse_duration_months_part() || parse_duration_weeks_part() || parse_duration_days_part());
  78. transaction.commit();
  79. return true;
  80. }
  81. // https://tc39.es/proposal-temporal/#prod-DurationMonthsPart
  82. [[nodiscard]] bool parse_duration_months_part()
  83. {
  84. StateTransaction transaction { *this };
  85. // DurationMonthsPart :
  86. // DecimalDigits[~Sep] MonthsDesignator DurationWeeksPart
  87. // DecimalDigits[~Sep] MonthsDesignator DurationDaysPart[opt]
  88. if (!parse_decimal_digits(Separator::No, m_state.parse_result.duration_months))
  89. return false;
  90. if (!parse_months_designator())
  91. return false;
  92. (void)(parse_duration_weeks_part() || parse_duration_days_part());
  93. transaction.commit();
  94. return true;
  95. }
  96. // https://tc39.es/proposal-temporal/#prod-DurationWeeksPart
  97. [[nodiscard]] bool parse_duration_weeks_part()
  98. {
  99. StateTransaction transaction { *this };
  100. // DurationWeeksPart :
  101. // DecimalDigits[~Sep] WeeksDesignator DurationDaysPart[opt]
  102. if (!parse_decimal_digits(Separator::No, m_state.parse_result.duration_weeks))
  103. return false;
  104. if (!parse_weeks_designator())
  105. return false;
  106. (void)parse_duration_days_part();
  107. transaction.commit();
  108. return true;
  109. }
  110. // https://tc39.es/proposal-temporal/#prod-DurationDaysPart
  111. [[nodiscard]] bool parse_duration_days_part()
  112. {
  113. StateTransaction transaction { *this };
  114. // DurationDaysPart :
  115. // DecimalDigits[~Sep] DaysDesignator
  116. if (!parse_decimal_digits(Separator::No, m_state.parse_result.duration_days))
  117. return false;
  118. if (!parse_days_designator())
  119. return false;
  120. transaction.commit();
  121. return true;
  122. }
  123. // https://tc39.es/proposal-temporal/#prod-DurationTime
  124. [[nodiscard]] bool parse_duration_time()
  125. {
  126. StateTransaction transaction { *this };
  127. // DurationTime :
  128. // TimeDesignator DurationHoursPart
  129. // TimeDesignator DurationMinutesPart
  130. // TimeDesignator DurationSecondsPart
  131. if (!parse_time_designator())
  132. return false;
  133. auto success = parse_duration_hours_part() || parse_duration_minutes_part() || parse_duration_seconds_part();
  134. if (!success)
  135. return false;
  136. transaction.commit();
  137. return true;
  138. }
  139. // https://tc39.es/proposal-temporal/#prod-DurationHoursPart
  140. [[nodiscard]] bool parse_duration_hours_part()
  141. {
  142. StateTransaction transaction { *this };
  143. // DurationHoursPart :
  144. // DecimalDigits[~Sep] TemporalDecimalFraction HoursDesignator
  145. // DecimalDigits[~Sep] HoursDesignator DurationMinutesPart
  146. // DecimalDigits[~Sep] HoursDesignator DurationSecondsPart[opt]
  147. if (!parse_decimal_digits(Separator::No, m_state.parse_result.duration_hours))
  148. return false;
  149. auto is_fractional = parse_temporal_decimal_fraction(m_state.parse_result.duration_hours_fraction);
  150. if (!parse_hours_designator())
  151. return false;
  152. if (!is_fractional)
  153. (void)(parse_duration_minutes_part() || parse_duration_seconds_part());
  154. transaction.commit();
  155. return true;
  156. }
  157. // https://tc39.es/proposal-temporal/#prod-DurationMinutesPart
  158. [[nodiscard]] bool parse_duration_minutes_part()
  159. {
  160. StateTransaction transaction { *this };
  161. // DurationMinutesPart :
  162. // DecimalDigits[~Sep] TemporalDecimalFraction MinutesDesignator
  163. // DecimalDigits[~Sep] MinutesDesignator DurationSecondsPart[opt]
  164. if (!parse_decimal_digits(Separator::No, m_state.parse_result.duration_minutes))
  165. return false;
  166. auto is_fractional = parse_temporal_decimal_fraction(m_state.parse_result.duration_minutes_fraction);
  167. if (!parse_minutes_designator())
  168. return false;
  169. if (!is_fractional)
  170. (void)parse_duration_seconds_part();
  171. transaction.commit();
  172. return true;
  173. }
  174. // https://tc39.es/proposal-temporal/#prod-DurationSecondsPart
  175. [[nodiscard]] bool parse_duration_seconds_part()
  176. {
  177. StateTransaction transaction { *this };
  178. // DurationSecondsPart :
  179. // DecimalDigits[~Sep] TemporalDecimalFraction[opt] SecondsDesignator
  180. if (!parse_decimal_digits(Separator::No, m_state.parse_result.duration_seconds))
  181. return false;
  182. (void)parse_temporal_decimal_fraction(m_state.parse_result.duration_seconds_fraction);
  183. if (!parse_seconds_designator())
  184. return false;
  185. transaction.commit();
  186. return true;
  187. }
  188. // https://tc39.es/ecma262/#prod-DecimalDigit
  189. [[nodiscard]] bool parse_decimal_digit()
  190. {
  191. // DecimalDigit : one of
  192. // 0 1 2 3 4 5 6 7 8 9
  193. if (m_state.lexer.next_is(is_ascii_digit)) {
  194. m_state.lexer.consume();
  195. return true;
  196. }
  197. return false;
  198. }
  199. // https://tc39.es/ecma262/#prod-DecimalDigits
  200. [[nodiscard]] bool parse_decimal_digits(Separator separator, Optional<StringView>& result)
  201. {
  202. StateTransaction transaction { *this };
  203. // FIXME: Implement [+Sep] if it's ever needed.
  204. VERIFY(separator == Separator::No);
  205. // DecimalDigits[Sep] ::
  206. // DecimalDigit
  207. // DecimalDigits[?Sep] DecimalDigit
  208. // [+Sep] DecimalDigits[+Sep] NumericLiteralSeparator DecimalDigit
  209. if (!parse_decimal_digit())
  210. return {};
  211. while (parse_decimal_digit())
  212. ;
  213. result = transaction.parsed_string_view();
  214. transaction.commit();
  215. return true;
  216. }
  217. // https://tc39.es/ecma262/#prod-TemporalDecimalFraction
  218. [[nodiscard]] bool parse_temporal_decimal_fraction(Optional<StringView>& result)
  219. {
  220. StateTransaction transaction { *this };
  221. // TemporalDecimalFraction :::
  222. // TemporalDecimalSeparator DecimalDigit
  223. // TemporalDecimalSeparator DecimalDigit DecimalDigit
  224. // TemporalDecimalSeparator DecimalDigit DecimalDigit DecimalDigit
  225. // TemporalDecimalSeparator DecimalDigit DecimalDigit DecimalDigit DecimalDigit
  226. // TemporalDecimalSeparator DecimalDigit DecimalDigit DecimalDigit DecimalDigit DecimalDigit
  227. // TemporalDecimalSeparator DecimalDigit DecimalDigit DecimalDigit DecimalDigit DecimalDigit DecimalDigit
  228. // TemporalDecimalSeparator DecimalDigit DecimalDigit DecimalDigit DecimalDigit DecimalDigit DecimalDigit DecimalDigit
  229. // TemporalDecimalSeparator DecimalDigit DecimalDigit DecimalDigit DecimalDigit DecimalDigit DecimalDigit DecimalDigit DecimalDigit
  230. // TemporalDecimalSeparator DecimalDigit DecimalDigit DecimalDigit DecimalDigit DecimalDigit DecimalDigit DecimalDigit DecimalDigit DecimalDigit
  231. if (!parse_temporal_decimal_separator())
  232. return false;
  233. if (!parse_decimal_digit())
  234. return false;
  235. for (size_t i = 0; i < 8; ++i) {
  236. if (!parse_decimal_digit())
  237. break;
  238. }
  239. result = transaction.parsed_string_view();
  240. transaction.commit();
  241. return true;
  242. }
  243. // https://tc39.es/ecma262/#prod-ASCIISign
  244. [[nodiscard]] bool parse_ascii_sign()
  245. {
  246. StateTransaction transaction { *this };
  247. // ASCIISign : one of
  248. // + -
  249. if (!m_state.lexer.next_is(is_any_of("+-"sv)))
  250. return false;
  251. m_state.parse_result.sign = m_state.lexer.consume();
  252. transaction.commit();
  253. return true;
  254. }
  255. // https://tc39.es/ecma262/#prod-TemporalDecimalSeparator
  256. [[nodiscard]] bool parse_temporal_decimal_separator()
  257. {
  258. // TemporalDecimalSeparator ::: one of
  259. // . ,
  260. return m_state.lexer.consume_specific('.') || m_state.lexer.consume_specific(',');
  261. }
  262. // https://tc39.es/proposal-temporal/#prod-DurationDesignator
  263. [[nodiscard]] bool parse_duration_designator()
  264. {
  265. // DurationDesignator : one of
  266. // P p
  267. return m_state.lexer.consume_specific('P') || m_state.lexer.consume_specific('p');
  268. }
  269. // https://tc39.es/proposal-temporal/#prod-TimeDesignator
  270. [[nodiscard]] bool parse_time_designator()
  271. {
  272. // TimeDesignator : one of
  273. // T t
  274. return m_state.lexer.consume_specific('T') || m_state.lexer.consume_specific('t');
  275. }
  276. // https://tc39.es/proposal-temporal/#prod-YearsDesignator
  277. [[nodiscard]] bool parse_years_designator()
  278. {
  279. // YearsDesignator : one of
  280. // Y y
  281. return m_state.lexer.consume_specific('Y') || m_state.lexer.consume_specific('y');
  282. }
  283. // https://tc39.es/proposal-temporal/#prod-MonthsDesignator
  284. [[nodiscard]] bool parse_months_designator()
  285. {
  286. // MonthsDesignator : one of
  287. // M m
  288. return m_state.lexer.consume_specific('M') || m_state.lexer.consume_specific('m');
  289. }
  290. // https://tc39.es/proposal-temporal/#prod-WeeksDesignator
  291. [[nodiscard]] bool parse_weeks_designator()
  292. {
  293. // WeeksDesignator : one of
  294. // W w
  295. return m_state.lexer.consume_specific('W') || m_state.lexer.consume_specific('w');
  296. }
  297. // https://tc39.es/proposal-temporal/#prod-DaysDesignator
  298. [[nodiscard]] bool parse_days_designator()
  299. {
  300. // DaysDesignator : one of
  301. // D d
  302. return m_state.lexer.consume_specific('D') || m_state.lexer.consume_specific('d');
  303. }
  304. // https://tc39.es/proposal-temporal/#prod-HoursDesignator
  305. [[nodiscard]] bool parse_hours_designator()
  306. {
  307. // HoursDesignator : one of
  308. // H h
  309. return m_state.lexer.consume_specific('H') || m_state.lexer.consume_specific('h');
  310. }
  311. // https://tc39.es/proposal-temporal/#prod-MinutesDesignator
  312. [[nodiscard]] bool parse_minutes_designator()
  313. {
  314. // MinutesDesignator : one of
  315. // M m
  316. return m_state.lexer.consume_specific('M') || m_state.lexer.consume_specific('m');
  317. }
  318. // https://tc39.es/proposal-temporal/#prod-SecondsDesignator
  319. [[nodiscard]] bool parse_seconds_designator()
  320. {
  321. // SecondsDesignator : one of
  322. // S s
  323. return m_state.lexer.consume_specific('S') || m_state.lexer.consume_specific('s');
  324. }
  325. private:
  326. struct State {
  327. GenericLexer lexer;
  328. ParseResult parse_result;
  329. };
  330. struct StateTransaction {
  331. explicit StateTransaction(ISO8601Parser& parser)
  332. : m_parser(parser)
  333. , m_saved_state(parser.m_state)
  334. , m_start_index(parser.m_state.lexer.tell())
  335. {
  336. }
  337. ~StateTransaction()
  338. {
  339. if (!m_commit)
  340. m_parser.m_state = move(m_saved_state);
  341. }
  342. void commit() { m_commit = true; }
  343. StringView parsed_string_view() const
  344. {
  345. return m_parser.m_input.substring_view(m_start_index, m_parser.m_state.lexer.tell() - m_start_index);
  346. }
  347. private:
  348. ISO8601Parser& m_parser;
  349. State m_saved_state;
  350. size_t m_start_index { 0 };
  351. bool m_commit { false };
  352. };
  353. StringView m_input;
  354. State m_state;
  355. };
  356. #define JS_ENUMERATE_ISO8601_PRODUCTION_PARSERS \
  357. __JS_ENUMERATE(TemporalDurationString, parse_temporal_duration_string)
  358. Optional<ParseResult> parse_iso8601(Production production, StringView input)
  359. {
  360. ISO8601Parser parser { input };
  361. switch (production) {
  362. #define __JS_ENUMERATE(ProductionName, parse_production) \
  363. case Production::ProductionName: \
  364. if (!parser.parse_production()) \
  365. return {}; \
  366. break;
  367. JS_ENUMERATE_ISO8601_PRODUCTION_PARSERS
  368. #undef __JS_ENUMERATE
  369. default:
  370. VERIFY_NOT_REACHED();
  371. }
  372. // If we parsed successfully but didn't reach the end, the string doesn't match the given production.
  373. if (!parser.lexer().is_eof())
  374. return {};
  375. return parser.parse_result();
  376. }
  377. }