Skip to content

Commit

Permalink
[EEP 78] Implement erl_eval for multi map-comprehensions
Browse files Browse the repository at this point in the history
  • Loading branch information
michalmuskala committed Feb 3, 2025
1 parent f40d278 commit dfe64f8
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 13 deletions.
31 changes: 19 additions & 12 deletions lib/stdlib/src/erl_eval.erl
Original file line number Diff line number Diff line change
Expand Up @@ -1253,30 +1253,37 @@ 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,
%% eval_mc(ExprOrExprs, [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, []),
eval_mc(Es, Qs, Bs, Lf, Ef, RBs, FUVs) when is_list(Es) ->
L = eval_mc1(Es, Qs, Bs, Lf, Ef, FUVs, []),
Map = maps:from_list(reverse(L)),
ret_expr(Map, Bs, RBs).
ret_expr(Map, Bs, RBs);
eval_mc(E, Qs, Bs, Lf, Ef, RBs, FUVs) ->
eval_mc([E], Qs, Bs, Lf, Ef, RBs, FUVs).

eval_mc1(E, [{zip, Anno, Gens}|Qs], Bs0, Lf, Ef, FUVs, Acc0) ->
eval_mc1(Es, [{zip, Anno, Gens}|Qs], Bs0, Lf, Ef, FUVs, Acc0) ->
{VarList, Bs1} = convert_gen_values(Gens, [], Bs0, Lf, Ef, FUVs),
eval_zip(E, [{zip, Anno, VarList}|Qs], Bs1, Lf, Ef, FUVs, Acc0, fun eval_mc1/7);
eval_mc1(E, [Q|Qs], Bs0, Lf, Ef, FUVs, Acc0) ->
eval_zip(Es, [{zip, Anno, VarList}|Qs], Bs1, Lf, Ef, FUVs, Acc0, fun eval_mc1/7);
eval_mc1(Es, [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,
CF = fun(Bs, Acc) -> eval_mc1(Es, 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,
CF = fun(Bs) -> eval_mc1(Es, 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_mc1(Es, [], Bs, Lf, Ef, FUVs, Acc) ->
eval_mc2(Es, Bs, Lf, Ef, FUVs, Acc).

eval_mc2([{map_field_assoc,_,K0,V0}|Es], Bs, Lf, Ef, FUVs, Acc) ->
{[K,V],_} = expr_list([K0,V0], Bs, Lf, Ef, FUVs),
eval_mc2(Es, Bs, Lf, Ef, FUVs, [{K,V}|Acc]);
eval_mc2([], _Bs, _Lf, _Ef, _FUVs, Acc) ->
Acc.

eval_zip(E, [{zip, Anno, VarList}|Qs], Bs0, Lf, Ef, FUVs, Acc0, Fun) ->
Gens = case check_bad_generators(VarList, {Bs0, Lf, Ef}, []) of
Expand Down
5 changes: 5 additions & 0 deletions lib/stdlib/src/erl_lint.erl
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,8 @@ format_error_1(update_literal) ->
~"expression updates a literal";
format_error_1(illegal_zip_generator) ->
~"only generators are allowed in a zip generator.";
format_error_1(illegal_map_exact_in_comprehension) ->
~"illegal map association, did you mean to use `=>`?";
%% --- patterns and guards ---
format_error_1(illegal_map_assoc_in_pattern) -> ~"illegal pattern, did you mean to use `:=`?";
format_error_1(illegal_pattern) -> ~"illegal pattern";
Expand Down Expand Up @@ -4060,6 +4062,9 @@ comprehension_exprs(E, Vt, St) ->

comprehension_expr({map_field_assoc,_,K,V}, Vt0, St0) ->
expr_list([K,V], Vt0, St0);
comprehension_expr({map_field_exact,A,K,V}, Vt0, St0) ->
St = add_error(A, illegal_map_exact_in_comprehension, St0),
expr_list([K,V], Vt0, St);
comprehension_expr(E, Vt, St) ->
expr(E, Vt, St).

Expand Down
13 changes: 12 additions & 1 deletion lib/stdlib/test/erl_eval_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
zbc/1,
zmc/1,
multi_lc/1,
multi_mc/1,
simple_cases/1,
unary_plus/1,
apply_atom/1,
Expand Down Expand Up @@ -105,7 +106,7 @@ all() ->
funs, custom_stacktrace, try_catch, eval_expr_5, zero_width,
eep37, eep43, otp_15035, otp_16439, otp_14708, otp_16545, otp_16865,
eep49, binary_and_map_aliases, eep58, strict_generators, binary_skip,
zlc, zbc, zmc, multi_lc].
zlc, zbc, zmc, multi_lc, multi_mc].

groups() ->
[].
Expand Down Expand Up @@ -447,6 +448,16 @@ multi_lc(Config) when is_list(Config) ->
"[1, 2 || _ <- [1, 2]].",
[1, 2, 1, 2]).

multi_mc(Config) when is_list(Config) ->
check(fun() -> #{A => B || X <- [1, 5], {A, B} <- [{X, X+1}, {X+2, X+3}]} end,
"#{X => X+1, X+2 => X+3 || X <- [1, 5]}.",
#{1 => 2, 3 => 4, 5 => 6, 7 => 8}),
check(fun() -> #{A => B || X <- [1, 5], {A, B} <- [{X, X+1}, {X, X+3}]} end,
"#{X => X+1, X => X+3 || X <- [1, 5]}.",
#{1 => 4, 5 => 8}),
error_check("#{1 := 2 || _ <- []}.", illegal_map_exact_in_comprehension),
error_check("#{1 => 2, 3 := 4 || _ <- []}.", illegal_map_exact_in_comprehension).

%% Simple cases, just to cover some code.
simple_cases(Config) when is_list(Config) ->
check(fun() -> A = $C end, "A = $C.", $C),
Expand Down

0 comments on commit dfe64f8

Please sign in to comment.