Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stricter number parsing in Float.fromString and Int.fromString #93

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
14 changes: 7 additions & 7 deletions src/Core__Float.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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 ,
Expand Down
11 changes: 7 additions & 4 deletions src/Core__Float.res
Original file line number Diff line number Diff line change
Expand Up @@ -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<float> = %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"

Expand Down
12 changes: 6 additions & 6 deletions src/Core__Int.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
16 changes: 4 additions & 12 deletions src/Core__Int.res
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}

Expand Down
6 changes: 2 additions & 4 deletions src/Core__Int.resi
Original file line number Diff line number Diff line change
Expand Up @@ -245,18 +245,16 @@ Int.fromFloat(0.9999) == 0
external fromFloat: float => int = "%intoffloat"

/**
`fromString(~radix?, str)` return an `option<int>` representing the given value
`str`. `~radix` specifies the radix base to use for the formatted number.
`fromString(str)` return an `option<int>` 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<int>
let fromString: string => option<int>

/**
`mod(n1, n2)` calculates the modulo (remainder after division) of two integers.
Expand Down
213 changes: 213 additions & 0 deletions test/FloatTests.mjs
Original file line number Diff line number Diff line change
@@ -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 */
24 changes: 24 additions & 0 deletions test/FloatTests.res
Original file line number Diff line number Diff line change
@@ -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))
Loading