From db39317ea85f7d8646b3da3d96f2f05954e16665 Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Tue, 3 May 2016 10:23:58 +0200 Subject: syntax_tools: Add support for new map type syntax The pretty-printing of `...' in map types is complex. The representation of `...' can be changed before OTP 19. --- lib/syntax_tools/src/erl_prettypr.erl | 57 +++++-- lib/syntax_tools/src/erl_syntax.erl | 167 ++++++++++++++------- .../test/syntax_tools_SUITE_data/type_specs.erl | 7 +- 3 files changed, 167 insertions(+), 64 deletions(-) (limited to 'lib/syntax_tools') diff --git a/lib/syntax_tools/src/erl_prettypr.erl b/lib/syntax_tools/src/erl_prettypr.erl index 9b6c5d977e..119d375746 100644 --- a/lib/syntax_tools/src/erl_prettypr.erl +++ b/lib/syntax_tools/src/erl_prettypr.erl @@ -27,8 +27,6 @@ -module(erl_prettypr). --compile(export_all). - -export([format/1, format/2, best/1, best/2, layout/1, layout/2, get_ctxt_precedence/1, set_ctxt_precedence/2, get_ctxt_paperwidth/1, set_ctxt_paperwidth/2, @@ -1133,20 +1131,25 @@ lay_2(Node, Ctxt) -> text("map()"); Fs -> {Prec, _PrecR} = type_preop_prec('#'), - Es = seq(Fs, - floating(text(",")), reset_prec(Ctxt), - fun lay/2), + Es = lay_map_fields(Fs, + floating(text(",")), + reset_prec(Ctxt)), D = beside(floating(text("#{")), beside(par(Es), floating(text("}")))), maybe_parentheses(D, Prec, Ctxt) end; - map_type_pair -> + map_type_assoc -> + Name = erl_syntax:map_type_assoc_name(Node), + Value = erl_syntax:map_type_assoc_value(Node), + lay_type_assoc(Name, Value, Ctxt); + + map_type_exact -> Ctxt1 = reset_prec(Ctxt), - D1 = lay(erl_syntax:map_type_pair_key(Node), Ctxt1), - D2 = lay(erl_syntax:map_type_pair_value(Node), Ctxt1), - par([D1, floating(text("=>")), D2], Ctxt1#ctxt.break_indent); + D1 = lay(erl_syntax:map_type_exact_name(Node), Ctxt1), + D2 = lay(erl_syntax:map_type_exact_value(Node), Ctxt1), + par([D1, floating(text(":=")), D2], Ctxt1#ctxt.break_indent); integer_range_type -> {PrecL, Prec, PrecR} = type_inop_prec('..'), @@ -1397,6 +1400,42 @@ lay_error_info(T, Ctxt) -> lay_concrete(T, Ctxt) -> lay(erl_syntax:abstract(T), Ctxt). +lay_map_fields([H | T], Separator, Ctxt) -> + case T of + [] -> + [case erl_syntax:type(H) of + map_type_assoc -> + lay_last_type_assoc(H, Ctxt); + _ -> + lay(H, Ctxt) + end]; + _ -> + [maybe_append(Separator, lay(H, Ctxt)) + | lay_map_fields(T, Separator, Ctxt)] + end; +lay_map_fields([], _, _) -> + [empty()]. + +lay_last_type_assoc(Node, Ctxt) -> + Name = erl_syntax:map_type_assoc_name(Node), + Value = erl_syntax:map_type_assoc_value(Node), + IsAny = fun({type,_,any,[]}) -> true; + %% ({var,_,'_'}) -> true; + (_) -> false + end, + case IsAny(Name) andalso IsAny(Value) of + true -> + text("..."); + false -> + lay_type_assoc(Name, Value, Ctxt) + end. + +lay_type_assoc(Name, Value, Ctxt) -> + Ctxt1 = reset_prec(Ctxt), + D1 = lay(Name, Ctxt1), + D2 = lay(Value, Ctxt1), + par([D1, floating(text("=>")), D2], Ctxt1#ctxt.break_indent). + lay_type_application(Name, Arguments, Ctxt) -> {PrecL, Prec} = func_prec(), % D1 = lay(Name, set_prec(Ctxt, PrecL)), diff --git a/lib/syntax_tools/src/erl_syntax.erl b/lib/syntax_tools/src/erl_syntax.erl index 5c252dd749..f4cda814fc 100644 --- a/lib/syntax_tools/src/erl_syntax.erl +++ b/lib/syntax_tools/src/erl_syntax.erl @@ -253,9 +253,12 @@ map_type/0, map_type/1, map_type_fields/1, - map_type_pair/2, - map_type_pair_key/1, - map_type_pair_value/1, + map_type_assoc/2, + map_type_assoc_name/1, + map_type_assoc_value/1, + map_type_exact/2, + map_type_exact_name/1, + map_type_exact_value/1, match_expr/2, match_expr_body/1, match_expr_pattern/1, @@ -509,7 +512,8 @@ %% map_field_exact %% %% map_type -%% map_type_pair +%% map_type_assoc +%% map_type_exact %% match_expr %% module_qualifier %% @@ -596,7 +600,8 @@ %% @see map_field_exact/2 %% @see map_type/0 %% @see map_type/1 -%% @see map_type_pair/2 +%% @see map_type_assoc/2 +%% @see map_type_exact/2 %% @see match_expr/2 %% @see module_qualifier/2 %% @see named_fun_expr/2 @@ -697,7 +702,8 @@ type(Node) -> {type, _, 'fun', []} -> fun_type; {type, _, 'fun', [_, _]} -> function_type; {type, _, map, _} -> map_type; - {type, _, map_field_assoc, _} -> map_type_pair; + {type, _, map_field_assoc, _} -> map_type_assoc; + {type, _, map_field_exact, _} -> map_type_exact; {type, _, record, _} -> record_type; {type, _, field_type, _} -> record_type_field; {type, _, range, _} -> integer_range_type; @@ -5265,69 +5271,118 @@ constraint_body(Node) -> %% ===================================================================== -%% @doc Creates an abstract type map assoc field. The result represents -%% "KeyType => ValueType". +%% @doc Creates an abstract map type assoc field. The result represents +%% "Name => Value". %% -%% @see map_type_pair_key/1 -%% @see map_type_pair_value/1 +%% @see map_type_assoc_name/1 +%% @see map_type_assoc_value/1 +%% @see map_type/1 --record(map_type_pair, {key :: syntaxTree(), - value :: syntaxTree()}). +-record(map_type_assoc, {name :: syntaxTree(), value :: syntaxTree()}). -%% type(Node) = map_type_pair -%% data(Node) = #map_type_pair{key :: KeyType, -%% value :: ValueType} +%% `erl_parse' representation: %% -%% KeyType = syntaxTree() -%% ValueType = syntaxTree() +%% {type, Pos, map_field_assoc, [Name, Value]} + +-spec map_type_assoc(syntaxTree(), syntaxTree()) -> syntaxTree(). + +map_type_assoc(Name, Value) -> + tree(map_type_assoc, #map_type_assoc{name = Name, value = Value}). + +revert_map_type_assoc(Node) -> + Pos = get_pos(Node), + Name = map_type_assoc_name(Node), + Value = map_type_assoc_value(Node), + {type, Pos, map_type_assoc, [Name, Value]}. + + +%% ===================================================================== +%% @doc Returns the name subtree of a `map_type_assoc' node. %% -%% `erl_parse' representation: +%% @see map_type_assoc/2 + +-spec map_type_assoc_name(syntaxTree()) -> syntaxTree(). + +map_type_assoc_name(Node) -> + case Node of + {type, _, map_field_assoc, [Name, _]} -> + Name; + _ -> + (data(Node))#map_type_assoc.name + end. + + +%% ===================================================================== +%% @doc Returns the value subtree of a `map_type_assoc' node. %% -%% {type, Pos, map_field_assoc, [KeyType, ValueType]} +%% @see map_type_assoc/2 + +-spec map_type_assoc_value(syntaxTree()) -> syntaxTree(). + +map_type_assoc_value(Node) -> + case Node of + {type, _, map_field_assoc, [_, Value]} -> + Value; + _ -> + (data(Node))#map_type_assoc.value + end. + + +%% ===================================================================== +%% @doc Creates an abstract map type exact field. The result represents +%% "Name := Value". %% -%% KeyType = erl_parse() -%% ValueType = erl_parse() +%% @see map_type_exact_name/1 +%% @see map_type_exact_value/1 +%% @see map_type/1 + +-record(map_type_exact, {name :: syntaxTree(), value :: syntaxTree()}). + +%% `erl_parse' representation: +%% +%% {type, Pos, map_field_exact, [Name, Value]} --spec map_type_pair(syntaxTree(), syntaxTree()) -> syntaxTree(). +-spec map_type_exact(syntaxTree(), syntaxTree()) -> syntaxTree(). -map_type_pair(KeyType, ValueType) -> - tree(map_type_pair, - #map_type_pair{key = KeyType, value = ValueType}). +map_type_exact(Name, Value) -> + tree(map_type_exact, #map_type_exact{name = Name, value = Value}). -revert_map_type_pair(Node) -> +revert_map_type_exact(Node) -> Pos = get_pos(Node), - KeyType = map_type_pair_key(Node), - ValueType = map_type_pair_value(Node), - {type, Pos, map_field_assoc, [KeyType, ValueType]}. + Name = map_type_exact_name(Node), + Value = map_type_exact_value(Node), + {type, Pos, map_type_exact, [Name, Value]}. + %% ===================================================================== -%% @doc Returns the key type subtrees of a `map_type_pair' node. +%% @doc Returns the name subtree of a `map_type_exact' node. %% -%% @see map_type_pair/2 +%% @see map_type_exact/2 --spec map_type_pair_key(syntaxTree()) -> syntaxTree(). +-spec map_type_exact_name(syntaxTree()) -> syntaxTree(). -map_type_pair_key(Node) -> - case unwrap(Node) of - {type, _, map_field_assoc, [KeyType, _]} -> - KeyType; - Node1 -> - (data(Node1))#map_type_pair.key +map_type_exact_name(Node) -> + case Node of + {type, _, map_field_exact, [Name, _]} -> + Name; + _ -> + (data(Node))#map_type_exact.name end. + %% ===================================================================== -%% @doc Returns the value type subtrees of a `map_type_pair' node. +%% @doc Returns the value subtree of a `map_type_exact' node. %% -%% @see map_type_pair/2 +%% @see map_type_exact/2 --spec map_type_pair_value(syntaxTree()) -> syntaxTree(). +-spec map_type_exact_value(syntaxTree()) -> syntaxTree(). -map_type_pair_value(Node) -> - case unwrap(Node) of - {type, _, map_field_assoc, [_, ValueType]} -> - ValueType; - Node1 -> - (data(Node1))#map_type_pair.value +map_type_exact_value(Node) -> + case Node of + {type, _, map_field_exact, [_, Value]} -> + Value; + _ -> + (data(Node))#map_type_exact.value end. @@ -7428,8 +7483,10 @@ revert_root(Node) -> revert_map_field_exact(Node); map_type -> revert_map_type(Node); - map_type_pair -> - revert_map_type_pair(Node); + map_type_assoc -> + revert_map_type_assoc(Node); + map_type_exact -> + revert_map_type_exact(Node); match_expr -> revert_match_expr(Node); module_qualifier -> @@ -7721,9 +7778,12 @@ subtrees(T) -> [map_field_exact_value(T)]]; map_type -> [map_type_fields(T)]; - map_type_pair -> - [[map_type_pair_key(T)], - [map_type_pair_value(T)]]; + map_type_assoc -> + [[map_type_assoc_name(T)], + [map_type_assoc_value(T)]]; + map_type_exact -> + [[map_type_exact_name(T)], + [map_type_exact_value(T)]]; match_expr -> [[match_expr_pattern(T)], [match_expr_body(T)]]; @@ -7886,7 +7946,8 @@ make_tree(map_expr, [[E], Fs]) -> map_expr(E, Fs); make_tree(map_field_assoc, [[K], [V]]) -> map_field_assoc(K, V); make_tree(map_field_exact, [[K], [V]]) -> map_field_exact(K, V); make_tree(map_type, [Fs]) -> map_type(Fs); -make_tree(map_type_pair, [[K],[V]]) -> map_type_pair(K, V); +make_tree(map_type_assoc, [[N],[V]]) -> map_type_assoc(N, V); +make_tree(map_type_exact, [[N],[V]]) -> map_type_exact(N, V); make_tree(match_expr, [[P], [E]]) -> match_expr(P, E); make_tree(named_fun_expr, [[N], C]) -> named_fun_expr(N, C); make_tree(module_qualifier, [[M], [N]]) -> module_qualifier(M, N); diff --git a/lib/syntax_tools/test/syntax_tools_SUITE_data/type_specs.erl b/lib/syntax_tools/test/syntax_tools_SUITE_data/type_specs.erl index d5a9554f18..5621d3a293 100644 --- a/lib/syntax_tools/test/syntax_tools_SUITE_data/type_specs.erl +++ b/lib/syntax_tools/test/syntax_tools_SUITE_data/type_specs.erl @@ -47,8 +47,11 @@ -type r0() :: #r0{} | #r{f1 :: 3} | #r{f1 :: 3, f2 :: 'sju'}. --type m1() :: #{}. --type m2() :: #{a => m1(), b => #{} | fy:m2()}. +-type m1() :: #{} | map(). +-type m2() :: #{a := m1(), b => #{} | fy:m2()}. +-type m3() :: #{...}. +-type m4() :: #{_ => _, ...}. +-type m5() :: #{any() => any(), ...}. % Currently printed as `#{..., ...}'. -type b1() :: B1 :: binary() | (BitString :: bitstring()). -define(PAIR(A, B), {(A), (B)}). -- cgit v1.2.3