Numbers.cpp 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. /*
  2. * Copyright (c) 2023, Jonatan Klemets <jonatan.r.klemets@gmail.com>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/GenericLexer.h>
  7. #include <LibWeb/HTML/Numbers.h>
  8. #include <LibWeb/Infra/CharacterTypes.h>
  9. #include <math.h>
  10. namespace Web::HTML {
  11. // https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#rules-for-parsing-integers
  12. Optional<i32> parse_integer(StringView string)
  13. {
  14. // 1. Let input be the string being parsed.
  15. // 2. Let position be a pointer into input, initially pointing at the start of the string.
  16. GenericLexer lexer { string };
  17. // 3. Let sign have the value "positive".
  18. // NOTE: Skipped, see comment on step 6.
  19. // 4. Skip ASCII whitespace within input given position.
  20. lexer.ignore_while(Web::Infra::is_ascii_whitespace);
  21. // 5. If position is past the end of input, return an error.
  22. if (lexer.is_eof()) {
  23. return {};
  24. }
  25. // 6. If the character indicated by position (the first character) is a U+002D HYPHEN-MINUS character (-):
  26. //
  27. // If we parse a signed integer, then we include the sign character (if present) in the collect step
  28. // (step 8) and lean on `AK::StringUtils::convert_to_int` to handle it for us.
  29. size_t start_index = lexer.tell();
  30. if (lexer.peek() == '-' || lexer.peek() == '+') {
  31. lexer.consume();
  32. }
  33. // 7. If the character indicated by position is not an ASCII digit, then return an error.
  34. if (!lexer.next_is(is_ascii_digit)) {
  35. return {};
  36. }
  37. // 8. Collect a sequence of code points that are ASCII digits from input given position, and interpret the resulting sequence as a base-ten integer. Let value be that integer.
  38. lexer.consume_while(is_ascii_digit);
  39. size_t end_index = lexer.tell();
  40. auto digits = lexer.input().substring_view(start_index, end_index - start_index);
  41. auto optional_value = AK::StringUtils::convert_to_int<i32>(digits);
  42. // 9. If sign is "positive", return value, otherwise return the result of subtracting value from zero.
  43. // NOTE: Skipped, see comment on step 6.
  44. return optional_value;
  45. }
  46. // https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#rules-for-parsing-non-negative-integers
  47. Optional<u32> parse_non_negative_integer(StringView string)
  48. {
  49. // 1. Let input be the string being parsed.
  50. // 2. Let value be the result of parsing input using the rules for parsing integers.
  51. //
  52. // NOTE: Because we call `parse_integer`, we parse all integers as signed. If we need the extra
  53. // size that an unsigned integer offers, then this would need to be improved. That said,
  54. // I don't think we need to support such large integers at the moment.
  55. auto optional_value = parse_integer(string);
  56. // 3. If value is an error, return an error.
  57. if (!optional_value.has_value()) {
  58. return {};
  59. }
  60. // 4. If value is less than zero, return an error.
  61. if (optional_value.value() < 0) {
  62. return {};
  63. }
  64. // 5. Return value.
  65. return static_cast<u32>(optional_value.value());
  66. }
  67. // https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#rules-for-parsing-floating-point-number-values
  68. Optional<double> parse_floating_point_number(StringView string)
  69. {
  70. // FIXME: Implement spec compliant floating point number parsing
  71. auto maybe_double = string.to_number<double>(TrimWhitespace::Yes);
  72. if (!maybe_double.has_value())
  73. return {};
  74. if (!isfinite(maybe_double.value()))
  75. return {};
  76. return maybe_double.value();
  77. }
  78. // https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#valid-floating-point-number
  79. bool is_valid_floating_point_number(StringView string)
  80. {
  81. GenericLexer lexer { string };
  82. // 1. Optionally, a U+002D HYPHEN-MINUS character (-).
  83. lexer.consume_specific('-');
  84. // 2. One or both of the following, in the given order:
  85. // 2.1. A series of one or more ASCII digits.
  86. bool has_leading_digits = !lexer.consume_while(is_ascii_digit).is_empty();
  87. // 2.2. Both of the following, in the given order:
  88. // 2.2.1. A single U+002E FULL STOP character (.).
  89. if (lexer.consume_specific('.')) {
  90. // 2.2.2. A series of one or more ASCII digits.
  91. if (lexer.consume_while(is_ascii_digit).is_empty())
  92. return false;
  93. } else if (!has_leading_digits) {
  94. // Doesn’t begin with digits, doesn’t begin with a full stop followed by digits.
  95. return false;
  96. }
  97. // 3. Optionally:
  98. // 3.1. Either a U+0065 LATIN SMALL LETTER E character (e) or a U+0045 LATIN CAPITAL
  99. // LETTER E character (E).
  100. if (lexer.consume_specific('e') || lexer.consume_specific('E')) {
  101. // 3.2. Optionally, a U+002D HYPHEN-MINUS character (-) or U+002B PLUS SIGN
  102. // character (+).
  103. lexer.consume_specific('-') || lexer.consume_specific('+');
  104. // 3.3. A series of one or more ASCII digits.
  105. if (lexer.consume_while(is_ascii_digit).is_empty())
  106. return false;
  107. }
  108. return lexer.tell_remaining() == 0;
  109. }
  110. WebIDL::ExceptionOr<String> convert_non_negative_integer_to_string(JS::Realm& realm, WebIDL::Long value)
  111. {
  112. if (value < 0)
  113. return WebIDL::IndexSizeError::create(realm, "The attribute is limited to only non-negative numbers"_string);
  114. return String::number(value);
  115. }
  116. }