Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
home:Ledest:erlang:24
erlang
2401-Fully-support-maps-in-ms_transform.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File 2401-Fully-support-maps-in-ms_transform.patch of Package erlang
From cc043f8a7d52ad48b41bb9dd89e199d811d09164 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20G=C3=B6m=C3=B6ri?= <gomoripeti@gmail.com> Date: Sat, 29 May 2021 12:55:58 +0200 Subject: [PATCH] Fully support maps in ms_transform Before this change only map patterns worked and only in the shell via a hack in `normalise/1` (It converted a map pattern AST to map, but it did not convert a map expressions AST). Now the transformation takes care of map patterns in MS head and map expressions in MS guards/body and `normalise/1` only does what `erl_parse:normalise/1`. Checking restrictions on map keys and values is left to the PAM machine. --- lib/stdlib/src/ms_transform.erl | 21 +++++--- lib/stdlib/test/ms_transform_SUITE.erl | 70 ++++++++++++++++++++++++-- lib/stdlib/test/qlc_SUITE.erl | 6 +-- 3 files changed, 83 insertions(+), 14 deletions(-) diff --git a/lib/stdlib/src/ms_transform.erl b/lib/stdlib/src/ms_transform.erl index f38b0eb905..dde8e572a3 100644 --- a/lib/stdlib/src/ms_transform.erl +++ b/lib/stdlib/src/ms_transform.erl @@ -756,7 +756,12 @@ tg({bin_element,Anno,X,Y,Z},B) -> tg({bin,Anno,List},B) -> {bin,Anno,[tg(X,B) || X <- List]}; - + +tg({map_field_assoc, Anno, Field, Value}, B) -> + {map_field_assoc, Anno, tg(Field, B), tg(Value, B)}; +tg({map, Anno, List}, B) -> + {map, Anno, [tg(X, B) || X <- List]}; + tg(T,B) when is_tuple(T), tuple_size(T) >= 2 -> Element = element(1,T), Anno = element(2,T), @@ -858,6 +863,9 @@ th({var,Anno,Name},B,OB) -> Trans -> {{atom,Anno,Trans},B} end; +th({map_field_exact,Anno,Field,Value},B,OB) -> + {[NField, NValue], NB} = th([Field, Value], B, OB), + {{map_field_assoc,Anno,NField,NValue}, NB}; th([H|T],B,OB) -> {NH,NB} = th(H,B,OB), {NT,NNB} = th(T,NB,OB), @@ -1134,12 +1142,11 @@ normalise({op,_,'++',A,B}) -> normalise(A) ++ normalise(B); normalise({tuple,_,Args}) -> list_to_tuple(normalise_list(Args)); -normalise({map,_,Pairs0}) -> - Pairs1 = lists:map(fun ({map_field_exact,_,K,V}) -> - {normalise(K),normalise(V)} - end, - Pairs0), - maps:from_list(Pairs1); +normalise({map,_,Pairs}) -> + maps:from_list(lists:map(fun + %% only allow '=>' + ({map_field_assoc,_,K,V}) -> {normalise(K),normalise(V)} + end, Pairs)); %% Special case for unary +/-. normalise({op,_,'+',{char,_,I}}) -> I; normalise({op,_,'+',{integer,_,I}}) -> I; diff --git a/lib/stdlib/test/ms_transform_SUITE.erl b/lib/stdlib/test/ms_transform_SUITE.erl index c9dc7e5c1d..c34c7e9e69 100644 --- a/lib/stdlib/test/ms_transform_SUITE.erl +++ b/lib/stdlib/test/ms_transform_SUITE.erl @@ -30,8 +30,14 @@ -export([from_shell/1]). -export([records/1]). -export([record_index/1]). --export([multipass/1]). +-export([map_pattern/1]). +-export([map_expr_in_head/1]). +-export([map_pattern_from_shell/1]). +-export([map_expr_in_head_from_shell/1]). +-export([map_exprs/1]). +-export([map_exprs_from_shell/1]). -export([top_match/1]). +-export([multipass/1]). -export([old_guards/1]). -export([autoimported/1]). -export([semicolon/1]). @@ -63,7 +69,10 @@ all() -> record_index, multipass, bitsyntax, binary_bifs, record_defaults, andalso_orelse, float_1_function, action_function, warnings, no_warnings, top_match, old_guards, autoimported, - semicolon, eep37, otp_14454, otp_16824, unused_record]. + semicolon, eep37, otp_14454, otp_16824, unused_record, + map_pattern, map_expr_in_head, + map_pattern_from_shell, map_expr_in_head_from_shell, + map_exprs, map_exprs_from_shell]. groups() -> []. @@ -316,7 +325,7 @@ basic_ets(Config) when is_list(Config) -> compile_and_run(<<"ets:fun2ms(fun({\"foo\" ++ _, X}) -> X end)">>), ok. -%% Tests basic ets:fun2ms. +%% Tests basic dbg:fun2ms. basic_dbg(Config) when is_list(Config) -> setup(Config), [{[a,b],[],[{message,banan},{return_trace}]}] = @@ -409,6 +418,59 @@ record_index(Config) when is_list(Config) -> <<"ets:fun2ms(fun({#a.a,A}) when A > #a.a -> #a.a end)">>), ok. +map_pattern(Config) when is_list(Config) -> + setup(Config), + MS = [{{key, #{foo => '$1'}},[],['$1']}], + MS = compile_and_run(<<"ets:fun2ms(fun({key, #{foo := V}}) -> V end)">>), + ok. + +map_expr_in_head(Config) when is_list(Config) -> + setup(Config), + MS = [{{key, #{foo => '$1'}},[],['$1']}], + %% Accidentally it is possible to use => instead of := in the fun head, + %% in compiled code. + %% Although this is not an intended behaviour it is kept to + %% maintain backwards compatibility. + MS = compile_and_run(<<"ets:fun2ms(fun({key, #{foo => V}}) -> V end)">>), + ok. + +map_pattern_from_shell(Config) when is_list(Config) -> + MS = [{{key, #{foo => '$1'}},[],['$1']}], + MS = do_eval("ets:fun2ms(fun({key, #{foo := V}}) -> V end)"), + ok. + +map_expr_in_head_from_shell(Config) when is_list(Config) -> + setup(Config), + MS = [{{key, #{foo => '$1'}},[],['$1']}], + %% Accidentally it is possible to use => instead of := in the fun head, + %% in compiled code. This behaviour is kept for backwards compatibility. + + %% As a side-effect, it is also possible to do the same with + %% `transform_from_shell/3', if the AST of the shell fun is + %% created bypassing the linter. (The linter would prevent + %% constructing such invalid syntax, so normally this is not + %% possible in the Erlang shell) + MS = do_eval("ets:fun2ms(fun({key, #{foo => V}}) -> V end)"), + ok. + +map_exprs(Config) when is_list(Config) -> + setup(Config), + MSGuard = [{{key,'$1','$2'}, [{'=:=','$1',#{foo => '$2'}}], ['$1']}], + MSGuard = compile_and_run( + <<"ets:fun2ms(fun({key, V1, V2}) when V1 =:= #{foo => V2} -> V1 end)">>), + MSBody = [{{key,'$1'}, [], [#{foo => '$1'}]}], + MSBody = compile_and_run( + <<"ets:fun2ms(fun({key, V}) -> #{foo => V} end)">>), + ok. + +map_exprs_from_shell(Config) when is_list(Config) -> + setup(Config), + MSGuard = [{{key,'$1','$2'}, [{'=:=','$1',#{foo => '$2'}}], ['$1']}], + MSGuard = do_eval("ets:fun2ms(fun({key, V1, V2}) when V1 =:= #{foo => V2} -> V1 end)"), + MSBody = [{{key,'$1'}, [], [#{foo => '$1'}]}], + MSBody = do_eval("ets:fun2ms(fun({key, V}) -> #{foo => V} end)"), + ok. + %% Tests matching on top level in head to give alias for object(). top_match(Config) when is_list(Config) -> setup(Config), @@ -924,5 +986,5 @@ do_eval(String) -> [], String++".\n",1), {ok,Tree} = erl_parse:parse_exprs(T), - {value,Res,[]} = erl_eval:exprs(Tree,[]), + {value,Res,[]} = erl_eval:exprs(Tree,[],none,none), Res. diff --git a/lib/stdlib/test/qlc_SUITE.erl b/lib/stdlib/test/qlc_SUITE.erl index 3bbb2a7e45..74c8aabf8e 100644 --- a/lib/stdlib/test/qlc_SUITE.erl +++ b/lib/stdlib/test/qlc_SUITE.erl @@ -2632,9 +2632,9 @@ info(Config) when is_list(Config) -> L = [{#{k => #{v => Fun}}, Fun}], H = qlc:q([Q || Q <- L, Q =:= {#{k => #{v => Fun}}, Fun}]), L = qlc:e(H), - {call,_,_,[{lc,_,{var,_,'Q'}, - [{generate,_,_,_}, - {op,_,_,_,_}]}]} = + {call,_,{remote,_,{atom,_,ets},{atom,_,match_spec_run}}, + [_, + {call,_,{remote,_,{atom,_,ets},{atom,_,match_spec_compile}},[_]}]} = qlc:info(H, [{format,abstract_code}])">> ], -- 2.34.1
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor