Skip to content

Commit

Permalink
Add minimal changes to adapt to OTP26
Browse files Browse the repository at this point in the history
* Handle new map comprehension and map generators in power_shell_eval
* Add tests for above
* (The experimental feature maybe is not handled)
  • Loading branch information
lisztspace committed Nov 10, 2023
1 parent 11ccf54 commit e44a515
Show file tree
Hide file tree
Showing 2 changed files with 157 additions and 24 deletions.
116 changes: 93 additions & 23 deletions src/power_shell_eval.erl
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,8 @@ expr({lc,_,E,Qs}, Bs, Lf, Ef, RBs, FUVs) ->
eval_lc(E, Qs, Bs, Lf, Ef, RBs, FUVs);
expr({bc,_,E,Qs}, Bs, Lf, Ef, RBs, FUVs) ->
eval_bc(E, Qs, Bs, Lf, Ef, RBs, FUVs);
expr({mc,_,E,Qs}, Bs, Lf, Ef, RBs, FUVs) ->
eval_mc(E, Qs, Bs, Lf, Ef, RBs, FUVs);
expr({tuple,_,Es}, Bs0, Lf, Ef, RBs, FUVs) ->
{Vs,Bs} = expr_list(Es, Bs0, Lf, Ef, FUVs),
ret_expr(list_to_tuple(Vs), Bs, RBs);
Expand Down Expand Up @@ -523,6 +525,9 @@ expr({bin,_,Fs}, Bs0, Lf, Ef, RBs, FUVs) ->
expr({remote,_,_,_}, _Bs, _Lf, _Ef, _RBs, _FUVs) ->
erlang:raise(error, {badexpr,':'}, ?STACKTRACE).

apply_error(Reason, Stack, _Anno, Bs0, Ef, RBs) ->
do_apply(erlang, raise, [error, Reason, Stack], Bs0, Ef, RBs).

find_maxline(LC) ->
put('$erl_eval_max_line', 0),
F = fun(A) ->
Expand Down Expand Up @@ -737,17 +742,15 @@ do_apply(Mod, Func, As, Bs0, Ef, RBs) ->
eval_lc(E, Qs, Bs, Lf, Ef, RBs, FUVs) ->
ret_expr(lists:reverse(eval_lc1(E, Qs, Bs, Lf, Ef, FUVs, [])), Bs, RBs).

eval_lc1(E, [{generate,_,P,L0}|Qs], Bs0, Lf, Ef, FUVs, Acc0) ->
{value,L1,_Bs1} = expr(L0, Bs0, Lf, Ef, none, FUVs),
CompFun = fun(Bs, Acc) -> eval_lc1(E, Qs, Bs, Lf, Ef, FUVs, Acc) end,
eval_generate(L1, P, Bs0, Lf, Ef, CompFun, Acc0);
eval_lc1(E, [{b_generate,_,P,L0}|Qs], Bs0, Lf, Ef, FUVs, Acc0) ->
{value,Bin,_Bs1} = expr(L0, Bs0, Lf, Ef, none, FUVs),
CompFun = fun(Bs, Acc) -> eval_lc1(E, Qs, Bs, Lf, Ef, FUVs, Acc) end,
eval_b_generate(Bin, P, Bs0, Lf, Ef, CompFun, Acc0);
eval_lc1(E, [F|Qs], Bs0, Lf, Ef, FUVs, Acc) ->
CompFun = fun(Bs) -> eval_lc1(E, Qs, Bs, Lf, Ef, FUVs, Acc) end,
eval_filter(F, Bs0, Lf, Ef, CompFun, FUVs, Acc);
eval_lc1(E, [Q|Qs], Bs0, Lf, Ef, FUVs, Acc0) ->
case is_generator(Q) of
true ->
CF = fun(Bs, Acc) -> eval_lc1(E, Qs, Bs, Lf, Ef, FUVs, Acc) end,
eval_generator(Q, Bs0, Lf, Ef, FUVs, Acc0, CF);
false ->
CF = fun(Bs) -> eval_lc1(E, Qs, Bs, Lf, Ef, FUVs, Acc0) end,
eval_filter(Q, Bs0, Lf, Ef, CF, FUVs, Acc0)
end;
eval_lc1(E, [], Bs, Lf, Ef, FUVs, Acc) ->
{value,V,_} = expr(E, Bs, Lf, Ef, none, FUVs),
[V|Acc].
Expand All @@ -759,21 +762,67 @@ eval_lc1(E, [], Bs, Lf, Ef, FUVs, Acc) ->
eval_bc(E, Qs, Bs, Lf, Ef, RBs, FUVs) ->
ret_expr(eval_bc1(E, Qs, Bs, Lf, Ef, FUVs, <<>>), Bs, RBs).

eval_bc1(E, [{b_generate,_,P,L0}|Qs], Bs0, Lf, Ef, FUVs, Acc0) ->
{value,Bin,_Bs1} = expr(L0, Bs0, Lf, Ef, none, FUVs),
CompFun = fun(Bs, Acc) -> eval_bc1(E, Qs, Bs, Lf, Ef, FUVs, Acc) end,
eval_b_generate(Bin, P, Bs0, Lf, Ef, CompFun, Acc0);
eval_bc1(E, [{generate,_,P,L0}|Qs], Bs0, Lf, Ef, FUVs, Acc0) ->
{value,List,_Bs1} = expr(L0, Bs0, Lf, Ef, none, FUVs),
CompFun = fun(Bs, Acc) -> eval_bc1(E, Qs, Bs, Lf, Ef, FUVs, Acc) end,
eval_generate(List, P, Bs0, Lf, Ef, CompFun, Acc0);
eval_bc1(E, [F|Qs], Bs0, Lf, Ef, FUVs, Acc) ->
CompFun = fun(Bs) -> eval_bc1(E, Qs, Bs, Lf, Ef, FUVs, Acc) end,
eval_filter(F, Bs0, Lf, Ef, CompFun, FUVs, Acc);
eval_bc1(E, [Q|Qs], Bs0, Lf, Ef, FUVs, Acc0) ->
case is_generator(Q) of
true ->
CF = fun(Bs, Acc) -> eval_bc1(E, Qs, Bs, Lf, Ef, FUVs, Acc) end,
eval_generator(Q, Bs0, Lf, Ef, FUVs, Acc0, CF);
false ->
CF = fun(Bs) -> eval_bc1(E, Qs, Bs, Lf, Ef, FUVs, Acc0) end,
eval_filter(Q, Bs0, Lf, Ef, CF, FUVs, Acc0)
end;
eval_bc1(E, [], Bs, Lf, Ef, FUVs, Acc) ->
{value,V,_} = expr(E, Bs, Lf, Ef, none, FUVs),
<<Acc/bitstring,V/bitstring>>.


%% eval_mc(Expr, [Qualifier], Bindings, LocalFunctionHandler,
%% ExternalFuncHandler, RetBindings) ->
%% {value,Value,Bindings} | Value

eval_mc(E, Qs, Bs, Lf, Ef, RBs, FUVs) ->
L = eval_mc1(E, Qs, Bs, Lf, Ef, FUVs, []),
Map = maps:from_list(L),
ret_expr(Map, Bs, RBs).

eval_mc1(E, [Q|Qs], Bs0, Lf, Ef, FUVs, Acc0) ->
case is_generator(Q) of
true ->
CF = fun(Bs, Acc) -> eval_mc1(E, Qs, Bs, Lf, Ef, FUVs, Acc) end,
eval_generator(Q, Bs0, Lf, Ef, FUVs, Acc0, CF);
false ->
CF = fun(Bs) -> eval_mc1(E, Qs, Bs, Lf, Ef, FUVs, Acc0) end,
eval_filter(Q, Bs0, Lf, Ef, CF, FUVs, Acc0)
end;
eval_mc1({map_field_assoc,Lfa,K0,V0}, [], Bs, Lf, Ef, FUVs, Acc) ->
{value,KV,_} = expr({tuple,Lfa,[K0,V0]}, Bs, Lf, Ef, none, FUVs),
[KV|Acc].

eval_generator({generate,_Anno,P,L0}, Bs0, Lf, Ef, FUVs, Acc0, CompFun) ->
{value,L1,_Bs1} = expr(L0, Bs0, Lf, Ef, none, FUVs),
eval_generate(L1, P, Bs0, Lf, Ef, CompFun, Acc0);
eval_generator({b_generate,_Anno,P,Bin0}, Bs0, Lf, Ef, FUVs, Acc0, CompFun) ->
{value,Bin,_Bs1} = expr(Bin0, Bs0, Lf, Ef, none, FUVs),
eval_b_generate(Bin, P, Bs0, Lf, Ef, CompFun, Acc0);
eval_generator({m_generate,Anno,P,Map0}, Bs0, Lf, Ef, FUVs, Acc0, CompFun) ->
{map_field_exact,_,K,V} = P,
{value,Map,_Bs1} = expr(Map0, Bs0, Lf, Ef, none, FUVs),
Iter = case is_map(Map) of
true ->
maps:iterator(Map);
false ->
%% Validate iterator.
try maps:foreach(fun(_, _) -> ok end, Map) of
_ ->
Map
catch
_:_ ->
apply_error({bad_generator,Map}, ?STACKTRACE,
Anno, Bs0, Ef, none)
end
end,
eval_m_generate(Iter, {tuple,Anno,[K,V]}, Anno, Bs0, Lf, Ef, CompFun, Acc0).

eval_generate([V|Rest], P, Bs0, Lf, Ef, CompFun, Acc) ->
case match(P, V, new_bindings(Bs0), Bs0) of
{match,Bsn} ->
Expand Down Expand Up @@ -804,6 +853,22 @@ eval_b_generate(<<_/bitstring>>=Bin, P, Bs0, Lf, Ef, CompFun, Acc) ->
eval_b_generate(Term, _P, _Bs0, _Lf, _Ef, _CompFun, _Acc) ->
erlang:raise(error, {bad_generator,Term}, ?STACKTRACE).


eval_m_generate(Iter0, P, Anno, Bs0, Lf, Ef, CompFun, Acc0) ->
case maps:next(Iter0) of
{K,V,Iter} ->
case match(P, {K,V}, new_bindings(Bs0), Bs0) of
{match,Bsn} ->
Bs2 = add_bindings(Bsn, Bs0),
Acc = CompFun(Bs2, Acc0),
eval_m_generate(Iter, P, Anno, Bs0, Lf, Ef, CompFun, Acc);
nomatch ->
eval_m_generate(Iter, P, Anno, Bs0, Lf, Ef, CompFun, Acc0)
end;
none ->
Acc0
end.

eval_filter(F, Bs0, Lf, Ef, CompFun, FUVs, Acc) ->
case erl_lint:is_guard_test(F) of
true ->
Expand All @@ -820,6 +885,11 @@ eval_filter(F, Bs0, Lf, Ef, CompFun, FUVs, Acc) ->
end
end.

is_generator({generate,_,_,_}) -> true;
is_generator({b_generate,_,_,_}) -> true;
is_generator({m_generate,_,_,_}) -> true;
is_generator(_) -> false.

%% eval_map_fields([Field], Bindings, LocalFunctionHandler,
%% ExternalFuncHandler) ->
%% {[{map_assoc | map_exact,Key,Value}],Bindings}
Expand Down Expand Up @@ -1692,4 +1762,4 @@ merge_with_1({K, V2, Iterator}, Map1, Map2, Combiner) ->
merge_with_1(maps:next(Iterator), maps:put(K, V2, Map1), Map2, Combiner)
end;
merge_with_1(none, Result, _, _) ->
Result.
Result.
65 changes: 64 additions & 1 deletion test/power_shell_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@
try_side_effect/0, try_side_effect/1,
rebind_var/0, rebind_var/1,
external_fun/0, external_fun/1,
map_comp_from_list/0, map_comp_from_list/1,
map_comp_from_bin/0, map_comp_from_bin/1,
map_gen_to_map/0, map_gen_to_map/1,
map_gen_to_list/0, map_gen_to_list/1,
map_gen_to_bin/0, map_gen_to_bin/1,
catch_apply/0, catch_apply/1,
export/0, export/1,
export_partial/0, export_partial/1,
Expand Down Expand Up @@ -65,7 +70,8 @@ test_cases() ->
[echo, self_echo, preloaded, second_clause, undef, undef_local, undef_nested, recursive,
calling_local, throwing, bad_match, function_clause, remote_callback,
callback_local, callback_local_fun_obj, callback_local_make_fun, recursive_eval,
remote_callback_exported, record, try_side_effect, rebind_var, external_fun, catch_apply].
remote_callback_exported, record, try_side_effect, rebind_var, external_fun, catch_apply,
map_comp_from_list, map_comp_from_bin, map_gen_to_map, map_gen_to_list, map_gen_to_bin].

%%--------------------------------------------------------------------
%% Function: groups() -> [Group]
Expand Down Expand Up @@ -228,6 +234,11 @@ export_all() ->
local_bad_match(),
rebind([]),
external_filter([]),
map_comprehension_from_list(),
map_comprehension_from_binary(),
map_generator_to_map(),
map_generator_to_list(),
map_generator_to_binary(),
throw_applied(),
try_side({1, 1}).

Expand Down Expand Up @@ -265,6 +276,23 @@ throw_applied() ->
throw(expected)
end.

map_comprehension_from_list() ->
#{N => N*N || N <- lists:seq(1, 10), N < 4}.

map_comprehension_from_binary() ->
#{X => Y || <<X,Y>> <= <<"abcdefgh">>}.

map_generator_to_map() ->
#{V => K
|| K := V <- #{1 => 1, 2 => 4, 3 => 9, one => one, two => four},
is_integer(K)}.

map_generator_to_list() ->
lists:sort([{V, K} || K := V <- #{1 => 1, 2 => 4, 3 => 9}]).

map_generator_to_binary() ->
<< <<K, V>> || K := V <- #{$a => $b, $c => $d} >>.

%%--------------------------------------------------------------------
%% Test Cases

Expand Down Expand Up @@ -417,6 +445,41 @@ external_fun() ->
external_fun(Config) when is_list(Config) ->
?assertEqual([1, 2], power_shell:eval(?MODULE, external_filter, [[1, atom, 2, atom]])).

map_comp_from_list() ->
[{doc, "Tests handling of map comprehension introduced in OTP26"}].

map_comp_from_list(Config) when is_list(Config) ->
?assertEqual(#{1 => 1, 2 => 4, 3 => 9},
power_shell:eval(?MODULE, map_comprehension_from_list, [])).

map_comp_from_bin() ->
[{doc, "Tests handling of map comprehension introduced in OTP26"}].

map_comp_from_bin(Config) when is_list(Config) ->
?assertEqual(#{$a => $b, $c => $d, $e => $f, $g => $h},
power_shell:eval(?MODULE, map_comprehension_from_binary, [])).

map_gen_to_map() ->
[{doc, "Tests handling of map generator introduced in OTP26"}].

map_gen_to_map(Config) when is_list(Config) ->
?assertEqual(#{1 => 1, 4 => 2, 9 => 3},
power_shell:eval(?MODULE, map_generator_to_map, [])).

map_gen_to_list() ->
[{doc, "Tests handling of map generator introduced in OTP26"}].

map_gen_to_list(Config) when is_list(Config) ->
?assertEqual([{1, 1}, {4, 2}, {9, 3}],
power_shell:eval(?MODULE, map_generator_to_list, [])).

map_gen_to_bin() ->
[{doc, "Tests handling of map generator introduced in OTP26"}].

map_gen_to_bin(Config) when is_list(Config) ->
?assertEqual("abcd",
lists:sort(binary_to_list(power_shell:eval(?MODULE, map_generator_to_binary, [])))).

catch_apply() ->
[{doc, "Tests that cast catch erlang:apply works and throws as expected, not converting it to badarg"}].

Expand Down

0 comments on commit e44a515

Please sign in to comment.