Skip to content
Merged
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
8 changes: 4 additions & 4 deletions src/Fable.Transforms/Beam/Replacements.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2907,7 +2907,7 @@ let private arrayModule
| ("Sub" | "GetSubArray"), [ arr; start; count ] ->
let arr = derefArr r arr

emitExpr r t [ arr; start; count ] "lists:sublist($0, $1 + 1, $2)"
Helper.LibCall(com, "fable_list", "sub", t, [ arr; start; count ])
|> wrapArr com r t
|> Some
| "Except", [ excludeArr; arr ] ->
Expand Down Expand Up @@ -2978,11 +2978,11 @@ let private arrayModule
| "Exists2", [ fn; a1; a2 ] ->
let a1 = derefArr r a1
let a2 = derefArr r a2
Helper.LibCall(com, "fable_list", "exists2", t, [ fn; a1; a2 ]) |> Some
Helper.LibCall(com, "fable_list", "array_exists2", t, [ fn; a1; a2 ]) |> Some
| "ForAll2", [ fn; a1; a2 ] ->
let a1 = derefArr r a1
let a2 = derefArr r a2
Helper.LibCall(com, "fable_list", "forall2", t, [ fn; a1; a2 ]) |> Some
Helper.LibCall(com, "fable_list", "array_forall2", t, [ fn; a1; a2 ]) |> Some
| "Fold2", [ fn; state; a1; a2 ] ->
let a1 = derefArr r a1
let a2 = derefArr r a2
Expand Down Expand Up @@ -3134,7 +3134,7 @@ let private arrayModule
let blitted =
Helper.LibCall(
com,
"fable_resize_array",
"fable_list",
"blit",
t,
[ derefSource; sourceIdx; derefTarget; targetIdx; count ]
Expand Down
4 changes: 4 additions & 0 deletions src/Fable.Transforms/Dart/Replacements.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1877,6 +1877,10 @@ let arrayModule (com: ICompiler) (ctx: Context) r (t: Type) (i: CallInfo) (_: Ex
| "IsEmpty", [ ar ] -> getFieldWith r t ar "isEmpty" |> Some
| "Contains", [ value; ar ] -> Helper.LibCall(com, "Array", "contains", t, [ value; ar ], ?loc = r) |> Some
| "CopyTo", args -> copyToArray com r t i args
// getSubArray is shared with ResizeArray.GetRange, which must stay unchecked
| "GetSubArray", args ->
Helper.LibCall(com, "Array", "sub", t, args, i.SignatureArgTypes, genArgs = i.GenericArgs, ?loc = r)
|> Some
| ("Distinct" | "DistinctBy" | "Except" | "GroupBy" | "CountBy" as meth), args ->
let meth = Naming.lowerFirst meth
let args = injectArg com ctx r "Seq2" meth i.GenericArgs args
Expand Down
16 changes: 14 additions & 2 deletions src/Fable.Transforms/Replacements.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2339,14 +2339,26 @@ let tuples (com: ICompiler) (ctx: Context) r (t: Type) (i: CallInfo) (thisArg: E
| "ToTuple", _ -> changeKind false args
| _ -> None

let arrayCopy (com: ICompiler) (ctx: Context) r t source sourceIndex target targetIndex count =
let toInt32 (arg: Expr) =
match arg.Type with
| Number(Int32, _) -> arg
| _ -> toInt com ctx r Int32.Number [ arg ]

let args =
[ source; toInt32 sourceIndex; target; toInt32 targetIndex; toInt32 count ]

Helper.LibCall(com, "Array", "copyTo", t, args, ?loc = r) |> Some

let arrays (com: ICompiler) (ctx: Context) r (t: Type) (i: CallInfo) (thisArg: Expr option) (args: Expr list) =
match i.CompiledName, thisArg, args with
| "get_Length", Some arg, _ -> getFieldWith r t arg "length" |> Some
| "get_Item", Some arg, [ idx ] -> getExpr r t arg idx |> Some
| "set_Item", Some arg, [ idx; value ] -> setExpr r arg idx value |> Some
| "Copy", None, [ _source; _sourceIndex; _target; _targetIndex; _count ] -> copyToArray com r t i args
| "Copy", None, [ source; sourceIndex; target; targetIndex; count ] ->
arrayCopy com ctx r t source sourceIndex target targetIndex count
| "Copy", None, [ source; target; count ] ->
copyToArray com r t i [ source; makeIntConst 0; target; makeIntConst 0; count ]
arrayCopy com ctx r t source (makeIntConst 0) target (makeIntConst 0) count
| "ConvertAll", None, [ source; mapping ] ->
Helper.LibCall(com, "Array", "map", t, [ mapping; source ], ?loc = r) |> Some
| "IndexOf", None, args ->
Expand Down
73 changes: 47 additions & 26 deletions src/fable-library-beam/Seq.fs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ type IEnumerable<'T> = System.Collections.Generic.IEnumerable<'T>
module SR =
let enumerationAlreadyFinished = "Enumeration already finished."
let enumerationNotStarted = "Enumeration has not started. Call MoveNext."
let inputMustBePositive = "The input must be positive."
let inputSequenceEmpty = "The input sequence was empty."

let inputSequenceTooLong = "The input sequence contains more than one element."
Expand Down Expand Up @@ -683,6 +684,9 @@ let head (xs: seq<'T>) =
| None -> invalidArg "source" SR.inputSequenceEmpty

let initialize count f =
if count < 0 then
invalidArg "count" SR.inputMustBeNonNegative

unfold
(fun i ->
if (i < count) then
Expand Down Expand Up @@ -1002,7 +1006,12 @@ let reduceBack folder (xs: seq<'T>) =
else
invalidArg "source" SR.inputSequenceEmpty

let replicate n x = initialize n (fun _ -> x)
let replicate n x =
// FSharp.Core uses Enumerable.Repeat, which raises ArgumentOutOfRangeException
if n < 0 then
raise (System.ArgumentOutOfRangeException("count"))

initialize n (fun _ -> x)

let reverse (xs: seq<'T>) =
delay (fun () -> xs |> toArray |> Array.rev |> ofArray)
Expand Down Expand Up @@ -1074,6 +1083,9 @@ let tail (xs: seq<'T>) =
)

let take count (xs: seq<'T>) =
if count < 0 then
invalidArg "count" SR.inputMustBeNonNegative

generateIndexed
(fun () -> ofSeq xs)
(fun i e ->
Expand Down Expand Up @@ -1122,19 +1134,25 @@ let pairwise (xs: seq<'T>) =
delay (fun () -> xs |> toArray |> Array.pairwise |> ofArray)

let splitInto (chunks: int) (xs: seq<'T>) : 'T[] seq =
if chunks < 1 then
invalidArg "count" SR.inputMustBePositive

delay (fun () -> xs |> toArray |> Array.splitInto chunks |> ofArray)

let windowed (windowSize: int) (xs: seq<'T>) : 'T[] seq =
if windowSize < 1 then
invalidArg "windowSize" SR.inputMustBePositive

delay (fun () -> xs |> toArray |> Array.windowed windowSize |> ofArray)

let transpose (xss: seq<#seq<'T>>) : seq<seq<'T>> =
delay (fun () ->
xss
|> toArray
|> Array.map toArray
|> Array.transpose
|> Array.map ofArray
|> ofArray
let rec columns (rows: 'T list list) =
match rows |> List.filter (List.isEmpty >> not) with
| [] -> []
| rows -> ofList (List.map List.head rows) :: columns (List.map List.tail rows)

xss |> toList |> List.map toList |> columns |> ofList
)

let sortWith (comparer: 'T -> 'T -> int) (xs: seq<'T>) =
Expand Down Expand Up @@ -1238,6 +1256,9 @@ let permute f (xs: seq<'T>) =
delay (fun () -> xs |> toArray |> Array.permute f |> ofArray)

let chunkBySize (chunkSize: int) (xs: seq<'T>) : seq<'T[]> =
if chunkSize < 1 then
invalidArg "chunkSize" SR.inputMustBePositive

delay (fun () -> xs |> toArray |> Array.chunkBySize chunkSize |> ofArray)

// let init = initialize
Expand Down Expand Up @@ -1342,28 +1363,28 @@ let removeAt (index: int) (xs: seq<'T>) : seq<'T> =

let removeManyAt (index: int) (count: int) (xs: seq<'T>) : seq<'T> =
if index < 0 then
invalidArg "index" SR.indexOutOfBounds
invalidArg "index" "index must be within bounds of the array"

generateIndexed
(fun () -> ofSeq xs)
(fun i e ->
let endIndex = index + count

delay (fun () ->
let mutable i = -1

let rest () =
if i < index then
if e.MoveNext() then
Some e.Current
else
invalidArg "index" SR.indexOutOfBounds
else
if i = index then
for _ = 1 to count do
if not (e.MoveNext()) then
invalidArg "count" SR.indexOutOfBounds
invalidArg "index" "index must be within bounds of the array"

if e.MoveNext() then
Some e.Current
else
None
)
(fun e -> e.Dispose())
empty ()

let ys =
xs
|> filter (fun _ ->
i <- i + 1
i < index || i >= endIndex
)

append ys (delay rest)
)

let updateAt (index: int) (y: 'T) (xs: seq<'T>) : seq<'T> =
let mutable isDone = false
Expand Down
114 changes: 103 additions & 11 deletions src/fable-library-beam/fable_list.erl
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@
pairwise/1,
exists2/3,
forall2/3,
array_exists2/3,
array_forall2/3,
map2/3,
map3/4,
mapi2/3,
Expand Down Expand Up @@ -79,6 +81,8 @@
remove_many_at/3,
index_of_value/2,
get_slice/3,
sub/3,
blit/5,
take/2,
random_shuffle_by/2,
random_shuffle_with/2,
Expand Down Expand Up @@ -134,6 +138,8 @@
-spec pairwise(list()) -> list({term(), term()}).
-spec exists2(fun(), list(), list()) -> boolean().
-spec forall2(fun(), list(), list()) -> boolean().
-spec array_exists2(fun(), list(), list()) -> boolean().
-spec array_forall2(fun(), list(), list()) -> boolean().
-spec map2(fun(), list(), list()) -> list().
-spec map3(fun(), list(), list(), list()) -> list().
-spec mapi2(fun(), list(), list()) -> list().
Expand All @@ -147,9 +153,9 @@
-spec group_by(fun(), list()) -> list({term(), list()}).
-spec unfold(fun(), term()) -> list().
-spec split_at(non_neg_integer(), list()) -> {list(), list()}.
-spec chunk_by_size(pos_integer(), list()) -> list(list()).
-spec windowed(pos_integer(), list()) -> list(list()).
-spec split_into(pos_integer(), list()) -> list(list()).
-spec chunk_by_size(integer(), list()) -> list(list()).
-spec windowed(integer(), list()) -> list(list()).
-spec split_into(integer(), list()) -> list(list()).
-spec except(list(), list()) -> list().
-spec all_pairs(list(), list()) -> list({term(), term()}).
-spec permute(fun(), list()) -> list().
Expand Down Expand Up @@ -424,11 +430,37 @@ pairwise([]) -> [];
pairwise([_]) -> [];
pairwise([A, B | T]) -> [{A, B} | pairwise([B | T])].

exists2(Fn, L1, L2) ->
lists:any(fun({A, B}) -> (Fn(A))(B) end, lists:zip(L1, L2)).

forall2(Fn, L1, L2) ->
lists:all(fun({A, B}) -> (Fn(A))(B) end, lists:zip(L1, L2)).
exists2(Fn, [A | T1], [B | T2]) ->
case (Fn(A))(B) of
true -> true;
false -> exists2(Fn, T1, T2)
end;
exists2(_Fn, [], []) ->
false;
exists2(_Fn, _L1, _L2) ->
lists_had_different_lengths(<<"list2">>).

forall2(Fn, [A | T1], [B | T2]) ->
case (Fn(A))(B) of
true -> forall2(Fn, T1, T2);
false -> false
end;
forall2(_Fn, [], []) ->
true;
forall2(_Fn, [], _L2) ->
lists_had_different_lengths(<<"list1">>);
forall2(_Fn, _L1, []) ->
lists_had_different_lengths(<<"list2">>).

array_exists2(Fn, A1, A2) when length(A1) =:= length(A2) ->
exists2(Fn, A1, A2);
array_exists2(_Fn, _A1, _A2) ->
invalid_arg(<<"array1">>, <<"The arrays have different lengths.">>).

array_forall2(Fn, A1, A2) when length(A1) =:= length(A2) ->
forall2(Fn, A1, A2);
array_forall2(_Fn, _A1, _A2) ->
invalid_arg(<<"array1">>, <<"The arrays have different lengths.">>).

map2(Fn, L1, L2) ->
lists:zipwith(fun(A, B) -> (Fn(A))(B) end, L1, L2).
Expand Down Expand Up @@ -507,6 +539,8 @@ unfold(Fn, State) ->
split_at(Index, List) ->
{lists:sublist(List, Index), lists:nthtail(Index, List)}.

chunk_by_size(Size, _List) when Size < 1 ->
invalid_arg(<<"chunkSize">>, <<"The input must be positive.">>);
chunk_by_size(Size, List) ->
chunk_by_size(Size, List, []).
chunk_by_size(_Size, [], Acc) ->
Expand All @@ -519,13 +553,19 @@ chunk_by_size(Size, List, Acc) ->
end,
chunk_by_size(Size, Rest, [Chunk | Acc]).

windowed(Size, _List) when Size < 1 ->
invalid_arg(<<"windowSize">>, <<"The input must be positive.">>);
windowed(Size, List) ->
windowed(Size, List, []).
windowed(Size, List, Acc) when length(List) < Size -> lists:reverse(Acc);
windowed(Size, [_ | T] = List, Acc) ->
Window = lists:sublist(List, Size),
windowed(Size, T, [Window | Acc]).

split_into(Count, _List) when Count < 1 ->
invalid_arg(<<"count">>, <<"The input must be positive.">>);
split_into(_Count, []) ->
[];
split_into(Count, List) ->
Len = length(List),
ActualCount = min(Count, Len),
Expand Down Expand Up @@ -617,12 +657,19 @@ try_find_index_back(Fn, [H | T], Idx) ->

transpose([]) ->
[];
transpose([[] | _]) ->
transpose([First | Rest] = Lists) ->
Len = length(First),
case lists:all(fun(L) -> length(L) =:= Len end, Rest) of
true -> transpose_rows(Lists);
false -> invalid_arg(<<"array.[0]">>, <<"The arrays have different lengths.">>)
end.

transpose_rows([[] | _]) ->
[];
transpose(Lists) ->
transpose_rows(Lists) ->
Heads = [hd(L) || L <- Lists],
Tails = [tl(L) || L <- Lists],
[Heads | transpose(Tails)].
[Heads | transpose_rows(Tails)].

compare_with(_Fn, [], []) ->
0;
Expand Down Expand Up @@ -676,6 +723,42 @@ get_slice(Lower, Upper, List) ->
end,
lists:sublist(List, Start + 1, End - Start + 1).

-spec sub(list(), integer(), integer()) -> list().
sub(_List, StartIndex, _Count) when StartIndex < 0 ->
invalid_arg(<<"startIndex">>, <<"The input must be non-negative.">>);
sub(_List, _StartIndex, Count) when Count < 0 ->
invalid_arg(<<"count">>, <<"The input must be non-negative.">>);
sub(List, 0, Count) ->
case lists:sublist(List, Count) of
Sub when length(Sub) =:= Count -> Sub;
_ -> argument_out_of_range(<<"count">>, <<"The index is outside the legal range.">>)
end;
sub([_ | T], StartIndex, Count) ->
sub(T, StartIndex - 1, Count);
sub([], _StartIndex, _Count) ->
argument_out_of_range(<<"count">>, <<"The index is outside the legal range.">>).

%% HashSet.CopyTo calls fable_resize_array:blit/5 directly, its count may exceed the set
-spec blit(list(), integer(), list(), integer(), integer()) -> list().
blit(_Source, _SourceIdx, _Target, _TargetIdx, Count) when Count < 0 ->
argument_out_of_range(<<"length">>, <<"Non-negative number required.">>);
blit(_Source, SourceIdx, _Target, _TargetIdx, _Count) when SourceIdx < 0 ->
argument_out_of_range(<<"sourceIndex">>, <<"Non-negative number required.">>);
blit(Source, SourceIdx, _Target, _TargetIdx, Count) when SourceIdx + Count > length(Source) ->
invalid_arg(
<<"sourceArray">>,
<<"Source array was not long enough. Check the source index, length, and the array's lower bounds.">>
);
blit(_Source, _SourceIdx, _Target, TargetIdx, _Count) when TargetIdx < 0 ->
argument_out_of_range(<<"destinationIndex">>, <<"Non-negative number required.">>);
blit(_Source, _SourceIdx, Target, TargetIdx, Count) when TargetIdx + Count > length(Target) ->
invalid_arg(
<<"destinationArray">>,
<<"Destination array was not long enough. Check the destination index, length, and the array's lower bounds.">>
);
blit(Source, SourceIdx, Target, TargetIdx, Count) ->
fable_resize_array:blit(Source, SourceIdx, Target, TargetIdx, Count).

-spec take(non_neg_integer(), list()) -> list().
take(Count, List) ->
case erlang:length(List) >= Count of
Expand Down Expand Up @@ -834,3 +917,12 @@ invalid_arg(ParamName, Message) ->
exn_type => argument_exception,
message => fable_utils:argument_exception_message(Message, ParamName)
}).

argument_out_of_range(ParamName, Message) ->
erlang:error(#{
exn_type => argument_out_of_range_exception,
message => fable_utils:argument_exception_message(Message, ParamName)
}).

lists_had_different_lengths(ParamName) ->
invalid_arg(ParamName, <<"The lists had different lengths.">>).
Loading
Loading