mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 00:57:45 +00:00
LibJS: Rewrite parseInt to be more specification-compliant
This commit is contained in:
parent
877ae85017
commit
de2f5bb5a7
1 changed files with 45 additions and 15 deletions
|
@ -387,37 +387,63 @@ JS_DEFINE_NATIVE_FUNCTION(GlobalObject::parse_float)
|
||||||
// 19.2.5 parseInt ( string, radix ), https://tc39.es/ecma262/#sec-parseint-string-radix
|
// 19.2.5 parseInt ( string, radix ), https://tc39.es/ecma262/#sec-parseint-string-radix
|
||||||
JS_DEFINE_NATIVE_FUNCTION(GlobalObject::parse_int)
|
JS_DEFINE_NATIVE_FUNCTION(GlobalObject::parse_int)
|
||||||
{
|
{
|
||||||
|
// 1. Let inputString be ? ToString(string).
|
||||||
auto input_string = TRY(vm.argument(0).to_string(global_object));
|
auto input_string = TRY(vm.argument(0).to_string(global_object));
|
||||||
|
|
||||||
// FIXME: There's a bunch of unnecessary string copying here.
|
// 2. Let S be ! TrimString(inputString, start).
|
||||||
double sign = 1;
|
auto string = MUST(trim_string(global_object, js_string(vm, input_string), TrimMode::Left));
|
||||||
auto s = input_string.trim_whitespace(TrimMode::Left);
|
|
||||||
if (!s.is_empty() && s[0] == '-')
|
|
||||||
sign = -1;
|
|
||||||
if (!s.is_empty() && (s[0] == '+' || s[0] == '-'))
|
|
||||||
s = s.substring(1, s.length() - 1);
|
|
||||||
|
|
||||||
|
// 3. Let sign be 1.
|
||||||
|
auto sign = 1;
|
||||||
|
|
||||||
|
// 4. If S is not empty and the first code unit of S is the code unit 0x002D (HYPHEN-MINUS), set sign to -1.
|
||||||
|
if (!string.is_empty() && string[0] == 0x2D)
|
||||||
|
sign = -1;
|
||||||
|
// 5. If S is not empty and the first code unit of S is the code unit 0x002B (PLUS SIGN) or the code unit 0x002D (HYPHEN-MINUS), remove the first code unit from S.
|
||||||
|
auto trimmed_view = string.view();
|
||||||
|
if (!string.is_empty() && (string[0] == 0x2B || string[0] == 0x2D))
|
||||||
|
trimmed_view = trimmed_view.substring_view(1);
|
||||||
|
|
||||||
|
// 6. Let R be ℝ(? ToInt32(radix)).
|
||||||
auto radix = TRY(vm.argument(1).to_i32(global_object));
|
auto radix = TRY(vm.argument(1).to_i32(global_object));
|
||||||
|
|
||||||
bool strip_prefix = true;
|
// 7. Let stripPrefix be true.
|
||||||
|
auto strip_prefix = true;
|
||||||
|
|
||||||
|
// 8. If R ≠ 0, then
|
||||||
if (radix != 0) {
|
if (radix != 0) {
|
||||||
|
// a. If R < 2 or R > 36, return NaN.
|
||||||
if (radix < 2 || radix > 36)
|
if (radix < 2 || radix > 36)
|
||||||
return js_nan();
|
return js_nan();
|
||||||
|
|
||||||
|
// b. If R ≠ 16, set stripPrefix to false.
|
||||||
if (radix != 16)
|
if (radix != 16)
|
||||||
strip_prefix = false;
|
strip_prefix = false;
|
||||||
} else {
|
}
|
||||||
|
// 9. Else,
|
||||||
|
else {
|
||||||
|
// a. Set R to 10.
|
||||||
radix = 10;
|
radix = 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 10. If stripPrefix is true, then
|
||||||
if (strip_prefix) {
|
if (strip_prefix) {
|
||||||
if (s.length() >= 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
|
// a. If the length of S is at least 2 and the first two code units of S are either "0x" or "0X", then
|
||||||
s = s.substring(2, s.length() - 2);
|
if (trimmed_view.length() >= 2 && trimmed_view.substring_view(0, 2).equals_ignoring_case("0x"sv)) {
|
||||||
|
// i. Remove the first two code units from S.
|
||||||
|
trimmed_view = trimmed_view.substring_view(2);
|
||||||
|
|
||||||
|
// ii. Set R to 16.
|
||||||
radix = 16;
|
radix = 16;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto parse_digit = [&](u32 code_point, i32 radix) -> Optional<i32> {
|
// 11. If S contains a code unit that is not a radix-R digit, let end be the index within S of the first such code unit; otherwise, let end be the length of S.
|
||||||
if (!is_ascii_alphanumeric(code_point) || radix <= 0)
|
// 12. Let Z be the substring of S from 0 to end.
|
||||||
|
// 13. If Z is empty, return NaN.
|
||||||
|
// 14. Let mathInt be the integer value that is represented by Z in radix-R notation, using the letters A-Z and a-z for digits with values 10 through 35. (However, if R is 10 and Z contains more than 20 significant digits, every significant digit after the 20th may be replaced by a 0 digit, at the option of the implementation; and if R is not 2, 4, 8, 10, 16, or 32, then mathInt may be an implementation-approximated integer representing the integer value denoted by Z in radix-R notation.)
|
||||||
|
auto parse_digit = [&](u32 code_point) -> Optional<u32> {
|
||||||
|
if (!is_ascii_alphanumeric(code_point))
|
||||||
return {};
|
return {};
|
||||||
auto digit = parse_ascii_base36_digit(code_point);
|
auto digit = parse_ascii_base36_digit(code_point);
|
||||||
if (digit >= (u32)radix)
|
if (digit >= (u32)radix)
|
||||||
|
@ -427,8 +453,8 @@ JS_DEFINE_NATIVE_FUNCTION(GlobalObject::parse_int)
|
||||||
|
|
||||||
bool had_digits = false;
|
bool had_digits = false;
|
||||||
double number = 0;
|
double number = 0;
|
||||||
for (auto code_point : Utf8View(s)) {
|
for (auto code_point : Utf8View(trimmed_view)) {
|
||||||
auto digit = parse_digit(code_point, radix);
|
auto digit = parse_digit(code_point);
|
||||||
if (!digit.has_value())
|
if (!digit.has_value())
|
||||||
break;
|
break;
|
||||||
had_digits = true;
|
had_digits = true;
|
||||||
|
@ -439,6 +465,10 @@ JS_DEFINE_NATIVE_FUNCTION(GlobalObject::parse_int)
|
||||||
if (!had_digits)
|
if (!had_digits)
|
||||||
return js_nan();
|
return js_nan();
|
||||||
|
|
||||||
|
// 15. If mathInt = 0, then
|
||||||
|
// a. If sign = -1, return -0𝔽.
|
||||||
|
// b. Return +0𝔽.
|
||||||
|
// 16. Return 𝔽(sign × mathInt).
|
||||||
return Value(sign * number);
|
return Value(sign * number);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue