diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e8490e2..d00cd3cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ - Add `panic`/`Error.panic`. https://github.com/rescript-association/rescript-core/pull/72 - The globally available `null` value now originates from `Nullable` and not `Null`, just like the globally available `undefined` value does. https://github.com/rescript-association/rescript-core/pull/88 - Add `Int.range` and `Int.rangeWithOptions`, https://github.com/rescript-association/rescript-core/pull/52 +- Stricter number parsing in `Float.fromString` and `Int.fromString`, https://github.com/rescript-association/rescript-core/pull/93 ### Documentation diff --git a/src/Core__Float.mjs b/src/Core__Float.mjs index 2d9f7f0b..b62e1e36 100644 --- a/src/Core__Float.mjs +++ b/src/Core__Float.mjs @@ -3,14 +3,14 @@ var Constants = {}; -function fromString(i) { - var i$1 = parseFloat(i); - if (isNaN(i$1)) { - return ; - } else { - return i$1; +var fromString = (function (str) { + if (!str || !str.trim()) { + return undefined; } -} + + let num = +str; + return isNaN(num) ? undefined : num; +}); export { Constants , diff --git a/src/Core__Float.res b/src/Core__Float.res index 8781324d..5cfdd5b1 100644 --- a/src/Core__Float.res +++ b/src/Core__Float.res @@ -27,12 +27,15 @@ module Constants = { @send external toStringWithRadix: (float, ~radix: int) => string = "toString" @send external toLocaleString: float => string = "toLocaleString" -let fromString = i => - switch parseFloat(i) { - | i if isNaN(i) => None - | i => Some(i) +let fromString: string => option = %raw(`function (str) { + if (!str || !str.trim()) { + return undefined; } + let num = +str; + return isNaN(num) ? undefined : num; +}`) + external toInt: float => int = "%intoffloat" external fromInt: int => float = "%identity" diff --git a/src/Core__Int.mjs b/src/Core__Int.mjs index 1ad4aea3..e8f259ea 100644 --- a/src/Core__Int.mjs +++ b/src/Core__Int.mjs @@ -2,14 +2,14 @@ import * as Pervasives from "rescript/lib/es6/pervasives.js"; import * as Core__Array from "./Core__Array.mjs"; +import * as Core__Float from "./Core__Float.mjs"; -function fromString(radix, x) { - var maybeInt = radix !== undefined ? parseInt(x, radix) : parseInt(x); - if (isNaN(maybeInt) || maybeInt > 2147483647 || maybeInt < -2147483648) { - return ; - } else { - return maybeInt | 0; +function fromString(str) { + var num = Core__Float.fromString(str); + if (num !== undefined && num === (num | 0) && isFinite(num)) { + return num; } + } function rangeWithOptions(start, end, options) { diff --git a/src/Core__Int.res b/src/Core__Int.res index a4f0e42e..284a07ce 100644 --- a/src/Core__Int.res +++ b/src/Core__Int.res @@ -19,18 +19,10 @@ module Constants = { external toFloat: int => float = "%identity" external fromFloat: float => int = "%intoffloat" -let fromString = (~radix=?, x) => { - let maybeInt = switch radix { - | Some(radix) => Core__Float.parseIntWithRadix(x, ~radix) - | None => Core__Float.parseInt(x) - } - if Core__Float.isNaN(maybeInt) { - None - } else if maybeInt > Constants.maxValue->toFloat || maybeInt < Constants.minValue->toFloat { - None - } else { - let asInt = fromFloat(maybeInt) - Some(asInt) +let fromString = str => { + switch Core__Float.fromString(str) { + | Some(num) => num === %raw("num | 0") && Core__Float.isFinite(num) ? Obj.magic(num) : None + | None => None } } diff --git a/src/Core__Int.resi b/src/Core__Int.resi index d1a8bcfb..3c260f04 100644 --- a/src/Core__Int.resi +++ b/src/Core__Int.resi @@ -245,18 +245,16 @@ Int.fromFloat(0.9999) == 0 external fromFloat: float => int = "%intoffloat" /** -`fromString(~radix?, str)` return an `option` representing the given value -`str`. `~radix` specifies the radix base to use for the formatted number. +`fromString(str)` return an `option` representing the given value 'str'. ## Examples ```rescript Int.fromString("0") == Some(0) Int.fromString("NaN") == None -Int.fromString(~radix=2, "6") == None ``` */ -let fromString: (~radix: int=?, string) => option +let fromString: string => option /** `mod(n1, n2)` calculates the modulo (remainder after division) of two integers. diff --git a/test/FloatTests.mjs b/test/FloatTests.mjs new file mode 100644 index 00000000..cb4ce0cd --- /dev/null +++ b/test/FloatTests.mjs @@ -0,0 +1,213 @@ +// Generated by ReScript, PLEASE EDIT WITH CARE + +import * as Test from "./Test.mjs"; +import * as Caml_obj from "rescript/lib/es6/caml_obj.js"; +import * as Pervasives from "rescript/lib/es6/pervasives.js"; +import * as Core__Float from "../src/Core__Float.mjs"; + +var eq = Caml_obj.equal; + +Test.run([ + [ + "FloatTests.res", + 5, + 20, + 32 + ], + "fromString" + ], Core__Float.fromString("4.2"), eq, 4.2); + +Test.run([ + [ + "FloatTests.res", + 6, + 20, + 32 + ], + "fromString" + ], Core__Float.fromString("4.2foo"), eq, undefined); + +Test.run([ + [ + "FloatTests.res", + 7, + 20, + 32 + ], + "fromString" + ], Core__Float.fromString("4,2"), eq, undefined); + +Test.run([ + [ + "FloatTests.res", + 8, + 20, + 32 + ], + "fromString" + ], Core__Float.fromString("4 2"), eq, undefined); + +Test.run([ + [ + "FloatTests.res", + 9, + 20, + 32 + ], + "fromString" + ], Core__Float.fromString("4_2"), eq, undefined); + +Test.run([ + [ + "FloatTests.res", + 10, + 20, + 32 + ], + "fromString" + ], Core__Float.fromString("42"), eq, 42); + +Test.run([ + [ + "FloatTests.res", + 11, + 20, + 32 + ], + "fromString" + ], Core__Float.fromString(" 4.2 "), eq, 4.2); + +Test.run([ + [ + "FloatTests.res", + 12, + 20, + 32 + ], + "fromString" + ], Core__Float.fromString(".42"), eq, 0.42); + +Test.run([ + [ + "FloatTests.res", + 13, + 20, + 32 + ], + "fromString" + ], Core__Float.fromString("4.2e1"), eq, 42); + +Test.run([ + [ + "FloatTests.res", + 14, + 20, + 32 + ], + "fromString" + ], Core__Float.fromString("4.2E1"), eq, 42); + +Test.run([ + [ + "FloatTests.res", + 15, + 20, + 32 + ], + "fromString" + ], Core__Float.fromString("4.2e+1"), eq, 42); + +Test.run([ + [ + "FloatTests.res", + 16, + 20, + 32 + ], + "fromString" + ], Core__Float.fromString("4.2e-1"), eq, 0.42); + +Test.run([ + [ + "FloatTests.res", + 17, + 20, + 32 + ], + "fromString" + ], Core__Float.fromString("0xF"), eq, 15); + +Test.run([ + [ + "FloatTests.res", + 18, + 20, + 32 + ], + "fromString" + ], Core__Float.fromString("0777"), eq, 777); + +Test.run([ + [ + "FloatTests.res", + 19, + 20, + 32 + ], + "fromString" + ], Core__Float.fromString("0o777"), eq, 511); + +Test.run([ + [ + "FloatTests.res", + 20, + 20, + 32 + ], + "fromString" + ], Core__Float.fromString("0b101"), eq, 5); + +Test.run([ + [ + "FloatTests.res", + 21, + 20, + 32 + ], + "fromString" + ], Core__Float.fromString("foo"), eq, undefined); + +Test.run([ + [ + "FloatTests.res", + 22, + 20, + 32 + ], + "fromString" + ], Core__Float.fromString("NaN"), eq, undefined); + +Test.run([ + [ + "FloatTests.res", + 23, + 20, + 32 + ], + "fromString" + ], Core__Float.fromString("Infinity"), eq, Pervasives.infinity); + +Test.run([ + [ + "FloatTests.res", + 24, + 20, + 32 + ], + "fromString" + ], Core__Float.fromString("-Infinity"), eq, Pervasives.neg_infinity); + +export { + eq , +} +/* Not a pure module */ diff --git a/test/FloatTests.res b/test/FloatTests.res new file mode 100644 index 00000000..59ae1cab --- /dev/null +++ b/test/FloatTests.res @@ -0,0 +1,24 @@ +open RescriptCore + +let eq = (a, b) => a == b + +Test.run(__POS_OF__("fromString"), Float.fromString("4.2"), eq, Some(4.2)) +Test.run(__POS_OF__("fromString"), Float.fromString("4.2foo"), eq, None) +Test.run(__POS_OF__("fromString"), Float.fromString("4,2"), eq, None) +Test.run(__POS_OF__("fromString"), Float.fromString("4 2"), eq, None) +Test.run(__POS_OF__("fromString"), Float.fromString("4_2"), eq, None) +Test.run(__POS_OF__("fromString"), Float.fromString("42"), eq, Some(42.)) +Test.run(__POS_OF__("fromString"), Float.fromString(" 4.2 "), eq, Some(4.2)) +Test.run(__POS_OF__("fromString"), Float.fromString(".42"), eq, Some(0.42)) +Test.run(__POS_OF__("fromString"), Float.fromString("4.2e1"), eq, Some(42.)) +Test.run(__POS_OF__("fromString"), Float.fromString("4.2E1"), eq, Some(42.)) +Test.run(__POS_OF__("fromString"), Float.fromString("4.2e+1"), eq, Some(42.)) +Test.run(__POS_OF__("fromString"), Float.fromString("4.2e-1"), eq, Some(0.42)) +Test.run(__POS_OF__("fromString"), Float.fromString("0xF"), eq, Some(15.)) +Test.run(__POS_OF__("fromString"), Float.fromString("0777"), eq, Some(777.)) +Test.run(__POS_OF__("fromString"), Float.fromString("0o777"), eq, Some(511.)) +Test.run(__POS_OF__("fromString"), Float.fromString("0b101"), eq, Some(5.)) +Test.run(__POS_OF__("fromString"), Float.fromString("foo"), eq, None) +Test.run(__POS_OF__("fromString"), Float.fromString("NaN"), eq, None) +Test.run(__POS_OF__("fromString"), Float.fromString("Infinity"), eq, Some(infinity)) +Test.run(__POS_OF__("fromString"), Float.fromString("-Infinity"), eq, Some(neg_infinity)) diff --git a/test/IntTests.mjs b/test/IntTests.mjs index 30daefe0..b5c04b98 100644 --- a/test/IntTests.mjs +++ b/test/IntTests.mjs @@ -29,6 +29,206 @@ Test.run([ "IntTests.res", 13, 20, + 32 + ], + "fromString" + ], Core__Int.fromString("4.2"), eq, undefined); + +Test.run([ + [ + "IntTests.res", + 14, + 20, + 32 + ], + "fromString" + ], Core__Int.fromString("4."), eq, 4); + +Test.run([ + [ + "IntTests.res", + 15, + 20, + 32 + ], + "fromString" + ], Core__Int.fromString("4.0"), eq, 4); + +Test.run([ + [ + "IntTests.res", + 16, + 20, + 32 + ], + "fromString" + ], Core__Int.fromString("4.2foo"), eq, undefined); + +Test.run([ + [ + "IntTests.res", + 17, + 20, + 32 + ], + "fromString" + ], Core__Int.fromString("4,2"), eq, undefined); + +Test.run([ + [ + "IntTests.res", + 18, + 20, + 32 + ], + "fromString" + ], Core__Int.fromString("4 2"), eq, undefined); + +Test.run([ + [ + "IntTests.res", + 19, + 20, + 32 + ], + "fromString" + ], Core__Int.fromString("4_2"), eq, undefined); + +Test.run([ + [ + "IntTests.res", + 20, + 20, + 32 + ], + "fromString" + ], Core__Int.fromString("42"), eq, 42); + +Test.run([ + [ + "IntTests.res", + 21, + 20, + 32 + ], + "fromString" + ], Core__Int.fromString(" 42 "), eq, 42); + +Test.run([ + [ + "IntTests.res", + 22, + 20, + 32 + ], + "fromString" + ], Core__Int.fromString("4.2e1"), eq, 42); + +Test.run([ + [ + "IntTests.res", + 23, + 20, + 32 + ], + "fromString" + ], Core__Int.fromString("4.2E1"), eq, 42); + +Test.run([ + [ + "IntTests.res", + 24, + 20, + 32 + ], + "fromString" + ], Core__Int.fromString("4.2e+1"), eq, 42); + +Test.run([ + [ + "IntTests.res", + 25, + 20, + 32 + ], + "fromString" + ], Core__Int.fromString("4.2e-1"), eq, undefined); + +Test.run([ + [ + "IntTests.res", + 26, + 20, + 32 + ], + "fromString" + ], Core__Int.fromString("0xF"), eq, 15); + +Test.run([ + [ + "IntTests.res", + 27, + 20, + 32 + ], + "fromString" + ], Core__Int.fromString("0777"), eq, 777); + +Test.run([ + [ + "IntTests.res", + 28, + 20, + 32 + ], + "fromString" + ], Core__Int.fromString("0o777"), eq, 511); + +Test.run([ + [ + "IntTests.res", + 29, + 20, + 32 + ], + "fromString" + ], Core__Int.fromString("0b101"), eq, 5); + +Test.run([ + [ + "IntTests.res", + 30, + 20, + 32 + ], + "fromString" + ], Core__Int.fromString("foo"), eq, undefined); + +Test.run([ + [ + "IntTests.res", + 31, + 20, + 32 + ], + "fromString" + ], Core__Int.fromString("NaN"), eq, undefined); + +Test.run([ + [ + "IntTests.res", + 32, + 20, + 32 + ], + "fromString" + ], Core__Int.fromString("Infinity"), eq, undefined); + +Test.run([ + [ + "IntTests.res", + 34, + 20, 50 ], "range - positive, increasing" @@ -41,7 +241,7 @@ Test.run([ Test.run([ [ "IntTests.res", - 14, + 35, 20, 50 ], @@ -54,7 +254,7 @@ Test.run([ Test.run([ [ "IntTests.res", - 15, + 36, 20, 51 ], @@ -68,7 +268,7 @@ Test.run([ Test.run([ [ "IntTests.res", - 16, + 37, 20, 42 ], @@ -78,7 +278,7 @@ Test.run([ Test.run([ [ "IntTests.res", - 17, + 38, 20, 50 ], @@ -91,7 +291,7 @@ Test.run([ Test.run([ [ "IntTests.res", - 18, + 39, 20, 50 ], @@ -104,7 +304,7 @@ Test.run([ Test.run([ [ "IntTests.res", - 21, + 42, 13, 62 ], @@ -119,7 +319,7 @@ Test.run([ Test.run([ [ "IntTests.res", - 27, + 48, 13, 62 ], @@ -134,7 +334,7 @@ Test.run([ Test.run([ [ "IntTests.res", - 33, + 54, 13, 62 ], @@ -150,7 +350,7 @@ Test.run([ Test.run([ [ "IntTests.res", - 39, + 60, 13, 62 ], @@ -165,7 +365,7 @@ Test.run([ Test.run([ [ "IntTests.res", - 45, + 66, 13, 62 ], @@ -179,7 +379,7 @@ Test.run([ Test.run([ [ "IntTests.res", - 51, + 72, 13, 54 ], @@ -191,7 +391,7 @@ Test.run([ Test.run([ [ "IntTests.res", - 57, + 78, 13, 63 ], @@ -203,7 +403,7 @@ Test.run([ Test.run([ [ "IntTests.res", - 63, + 84, 13, 62 ], @@ -215,7 +415,7 @@ Test.run([ Test.run([ [ "IntTests.res", - 69, + 90, 13, 63 ], @@ -230,7 +430,7 @@ Test.run([ Test.run([ [ "IntTests.res", - 75, + 96, 13, 63 ], @@ -245,7 +445,7 @@ Test.run([ Test.run([ [ "IntTests.res", - 81, + 102, 13, 63 ], @@ -261,7 +461,7 @@ Test.run([ Test.run([ [ "IntTests.res", - 87, + 108, 13, 63 ], @@ -276,7 +476,7 @@ Test.run([ Test.run([ [ "IntTests.res", - 93, + 114, 13, 73 ], @@ -292,7 +492,7 @@ Test.run([ Test.run([ [ "IntTests.res", - 99, + 120, 13, 73 ], @@ -309,7 +509,7 @@ Test.run([ Test.run([ [ "IntTests.res", - 105, + 126, 13, 73 ], @@ -326,7 +526,7 @@ Test.run([ Test.run([ [ "IntTests.res", - 111, + 132, 13, 73 ], @@ -342,7 +542,7 @@ Test.run([ Test.run([ [ "IntTests.res", - 117, + 138, 13, 73 ], @@ -357,7 +557,7 @@ Test.run([ Test.run([ [ "IntTests.res", - 123, + 144, 13, 65 ], @@ -370,7 +570,7 @@ Test.run([ Test.run([ [ "IntTests.res", - 129, + 150, 13, 74 ], @@ -383,7 +583,7 @@ Test.run([ Test.run([ [ "IntTests.res", - 135, + 156, 13, 73 ], @@ -396,7 +596,7 @@ Test.run([ Test.run([ [ "IntTests.res", - 141, + 162, 13, 74 ], @@ -412,7 +612,7 @@ Test.run([ Test.run([ [ "IntTests.res", - 147, + 168, 13, 74 ], @@ -429,7 +629,7 @@ Test.run([ Test.run([ [ "IntTests.res", - 153, + 174, 13, 74 ], @@ -446,7 +646,7 @@ Test.run([ Test.run([ [ "IntTests.res", - 159, + 180, 13, 74 ], diff --git a/test/IntTests.res b/test/IntTests.res index 373cf8c9..db632263 100644 --- a/test/IntTests.res +++ b/test/IntTests.res @@ -10,6 +10,27 @@ let catch = f => | Exn.Error(err) => err } +Test.run(__POS_OF__("fromString"), Int.fromString("4.2"), eq, None) +Test.run(__POS_OF__("fromString"), Int.fromString("4."), eq, Some(4)) +Test.run(__POS_OF__("fromString"), Int.fromString("4.0"), eq, Some(4)) +Test.run(__POS_OF__("fromString"), Int.fromString("4.2foo"), eq, None) +Test.run(__POS_OF__("fromString"), Int.fromString("4,2"), eq, None) +Test.run(__POS_OF__("fromString"), Int.fromString("4 2"), eq, None) +Test.run(__POS_OF__("fromString"), Int.fromString("4_2"), eq, None) +Test.run(__POS_OF__("fromString"), Int.fromString("42"), eq, Some(42)) +Test.run(__POS_OF__("fromString"), Int.fromString(" 42 "), eq, Some(42)) +Test.run(__POS_OF__("fromString"), Int.fromString("4.2e1"), eq, Some(42)) +Test.run(__POS_OF__("fromString"), Int.fromString("4.2E1"), eq, Some(42)) +Test.run(__POS_OF__("fromString"), Int.fromString("4.2e+1"), eq, Some(42)) +Test.run(__POS_OF__("fromString"), Int.fromString("4.2e-1"), eq, None) +Test.run(__POS_OF__("fromString"), Int.fromString("0xF"), eq, Some(15)) +Test.run(__POS_OF__("fromString"), Int.fromString("0777"), eq, Some(777)) +Test.run(__POS_OF__("fromString"), Int.fromString("0o777"), eq, Some(511)) +Test.run(__POS_OF__("fromString"), Int.fromString("0b101"), eq, Some(5)) +Test.run(__POS_OF__("fromString"), Int.fromString("foo"), eq, None) +Test.run(__POS_OF__("fromString"), Int.fromString("NaN"), eq, None) +Test.run(__POS_OF__("fromString"), Int.fromString("Infinity"), eq, None) + Test.run(__POS_OF__("range - positive, increasing"), Int.range(3, 6), eq, [3, 4, 5]) Test.run(__POS_OF__("range - negative, increasing"), Int.range(-3, -1), eq, [-3, -2]) Test.run(__POS_OF__("range - cross-zero, incresing"), Int.range(-1, 2), eq, [-1, 0, 1]) diff --git a/test/TempTests.mjs b/test/TempTests.mjs index 9807a51a..87cadd66 100644 --- a/test/TempTests.mjs +++ b/test/TempTests.mjs @@ -87,7 +87,7 @@ console.log((10.2).toFixed(2)); console.log((10).toFixed(2)); -console.log(Core__Int.fromString(undefined, "0")); +console.log(Core__Int.fromString("0")); console.log(Core__Float.fromString("0.1")); @@ -320,15 +320,13 @@ console.log({ var Bugfix = {}; -console.log(Core__Int.fromString(undefined, "1231231")); +console.log(Core__Int.fromString("1231231")); -console.log(Core__Int.fromString(undefined, "12.22")); +console.log(Core__Int.fromString("12.22")); -console.log(Core__Int.fromString(undefined, "99999999999999999")); +console.log(Core__Int.fromString("99999999999999999")); -console.log(Core__Int.fromString(undefined, "99999999999999999")); - -console.log(Core__Int.fromString(2, "010101")); +console.log(Core__Int.fromString("99999999999999999")); export { date , diff --git a/test/TempTests.res b/test/TempTests.res index 82784cea..6dabacd2 100644 --- a/test/TempTests.res +++ b/test/TempTests.res @@ -207,4 +207,3 @@ Console.log(Int.fromString("1231231")) Console.log(Int.fromString("12.22")) Console.log(Int.fromString("99999999999999999")) Console.log(Int.fromString("99999999999999999")) -Console.log(Int.fromString(~radix=2, "010101")) diff --git a/test/TestSuite.mjs b/test/TestSuite.mjs index c968bd8f..fed04d2e 100644 --- a/test/TestSuite.mjs +++ b/test/TestSuite.mjs @@ -4,6 +4,7 @@ import * as IntTests from "./IntTests.mjs"; import * as TestTests from "./TestTests.mjs"; import * as ArrayTests from "./ArrayTests.mjs"; import * as ErrorTests from "./ErrorTests.mjs"; +import * as FloatTests from "./FloatTests.mjs"; import * as PromiseTest from "./PromiseTest.mjs"; var bign = TestTests.bign; @@ -26,10 +27,10 @@ var Concurrently = PromiseTest.Concurrently; var panicTest = ErrorTests.panicTest; -var eq = IntTests.eq; - var $$catch = IntTests.$$catch; +var eq = FloatTests.eq; + export { bign , TestError , @@ -41,7 +42,7 @@ export { Catching , Concurrently , panicTest , - eq , $$catch , + eq , } /* IntTests Not a pure module */ diff --git a/test/TestSuite.res b/test/TestSuite.res index 6277bf57..cdc2d3db 100644 --- a/test/TestSuite.res +++ b/test/TestSuite.res @@ -3,3 +3,4 @@ include PromiseTest include ErrorTests include ArrayTests include IntTests +include FloatTests