Skip to content

Commit 5120b43

Browse files
committed
[Dart] Fix tests for round, sign, truncate, log, log2, log10, pow, DivRem, Min, Max, Clamp, MinMagnitude, MaxMagnitude, cosh, sinh, tanh, and float Parse
1 parent b3c38ab commit 5120b43

12 files changed

Lines changed: 1064 additions & 84 deletions

File tree

src/Fable.Cli/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Fixed
1111

12+
* [Dart] Fix tests for `round`, `sign`, `truncate`, `log`, `log2`, `log10`, `pow`, `DivRem`, `Min`, `Max`, `Clamp`, `MinMagnitude`, `MaxMagnitude`, `cosh`, `sinh`, `tanh`, and float `Parse` (by @ncave)
1213
* [Beam] Fix `System.String.Concat` with 4+ arguments not being supported (by @dbrattli)
1314
* [TS/Python] Fix invalid `this` argument type in structs (#4453) (by @ncave)
1415
* [JS/TS] Fix `N` format specifier (`ToString("N0")`, `String.Format("{0:N0}", ...)`) producing a trailing dot when precision is 0 (fix #2582) (by @MangelMaxime)

src/Fable.Compiler/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Fixed
1111

12+
* [Dart] Fix tests for `round`, `sign`, `truncate`, `log`, `log2`, `log10`, `pow`, `DivRem`, `Min`, `Max`, `Clamp`, `MinMagnitude`, `MaxMagnitude`, `cosh`, `sinh`, `tanh`, and float `Parse` (by @ncave)
1213
* [Beam] Fix `System.String.Concat` with 4+ arguments not being supported (by @dbrattli)
1314
* [TS/Python] Fix invalid `this` argument type in structs (#4453) (by @ncave)
1415
* [JS/TS] Fix `N` format specifier (`ToString("N0")`, `String.Format("{0:N0}", ...)`) producing a trailing dot when precision is 0 (fix #2582) (by @MangelMaxime)

src/Fable.Transforms/Dart/DartPrinter.fs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -430,8 +430,11 @@ module PrinterExtensions =
430430
| DoubleLiteral value ->
431431
let value =
432432
match value.ToString(System.Globalization.CultureInfo.InvariantCulture) with
433-
| "" -> "double.infinity"
434-
| "-∞" -> "-double.infinity"
433+
| ""
434+
| "Infinity" -> "double.infinity"
435+
| "-∞"
436+
| "-Infinity" -> "double.negativeInfinity"
437+
| "NaN" -> "double.nan"
435438
| value when not (value.Contains(".")) -> value + ".0"
436439
| value -> value
437440

src/Fable.Transforms/Dart/Replacements.fs

Lines changed: 50 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,7 @@ let applyOp (com: ICompiler) (ctx: Context) r t opName (args: Expr list) =
303303
let truncateUnsigned operation = // see #1550
304304
match t with
305305
| Number(UInt32, _) -> Operation(Binary(BinaryShiftRightZeroFill, operation, makeIntConst 0), Tags.empty, t, r)
306+
| Number(UInt64, _) -> Helper.LibCall(com, "Util", "toUInt64", t, [ operation ], ?loc = r)
306307
| _ -> operation
307308

308309
let logicOp op left right =
@@ -326,6 +327,8 @@ let applyOp (com: ICompiler) (ctx: Context) r t opName (args: Expr list) =
326327
| Operators.rightShift, [ left; right ] ->
327328
match argTypes with
328329
| Number(UInt32, _) :: _ -> binOp BinaryShiftRightZeroFill left right // See #646
330+
| Number(UInt64, _) :: _ ->
331+
Helper.LibCall(com, "Util", "rightShiftUnsigned64", t, [ left; right ], argTypes, ?loc = r)
329332
| _ -> binOp BinaryShiftRightSignPropagating left right
330333
| Operators.bitwiseAnd, [ left; right ] -> binOp BinaryAndBitwise left right |> truncateUnsigned
331334
| Operators.bitwiseOr, [ left; right ] -> binOp BinaryOrBitwise left right |> truncateUnsigned
@@ -334,13 +337,12 @@ let applyOp (com: ICompiler) (ctx: Context) r t opName (args: Expr list) =
334337
| Operators.booleanOr, [ left; right ] -> logicOp LogicalOr left right
335338
| Operators.logicalNot, [ operand ] -> unOp UnaryNotBitwise operand |> truncateUnsigned
336339
| Operators.unaryNegation, [ operand ] ->
337-
// TODO: Check for min value, see "Unary negation with integer MinValue works" test
338-
unOp UnaryMinus operand
339-
// match argTypes with
340-
// | Number(Int8,_)::_ -> Helper.LibCall(com, "Int32", "op_UnaryNegation_Int8", t, args, ?loc=r)
341-
// | Number(Int16,_)::_ -> Helper.LibCall(com, "Int32", "op_UnaryNegation_Int16", t, args, ?loc=r)
342-
// | Number(Int32,_)::_ -> Helper.LibCall(com, "Int32", "op_UnaryNegation_Int32", t, args, ?loc=r)
343-
// | _ -> unOp UnaryMinus operand
340+
match argTypes with
341+
| Number(Int8, _) :: _ -> Helper.LibCall(com, "Util", "negateInt8", t, args, ?loc = r)
342+
| Number(Int16, _) :: _ -> Helper.LibCall(com, "Util", "negateInt16", t, args, ?loc = r)
343+
| Number(Int32, _) :: _ -> Helper.LibCall(com, "Util", "negateInt32", t, args, ?loc = r)
344+
| Number(Int64, _) :: _ -> Helper.LibCall(com, "Util", "negateInt64", t, args, ?loc = r)
345+
| _ -> unOp UnaryMinus operand
344346
| Operators.unaryPlus, [ operand ] -> unOp UnaryPlus operand
345347
| _ ->
346348
$"Operator %s{opName} not found in %A{argTypes}"
@@ -944,10 +946,14 @@ let defaultValue com ctx r t defValue option =
944946
|> Some
945947

946948
let operators (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr option) (args: Expr list) =
947-
let math r t (args: Expr list) argTypes genArgs methName =
949+
let math r t (args: Expr list) argTypes methName =
948950
let meth = Naming.lowerFirst methName
949951

950-
Helper.ImportedCall("dart:math", meth, t, args, argTypes, genArgs = genArgs, ?loc = r)
952+
let call = Helper.ImportedCall("dart:math", meth, t, args, argTypes, ?loc = r)
953+
954+
match meth, t with
955+
| "pow", Number((Float32 | Float64), _) -> Helper.InstanceCall(call, "toDouble", t, [], ?loc = r)
956+
| _ -> call
951957

952958
match i.CompiledName, args with
953959
| ("DefaultArg" | "DefaultValueArg"), [ option; defValue ] -> defaultValue com ctx r t defValue option
@@ -1037,7 +1043,7 @@ let operators (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr o
10371043
)
10381044
|> Some
10391045
| CustomOp com ctx r t "Pow" args e -> Some e
1040-
| _ -> math r t args i.SignatureArgTypes i.GenericArgs "pow" |> Some
1046+
| _ -> math r t args i.SignatureArgTypes "pow" |> Some
10411047
| ("Ceiling" | "Floor" as meth), [ arg ] ->
10421048
let meth = Naming.lowerFirst meth
10431049

@@ -1065,11 +1071,14 @@ let operators (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr o
10651071
Helper.InstanceCall(arg, meth, t, [], ?loc = r) |> Some
10661072
| "Log", [ arg1; arg2 ] ->
10671073
// "Math.log($0) / Math.log($1)"
1068-
let dividend = math None t [ arg1 ] [] (List.take 1 i.SignatureArgTypes) "log"
1074+
let dividend = math None t [ arg1 ] (List.take 1 i.SignatureArgTypes) "log"
10691075

1070-
let divisor = math None t [ arg2 ] [] (List.skip 1 i.SignatureArgTypes) "log"
1076+
let divisor = math None t [ arg2 ] (List.skip 1 i.SignatureArgTypes) "log"
10711077

10721078
makeBinOp r t dividend divisor BinaryDivide |> Some
1079+
| ("Cosh" | "Sinh" | "Tanh" | "Log2" | "Log10" as meth), _ ->
1080+
Helper.LibCall(com, "Util", Naming.lowerFirst meth, t, args, i.SignatureArgTypes, ?loc = r)
1081+
|> Some
10731082
| "Abs", [ arg ] ->
10741083
match arg with
10751084
| ExprType(Number(BigInt | Decimal as kind, _)) ->
@@ -1096,17 +1105,13 @@ let operators (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr o
10961105
| "Atan", _
10971106
| "Atan2", _
10981107
| "Cos", _
1099-
| "Cosh", _
11001108
| "Exp", _
11011109
| "Log", _
1102-
| "Log10", _
11031110
| "Sin", _
1104-
| "Sinh", _
11051111
| "Sqrt", _
1106-
| "Tan", _
1107-
| "Tanh", _ ->
1112+
| "Tan", _ ->
11081113
match args with
1109-
| ExprType(Number(_, _)) :: _ -> math r t args i.SignatureArgTypes [] i.CompiledName |> Some
1114+
| ExprType(Number(_, _)) :: _ -> math r t args i.SignatureArgTypes i.CompiledName |> Some
11101115
| _ -> applyOp com ctx r t i.CompiledName args |> Some
11111116
| "Round", _ ->
11121117
match args with
@@ -1124,17 +1129,7 @@ let operators (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr o
11241129
)
11251130
|> Some
11261131
| _ ->
1127-
Helper.LibCall(
1128-
com,
1129-
"Util",
1130-
"round",
1131-
t,
1132-
args,
1133-
i.SignatureArgTypes,
1134-
genArgs = i.GenericArgs,
1135-
?thisArg = thisArg,
1136-
?loc = r
1137-
)
1132+
Helper.LibCall(com, "Util", "round", t, args, i.SignatureArgTypes, ?thisArg = thisArg, ?loc = r)
11381133
|> Some
11391134
| "Truncate", _ ->
11401135
match args with
@@ -1151,21 +1146,14 @@ let operators (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr o
11511146
?loc = r
11521147
)
11531148
|> Some
1154-
| _ ->
1155-
Helper.GlobalCall("Math", t, args, i.SignatureArgTypes, genArgs = i.GenericArgs, memb = "trunc", ?loc = r)
1156-
|> Some
1149+
| _ -> Helper.InstanceCall(args.Head, "truncateToDouble", t, [], ?loc = r) |> Some
11571150
| "Sign", _ ->
11581151
let args = toFloat com ctx r t args |> List.singleton
11591152

1160-
Helper.LibCall(com, "Util", "sign", t, args, i.SignatureArgTypes, genArgs = i.GenericArgs, ?loc = r)
1153+
Helper.LibCall(com, "Util", "sign", t, args, i.SignatureArgTypes, ?loc = r)
11611154
|> Some
11621155
| "DivRem", _ ->
1163-
let modName =
1164-
match i.SignatureArgTypes with
1165-
| Number(Int64, _) :: _ -> "Long"
1166-
| _ -> "Int32"
1167-
1168-
Helper.LibCall(com, modName, "divRem", t, args, i.SignatureArgTypes, genArgs = i.GenericArgs, ?loc = r)
1156+
Helper.LibCall(com, "Types", "divRem", t, args, i.SignatureArgTypes, ?loc = r)
11691157
|> Some
11701158
// Numbers
11711159
| ("Infinity" | "InfinitySingle"), _ -> Helper.GlobalIdent("Number", "POSITIVE_INFINITY", t, ?loc = r) |> Some
@@ -1209,12 +1197,15 @@ let operators (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr o
12091197
| (Operators.greaterThan | "Gt"), [ left; right ] -> booleanCompare com ctx r left right BinaryGreater |> Some
12101198
| (Operators.greaterThanOrEqual | "Gte"), [ left; right ] ->
12111199
booleanCompare com ctx r left right BinaryGreaterOrEqual |> Some
1212-
| ("Min" | "Max" | "Clamp" as meth), _ ->
1200+
| ("Min" | "Max" | "MinMagnitude" | "MaxMagnitude" | "Clamp" as meth), _ ->
12131201
let meth = Naming.lowerFirst meth
12141202

12151203
match meth, t with
12161204
| ("min" | "max"), Number((DartInt | DartDouble), NumberInfo.Empty) ->
1217-
Helper.ImportedCall("dart:math", meth, t, args, i.SignatureArgTypes, genArgs = i.GenericArgs, ?loc = r)
1205+
Helper.ImportedCall("dart:math", meth, t, args, i.SignatureArgTypes, ?loc = r)
1206+
|> Some
1207+
| ("minMagnitude" | "maxMagnitude"), Number((DartInt | DartDouble), NumberInfo.Empty) ->
1208+
Helper.LibCall(com, "Util", meth, t, args, i.SignatureArgTypes, ?loc = r)
12181209
|> Some
12191210
| _ ->
12201211
let f = makeComparerFunction com ctx t
@@ -2046,46 +2037,31 @@ let parseNum (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr op
20462037
[ str; makeIntConst style; makeBoolConst unsigned; makeIntConst bitsize ]
20472038
@ outValue
20482039

2049-
Helper.LibCall(com, numberModule, Naming.lowerFirst meth, t, args, ?loc = r)
2050-
|> Some
2040+
match kind, meth with
2041+
| (Float32 | Float64), "Parse" -> Helper.GlobalCall("double", t, [ str ], memb = "parse", ?loc = r) |> Some
2042+
| _ ->
2043+
Helper.LibCall(com, numberModule, Naming.lowerFirst meth, t, args, ?loc = r)
2044+
|> Some
20512045

20522046
let isFloat =
20532047
match i.SignatureArgTypes with
20542048
| Number((Float32 | Float64), _) :: _ -> true
20552049
| _ -> false
20562050

20572051
match i.CompiledName, args with
2058-
| "IsNaN", [ _ ] when isFloat -> Helper.GlobalCall("Number", t, args, memb = "isNaN", ?loc = r) |> Some
2059-
| "IsPositiveInfinity", [ _ ] when isFloat ->
2060-
Helper.LibCall(
2061-
com,
2062-
"Double",
2063-
"isPositiveInfinity",
2064-
t,
2065-
args,
2066-
i.SignatureArgTypes,
2067-
genArgs = i.GenericArgs,
2068-
?loc = r
2069-
)
2070-
|> Some
2071-
| "IsNegativeInfinity", [ _ ] when isFloat ->
2072-
Helper.LibCall(
2073-
com,
2074-
"Double",
2075-
"isNegativeInfinity",
2076-
t,
2077-
args,
2078-
i.SignatureArgTypes,
2079-
genArgs = i.GenericArgs,
2080-
?loc = r
2081-
)
2082-
|> Some
2083-
| "IsInfinity", [ _ ] when isFloat ->
2084-
Helper.LibCall(com, "Double", "isInfinity", t, args, i.SignatureArgTypes, genArgs = i.GenericArgs, ?loc = r)
2085-
|> Some
2086-
| "IsInfinity", [ _ ] when isFloat ->
2087-
Helper.LibCall(com, "Double", "isInfinity", t, args, i.SignatureArgTypes, genArgs = i.GenericArgs, ?loc = r)
2088-
|> Some
2052+
| "IsNaN", [ _ ] when isFloat -> emitExpr r t args "$0.isNaN" |> Some
2053+
| "Log2", _ ->
2054+
let log =
2055+
Helper.LibCall(com, "Util", "log2", Float64.Number, args, i.SignatureArgTypes, ?loc = r)
2056+
2057+
match t with
2058+
| Number((Float32 | Float64), _) -> TypeCast(log, t) |> Some
2059+
| Number _ -> toInt com ctx r t [ log ] |> Some
2060+
| _ -> log |> Some
2061+
| "IsPositiveInfinity", [ _ ] when isFloat -> emitExpr r t args "$0 == double.infinity" |> Some
2062+
| "IsNegativeInfinity", [ _ ] when isFloat -> emitExpr r t args "$0 == double.negativeInfinity" |> Some
2063+
| "IsInfinity", [ _ ] when isFloat -> emitExpr r t args "$0.isInfinite" |> Some
2064+
| ("Min" | "Max" | "MinMagnitude" | "MaxMagnitude" | "Clamp"), _ -> operators com ctx r t i thisArg args
20892065
| ("Parse" | "TryParse") as meth, str :: NumberConst(NumberValue.Int32 style, _) :: _ ->
20902066
let hexConst = int System.Globalization.NumberStyles.HexNumber
20912067
let intConst = int System.Globalization.NumberStyles.Integer

src/fable-library-dart/Array.fs

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -979,7 +979,13 @@ let splitInto (chunks: int) (array: 'T[]) : 'T[][] =
979979
else
980980
let len = array.Length
981981
let result = ResizeArray()
982-
let chunks = Operators.min chunks len
982+
983+
let chunks =
984+
if chunks < len then
985+
chunks
986+
else
987+
len
988+
983989
let minChunkSize = len / chunks
984990
let chunksWithExtraItem = len % chunks
985991

@@ -990,8 +996,21 @@ let splitInto (chunks: int) (array: 'T[]) : 'T[][] =
990996
else
991997
minChunkSize
992998

993-
let start = i * minChunkSize + (Operators.min chunksWithExtraItem i)
994-
let end_ = Operators.min len (start + chunkSize)
999+
let startOffset =
1000+
if chunksWithExtraItem < i then
1001+
chunksWithExtraItem
1002+
else
1003+
i
1004+
1005+
let start = i * minChunkSize + startOffset
1006+
let finish = start + chunkSize
1007+
1008+
let end_ =
1009+
if finish < len then
1010+
finish
1011+
else
1012+
len
1013+
9951014
let slice = Native.sublist (array, start, end_)
9961015
result.Add(slice)
9971016

src/fable-library-dart/Types.dart

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,16 @@ bool removeKey<K, V>(Map<K, V> map, K key) {
9999
return false;
100100
}
101101

102+
dynamic divRem(int x, int y, [FSharpRef<int>? remainder]) {
103+
final quotient = x ~/ y;
104+
final modulus = x.remainder(y);
105+
if (remainder != null) {
106+
remainder.contents = modulus;
107+
return quotient;
108+
}
109+
return Tuple2(quotient, modulus);
110+
}
111+
102112
Set<T> setWith<T>(IEqualityComparer<T> comparer, [Iterable<T>? initialValues]) {
103113
final set =
104114
LinkedHashSet<T>(equals: comparer.Equals, hashCode: comparer.GetHashCode);

src/fable-library-dart/Util.dart

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// ignore_for_file: file_names
2+
import 'dart:math' as dart_math;
23

34
final _curried = Expando();
45

@@ -1007,6 +1008,76 @@ T clamp<T>(int Function(T, T) comparer, T value, T min, T max) {
10071008
: value;
10081009
}
10091010

1011+
// Banker's rounding (round half to even), compatible with .NET Math.Round
1012+
double round(double x, [int digits = 0]) {
1013+
if (digits == 0) {
1014+
final truncated = x.truncateToDouble();
1015+
final diff = (x - truncated).abs();
1016+
if (diff == 0.5) {
1017+
return truncated.remainder(2.0) == 0.0
1018+
? truncated
1019+
: (x > 0 ? truncated + 1.0 : truncated - 1.0);
1020+
}
1021+
return x.roundToDouble();
1022+
} else {
1023+
final scale = dart_math.pow(10, digits).toDouble();
1024+
return round(x * scale) / scale;
1025+
}
1026+
}
1027+
1028+
int negateInt8(int x) => (((-x) + 0x80) & 0xFF) - 0x80;
1029+
1030+
int negateInt16(int x) => (((-x) + 0x8000) & 0xFFFF) - 0x8000;
1031+
1032+
int negateInt32(int x) => (((-x) + 0x80000000) & 0xFFFFFFFF) - 0x80000000;
1033+
1034+
int toUInt64(int x) => BigInt.from(x).toUnsigned(64).toInt();
1035+
1036+
int toInt64(int x) => BigInt.from(x).toUnsigned(64).toSigned(64).toInt();
1037+
1038+
int negateInt64(int x) => toInt64(-x);
1039+
1040+
int rightShiftUnsigned64(int x, int bits) =>
1041+
(BigInt.from(x).toUnsigned(64) >> bits).toInt();
1042+
1043+
int sign(double x) {
1044+
return x > 0 ? 1 : x < 0 ? -1 : 0;
1045+
}
1046+
1047+
double cosh(double x) {
1048+
final expX = dart_math.exp(x);
1049+
final expNegX = dart_math.exp(-x);
1050+
return (expX + expNegX) / 2.0;
1051+
}
1052+
1053+
double sinh(double x) {
1054+
final expX = dart_math.exp(x);
1055+
final expNegX = dart_math.exp(-x);
1056+
return (expX - expNegX) / 2.0;
1057+
}
1058+
1059+
double tanh(double x) {
1060+
final expX = dart_math.exp(x);
1061+
final expNegX = dart_math.exp(-x);
1062+
return (expX - expNegX) / (expX + expNegX);
1063+
}
1064+
1065+
double log2(double x) {
1066+
return dart_math.log(x) / dart_math.ln2;
1067+
}
1068+
1069+
double log10(double x) {
1070+
return dart_math.log(x) / dart_math.ln10;
1071+
}
1072+
1073+
T minMagnitude<T extends num>(T x, T y) {
1074+
return x.abs() < y.abs() ? x : y;
1075+
}
1076+
1077+
T maxMagnitude<T extends num>(T x, T y) {
1078+
return x.abs() > y.abs() ? x : y;
1079+
}
1080+
10101081
String int16ToString(int i, [int radix = 10]) {
10111082
if (radix == 10) {
10121083
return i.toString();

0 commit comments

Comments
 (0)