Skip to content
This repository has been archived by the owner on Aug 30, 2019. It is now read-only.

Commit

Permalink
Support "EEP 43: Maps" introduced in Erlang/OTP 17.0
Browse files Browse the repository at this point in the history
  • Loading branch information
depressed-pho committed Jan 20, 2016
1 parent 84f9163 commit 7bdb559
Show file tree
Hide file tree
Showing 5 changed files with 124 additions and 2 deletions.
11 changes: 10 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -195,14 +195,23 @@ you're just defining another list element.
#### Records

-record(vector, { x, y, z }).

test() ->
GetZ = _#vector.z,
7 = GetZ(#vector { z = 7 }),
SetX = _#vector{x = _},
V = #vector{ x = 5, y = 4 } = SetX(#vector{ y = 4 }, 5).


#### Maps

test() ->
GetZ = maps:get(z, _),
7 = GetZ(#{ z => 7 }),
SetX = _#{x => _},
V = #{ x := 5, y := 4 } = SetX(#{ y => 4 }, 5).


#### Case

F = case _ of
Expand Down
57 changes: 57 additions & 0 deletions src/cut.erl
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,10 @@ pattern({cons,Line,H0,T0}) ->
pattern({tuple,Line,Ps0}) ->
Ps1 = pattern_list(Ps0),
{tuple,Line,Ps1};
%% OTP 17.0: EEP 443: Map pattern
pattern({map, Line, Fields0}) ->
Fields1 = map_fields(Fields0),
{map, Line, Fields1};
%%pattern({struct,Line,Tag,Ps0}) ->
%% Ps1 = pattern_list(Ps0),
%% {struct,Line,Tag,Ps1};
Expand Down Expand Up @@ -234,6 +238,27 @@ expr({tuple, Line, Es0}) ->
{'fun', Line, {clauses, [{clause, Line, Pattern, [],
[{tuple, Line, Es2}]}]}}
end;
%% OTP 17.0: EEP 443: Map construction
expr({map, Line, Fields0}) ->
Fields1 = map_fields(Fields0),
case find_map_cut_vars(Fields1) of
{[], _Fields2} ->
{map, Line, Fields1};
{Pattern, Fields2} ->
{'fun', Line, {clauses, [{clause, Line, Pattern, [],
[{map, Line, Fields2}]}]}}
end;
%% OTP 17.0: EEP 443: Map update
expr({map, Line, Expr0, Fields0}) ->
Expr1 = expr(Expr0),
Fields1 = map_fields(Fields0),
case {find_cut_vars([Expr1]), find_map_cut_vars(Fields1)} of
{{[], _Expr2}, {[], _Fields2}} ->
{map, Line, Expr1, Fields1};
{{Pattern1, [Expr2]}, {Pattern2, Fields2}} ->
{'fun', Line, {clauses, [{clause, Line, Pattern1++Pattern2, [],
[{map, Line, Expr2, Fields2}]}]}}
end;
%%expr({struct,Line,Tag,Es0}) ->
%% Es1 = pattern_list(Es0),
%% {struct,Line,Tag,Es1};
Expand Down Expand Up @@ -408,6 +433,17 @@ expr_list([E0|Es]) ->
[E1|expr_list(Es)];
expr_list([]) -> [].

%% -type map_fields([MapField]) -> [MapField].
map_fields([{map_field_assoc, Line, ExpK0, ExpV0}|Fs]) ->
ExpK1 = expr(ExpK0),
ExpV1 = expr(ExpV0),
[{map_field_assoc, Line, ExpK1, ExpV1}|map_fields(Fs)];
map_fields([{map_field_exact, Line, ExpK0, ExpV0}|Fs]) ->
ExpK1 = expr(ExpK0),
ExpV1 = expr(ExpV0),
[{map_field_exact, Line, ExpK1, ExpV1}|map_fields(Fs)];
map_fields([]) -> [].

%% -type record_inits([RecordInit]) -> [RecordInit].
%% N.B. Field names are full expressions here but only atoms are allowed
%% by the *linter*!.
Expand Down Expand Up @@ -482,6 +518,27 @@ find_binary_cut_vars(BinFields) ->
end,
BinFields).

find_map_cut_vars(MapFields) ->
cut_vars(
fun ({map_field_assoc, _Line, {var, _Line1, '_'} = ExpK, {var, _Line2, '_'} = ExpV}) -> [ExpK, ExpV];
({map_field_assoc, _Line, {var, _Line1, '_'} = ExpK, _ExpV}) -> [ExpK];
({map_field_assoc, _Line, _ExpK, {var, _Line1, '_'} = ExpV}) -> [ExpV];
({map_field_assoc, _Line, _ExpK, _ExpV}) -> [];
({map_field_exact, _Line, {var, _Line1, '_'} = ExpK, {var, _Line2, '_'} = ExpV}) -> [ExpK, ExpV];
({map_field_exact, _Line, {var, _Line1, '_'} = ExpK, _ExpV}) -> [ExpK];
({map_field_exact, _Line, _ExpK, {var, _Line1, '_'} = ExpV}) -> [ExpV];
({map_field_exact, _Line, _ExpK, _ExpV}) -> [];
(_) -> []
end,
fun ({map_field_assoc, Line, _ExpK , _ExpV }, [ExpK, ExpV]) -> {map_field_assoc, Line, ExpK, ExpV};
({map_field_assoc, Line, {var, _Line1, '_'}, ExpV }, [ExpK] ) -> {map_field_assoc, Line, ExpK, ExpV};
({map_field_assoc, Line, ExpK , {var, _Line2, '_'}}, [ExpV] ) -> {map_field_assoc, Line, ExpK, ExpV};
({map_field_exact, Line, _ExpK , _ExpV }, [ExpK, ExpV]) -> {map_field_assoc, Line, ExpK, ExpV};
({map_field_exact, Line, {var, _Line1, '_'}, ExpV }, [ExpK] ) -> {map_field_assoc, Line, ExpK, ExpV};
({map_field_exact, Line, ExpK , {var, _Line2, '_'}}, [ExpV] ) -> {map_field_assoc, Line, ExpK, ExpV}
end,
MapFields).

find_record_cut_vars(RecFields) ->
cut_vars(
fun ({record_field, _Line, _FName, {var, _Line1, '_'} = Var}) -> [Var];
Expand Down
24 changes: 24 additions & 0 deletions src/do.erl
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,10 @@ pattern({cons,Line,H0,T0}) ->
pattern({tuple,Line,Ps0}) ->
Ps1 = pattern_list(Ps0),
{tuple,Line,Ps1};
%% OTP 17.0: EEP 443: Map pattern
pattern({map, Line, Fields0}) ->
Fields1 = map_fields(Fields0, []),
{map, Line, Fields1};
%%pattern({struct,Line,Tag,Ps0}) ->
%% Ps1 = pattern_list(Ps0),
%% {struct,Line,Tag,Ps1};
Expand Down Expand Up @@ -201,6 +205,15 @@ expr({bc, Line, E0, Qs0}, MonadStack) ->
expr({tuple, Line, Es0}, MonadStack) ->
Es1 = expr_list(Es0, MonadStack),
{tuple, Line, Es1};
%% OTP 17.0: EEP 443: Map construction
expr({map, Line, Fields0}, MonadStack) ->
Fields1 = map_fields(Fields0, MonadStack),
{map, Line, Fields1};
%% OTP 17.0: EEP 443: Map update
expr({map, Line, Expr0, Fields0}, MonadStack) ->
Expr1 = expr(Expr0, MonadStack),
Fields1 = map_fields(Fields0, MonadStack),
{map, Line, Expr1, Fields1};
expr({record_index, Line, Name, Field0}, MonadStack) ->
Field1 = expr(Field0, MonadStack),
{record_index, Line, Name, Field1};
Expand Down Expand Up @@ -324,6 +337,17 @@ expr_list([E0|Es], MonadStack) ->
[E1|expr_list(Es, MonadStack)];
expr_list([], _MonadStack) -> [].

%% -type map_fields([MapField]) -> [MapField].
map_fields([{map_field_assoc, Line, ExpK0, ExpV0}|Fs], MonadStack) ->
ExpK1 = expr(ExpK0, MonadStack),
ExpV1 = expr(ExpV0, MonadStack),
[{map_field_assoc, Line, ExpK1, ExpV1}|map_fields(Fs, MonadStack)];
map_fields([{map_field_exact, Line, ExpK0, ExpV0}|Fs], MonadStack) ->
ExpK1 = expr(ExpK0, MonadStack),
ExpV1 = expr(ExpV0, MonadStack),
[{map_field_exact, Line, ExpK1, ExpV1}|map_fields(Fs, MonadStack)];
map_fields([], _MoandStack) -> [].

%% -type record_inits([RecordInit]) -> [RecordInit].
%% N.B. Field names are full expressions here but only atoms are allowed
%% by the *linter*!.
Expand Down
23 changes: 23 additions & 0 deletions test/src/test_cut.erl
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,27 @@ test_cut_record_nested() ->
F1 = R#r.f1,
#r{f1 = orange, f3 = banana} = F1(orange, banana).

test_cut_map() ->
true = #{} =/= #{f3 => _},
orange = maps:get(f3, (#{f3 => _})(orange)),
#{f1 := foo, f2 := bar, f3 := baz}
= (#{f1 => _, f3 => _, f2 => _})(foo, baz, bar),
M = #{f1 => false, f2 => wibble, f3 => juice},
F = M#{f3 => _, f2 => _},
wobble = maps:get(f2, F(orange, wobble)),
Getter = maps:get(f2, _),
wibble = Getter(M),
Setter = _#{f2 := gerbil},
gerbil = Getter(Setter(M)),
Setter2 = _#{f2 := _},
hamster = Getter(Setter2(M, hamster)).

test_cut_map_nested() ->
F = #{f1 => #{f1 => _, f3 => _}, f2 => _},
M = F(apple),
F1 = maps:get(f1, M),
#{f1 := orange, f3 := banana} = F1(orange, banana).

test_cut_binary() ->
<<"AbA", _/binary>> = (<<65, _, 65>>)($b),
F = <<_:_>>,
Expand Down Expand Up @@ -140,6 +161,8 @@ test() ->
test_cut_op,
test_cut_unary_op,
test_cut_tuple,
test_cut_map,
test_cut_map_nested,
test_cut_record,
test_cut_record_nested,
test_cut_binary,
Expand Down
11 changes: 10 additions & 1 deletion test/src/test_do.erl
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,14 @@ test_named_fun() ->
end,
true = Fib(10) =:= 55.

test_maps() ->
M1 = do([maybe_m || A = #{ a => b },
X <- return(A),
Y <- return(X#{ a := c, b => d }),
return(Y)
]),
{just, #{ a := c, b := d }} = M1.

test() ->
test:test([{?MODULE, [test_sequence,
test_join,
Expand All @@ -206,5 +214,6 @@ test() ->
test_let_match,
test_let_first,
test_let_escapes,
test_nemd_fun]}],
test_nemd_fun,
test_maps]}],
[report, {name, ?MODULE}]).

0 comments on commit 7bdb559

Please sign in to comment.