From 7bdb5593c5912c36c4bb6ad90653f61901ab6455 Mon Sep 17 00:00:00 2001 From: PHO Date: Wed, 20 Jan 2016 14:05:37 +0900 Subject: [PATCH] Support "EEP 43: Maps" introduced in Erlang/OTP 17.0 See http://www.erlang.org/eeps/eep-0043.html --- README.md | 11 ++++++++- src/cut.erl | 57 +++++++++++++++++++++++++++++++++++++++++++ src/do.erl | 24 ++++++++++++++++++ test/src/test_cut.erl | 23 +++++++++++++++++ test/src/test_do.erl | 11 ++++++++- 5 files changed, 124 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e7e4390..09e94cc 100644 --- a/README.md +++ b/README.md @@ -195,7 +195,7 @@ you're just defining another list element. #### Records -record(vector, { x, y, z }). - + test() -> GetZ = _#vector.z, 7 = GetZ(#vector { z = 7 }), @@ -203,6 +203,15 @@ you're just defining another list element. 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 diff --git a/src/cut.erl b/src/cut.erl index 72d7845..5622003 100644 --- a/src/cut.erl +++ b/src/cut.erl @@ -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}; @@ -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}; @@ -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*!. @@ -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]; diff --git a/src/do.erl b/src/do.erl index a587071..3e74c92 100644 --- a/src/do.erl +++ b/src/do.erl @@ -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}; @@ -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}; @@ -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*!. diff --git a/test/src/test_cut.erl b/test/src/test_cut.erl index 6d0c337..4480e0a 100644 --- a/test/src/test_cut.erl +++ b/test/src/test_cut.erl @@ -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 = <<_:_>>, @@ -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, diff --git a/test/src/test_do.erl b/test/src/test_do.erl index 60e4436..c0b8547 100644 --- a/test/src/test_do.erl +++ b/test/src/test_do.erl @@ -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, @@ -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}]).