From ca1c1f8d1a4fe4f7b19b9959c48bd64915215f24 Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Wed, 20 May 2015 12:45:37 +0200 Subject: stdlib: Introduce precedence for operators in types Add new functions erl_parse:type_inop_prec() and erl_parse:type_preop_prec(). Get rid of paren_type used for parentheses in types. --- lib/stdlib/src/erl_parse.yrl | 64 +++++++++++++------- lib/stdlib/src/erl_pp.erl | 127 +++++++++++++++++++++------------------ lib/stdlib/test/erl_pp_SUITE.erl | 2 +- 3 files changed, 111 insertions(+), 82 deletions(-) diff --git a/lib/stdlib/src/erl_parse.yrl b/lib/stdlib/src/erl_parse.yrl index e328e065e3..274bb2a782 100644 --- a/lib/stdlib/src/erl_parse.yrl +++ b/lib/stdlib/src/erl_parse.yrl @@ -125,22 +125,19 @@ top_type_100 -> type_200 : '$1'. top_type_100 -> type_200 '|' top_type_100 : lift_unions('$1','$3'). type_200 -> type_300 '..' type_300 : {type, ?anno('$1'), range, - [skip_paren('$1'), - skip_paren('$3')]}. + ['$1', '$3']}. type_200 -> type_300 : '$1'. -type_300 -> type_300 add_op type_400 : ?mkop2(skip_paren('$1'), - '$2', skip_paren('$3')). +type_300 -> type_300 add_op type_400 : ?mkop2('$1', '$2', '$3'). type_300 -> type_400 : '$1'. -type_400 -> type_400 mult_op type_500 : ?mkop2(skip_paren('$1'), - '$2', skip_paren('$3')). +type_400 -> type_400 mult_op type_500 : ?mkop2('$1', '$2', '$3'). type_400 -> type_500 : '$1'. -type_500 -> prefix_op type : ?mkop1('$1', skip_paren('$2')). +type_500 -> prefix_op type : ?mkop1('$1', '$2'). type_500 -> type : '$1'. -type -> '(' top_type ')' : {paren_type, ?anno('$2'), ['$2']}. +type -> '(' top_type ')' : '$2'. type -> var : '$1'. type -> atom : '$1'. type -> atom '(' ')' : build_gen_type('$1'). @@ -524,6 +521,7 @@ Erlang code. -export([normalise/1,abstract/1,tokens/1,tokens/2]). -export([abstract/2]). -export([inop_prec/1,preop_prec/1,func_prec/0,max_prec/0]). +-export([type_inop_prec/1,type_preop_prec/1]). -export([map_anno/2, fold_anno/3, mapfold_anno/3, new_anno/1, anno_to_term/1, anno_from_term/1]). -export([set_line/2,get_attribute/2,get_attributes/1]). @@ -671,11 +669,6 @@ lift_unions(T1, {type, _Aa, union, List}) -> lift_unions(T1, T2) -> {type, ?anno(T1), union, [T1, T2]}. -skip_paren({paren_type,_A,[Type]}) -> - skip_paren(Type); -skip_paren(Type) -> - Type. - build_gen_type({atom, Aa, tuple}) -> {type, Aa, tuple, any}; build_gen_type({atom, Aa, map}) -> @@ -687,7 +680,7 @@ build_gen_type({atom, Aa, Name}) -> build_bin_type([{var, _, '_'}|Left], Int) -> build_bin_type(Left, Int); build_bin_type([], Int) -> - skip_paren(Int); + Int; build_bin_type([{var, Aa, _}|_], _) -> ret_err(Aa, "Bad binary type"). @@ -807,8 +800,7 @@ record_fields([{typed,Expr,TypeInfo}|Fields]) -> {atom, Aa, _} -> case has_undefined(TypeInfo) of false -> - TypeInfo2 = maybe_add_paren(TypeInfo), - lift_unions(abstract2(undefined, Aa), TypeInfo2); + lift_unions(abstract2(undefined, Aa), TypeInfo); true -> TypeInfo end @@ -822,18 +814,11 @@ has_undefined({atom,_,undefined}) -> true; has_undefined({ann_type,_,[_,T]}) -> has_undefined(T); -has_undefined({paren_type,_,[T]}) -> - has_undefined(T); has_undefined({type,_,union,Ts}) -> lists:any(fun has_undefined/1, Ts); has_undefined(_) -> false. -maybe_add_paren({ann_type,A,T}) -> - {paren_type,A,[{ann_type,A,T}]}; -maybe_add_paren(T) -> - T. - term(Expr) -> try normalise(Expr) catch _:_R -> ret_err(?anno(Expr), "bad attribute") @@ -1099,6 +1084,39 @@ func_prec() -> {800,700}. max_prec() -> 900. +-type prec() :: non_neg_integer(). + +-type type_inop() :: '::' | '|' | '..' | '+' | '-' | 'bor' | 'bxor' + | 'bsl' | 'bsr' | '*' | '/' | 'div' | 'rem' | 'band'. + +-type type_preop() :: '+' | '-' | 'bnot' | '#'. + +-spec type_inop_prec(type_inop()) -> {prec(), prec(), prec()}. + +type_inop_prec('=') -> {150,100,100}; +type_inop_prec('::') -> {160,150,150}; +type_inop_prec('|') -> {180,170,170}; +type_inop_prec('..') -> {300,200,300}; +type_inop_prec('+') -> {400,400,500}; +type_inop_prec('-') -> {400,400,500}; +type_inop_prec('bor') -> {400,400,500}; +type_inop_prec('bxor') -> {400,400,500}; +type_inop_prec('bsl') -> {400,400,500}; +type_inop_prec('bsr') -> {400,400,500}; +type_inop_prec('*') -> {500,500,600}; +type_inop_prec('/') -> {500,500,600}; +type_inop_prec('div') -> {500,500,600}; +type_inop_prec('rem') -> {500,500,600}; +type_inop_prec('band') -> {500,500,600}; +type_inop_prec('#') -> {800,700,800}. + +-spec type_preop_prec(type_preop()) -> {prec(), prec()}. + +type_preop_prec('+') -> {600,700}; +type_preop_prec('-') -> {600,700}; +type_preop_prec('bnot') -> {600,700}; +type_preop_prec('#') -> {700,800}. + %%% [Experimental]. The parser just copies the attributes of the %%% scanner tokens to the abstract format. This design decision has %%% been hidden to some extent: use set_line() and get_attribute() to diff --git a/lib/stdlib/src/erl_pp.erl b/lib/stdlib/src/erl_pp.erl index 623a29f923..6da585b72e 100644 --- a/lib/stdlib/src/erl_pp.erl +++ b/lib/stdlib/src/erl_pp.erl @@ -27,7 +27,8 @@ -import(lists, [append/1,foldr/3,mapfoldl/3,reverse/1,reverse/2]). -import(io_lib, [write/1,format/2]). --import(erl_parse, [inop_prec/1,preop_prec/1,func_prec/0,max_prec/0]). +-import(erl_parse, [inop_prec/1,preop_prec/1,func_prec/0,max_prec/0, + type_inop_prec/1, type_preop_prec/1]). -define(MAXLINE, 72). @@ -271,49 +272,64 @@ typeattr(Tag, {TypeName,Type,Args}, _Opts) -> {first,leaf("-"++atom_to_list(Tag)++" "), typed(call({atom,a0(),TypeName}, Args, 0, options(none)), Type)}. -ltype({ann_type,_Line,[V,T]}) -> - typed(lexpr(V, options(none)), T); -ltype({paren_type,_Line,[T]}) -> - [$(,ltype(T),$)]; -ltype({type,_Line,union,Ts}) -> - {seq,[],[],[' |'],ltypes(Ts)}; -ltype({type,_Line,list,[T]}) -> +ltype(T) -> + ltype(T, 0). + +ltype({ann_type,_Line,[V,T]}, Prec) -> + {_L,P,_R} = type_inop_prec('::'), + E = typed(lexpr(V, options(none)), T), + maybe_paren(P, Prec, E); +ltype({paren_type,_Line,[T]}, P) -> + %% Generated before Erlang/OTP 18. + ltype(T, P); +ltype({type,_Line,union,Ts}, Prec) -> + {_L,P,R} = type_inop_prec('|'), + E = {seq,[],[],[' |'],ltypes(Ts, R)}, + maybe_paren(P, Prec, E); +ltype({type,_Line,list,[T]}, _) -> {seq,$[,$],$,,[ltype(T)]}; -ltype({type,_Line,nonempty_list,[T]}) -> +ltype({type,_Line,nonempty_list,[T]}, _) -> {seq,$[,$],[$,],[ltype(T),leaf("...")]}; -ltype({type,Line,nil,[]}) -> - lexpr({nil,Line}, 0, options(none)); -ltype({type,Line,map,any}) -> +ltype({type,Line,nil,[]}, _) -> + lexpr({nil,Line}, options(none)); +ltype({type,Line,map,any}, _) -> simple_type({atom,Line,map}, []); -ltype({type,_Line,map,Pairs}) -> - map_type(Pairs); -ltype({type,Line,tuple,any}) -> +ltype({type,_Line,map,Pairs}, Prec) -> + {P,_R} = type_preop_prec('#'), + E = map_type(Pairs), + maybe_paren(P, Prec, E); +ltype({type,Line,tuple,any}, _) -> simple_type({atom,Line,tuple}, []); -ltype({type,_Line,tuple,Ts}) -> - tuple_type(Ts, fun ltype/1); -ltype({type,_Line,record,[{atom,_,N}|Fs]}) -> - record_type(N, Fs); -ltype({type,_Line,range,[_I1,_I2]=Es}) -> - expr_list(Es, '..', fun lexpr/2, options(none)); -ltype({type,_Line,binary,[I1,I2]}) -> +ltype({type,_Line,tuple,Ts}, _) -> + tuple_type(Ts, fun ltype/2); +ltype({type,_Line,record,[{atom,_,N}|Fs]}, Prec) -> + {P,_R} = type_preop_prec('#'), + E = record_type(N, Fs), + maybe_paren(P, Prec, E); +ltype({type,_Line,range,[_I1,_I2]=Es}, Prec) -> + {_L,P,R} = type_inop_prec('..'), + F = fun(E, Opts) -> lexpr(E, R, Opts) end, + E = expr_list(Es, '..', F, options(none)), + maybe_paren(P, Prec, E); +ltype({type,_Line,binary,[I1,I2]}, _) -> binary_type(I1, I2); % except binary() -ltype({type,_Line,'fun',[]}) -> +ltype({type,_Line,'fun',[]}, _) -> leaf("fun()"); -ltype({type,_,'fun',[{type,_,any},_]}=FunType) -> +ltype({type,_,'fun',[{type,_,any},_]}=FunType, _) -> [fun_type(['fun',$(], FunType),$)]; -ltype({type,_Line,'fun',[{type,_,product,_},_]}=FunType) -> +ltype({type,_Line,'fun',[{type,_,product,_},_]}=FunType, _) -> [fun_type(['fun',$(], FunType),$)]; -ltype({type,Line,T,Ts}) -> +ltype({type,Line,T,Ts}, _) -> %% Compatibility. Before 18.0. simple_type({atom,Line,T}, Ts); -ltype({user_type,Line,T,Ts}) -> +ltype({user_type,Line,T,Ts}, _) -> simple_type({atom,Line,T}, Ts); -ltype({remote_type,Line,[M,F,Ts]}) -> +ltype({remote_type,Line,[M,F,Ts]}, _) -> simple_type({remote,Line,M,F}, Ts); -ltype({atom,_,T}) -> +ltype({atom,_,T}, _) -> leaf(write(T)); -ltype(E) -> - lexpr(E, 0, options(none)). +ltype(E, P) -> + lexpr(E, P, options(none)). binary_type(I1, I2) -> B = [[] || {integer,_,0} <- [I1]] =:= [], @@ -327,42 +343,37 @@ map_type(Fs) -> {first,[$#],map_pair_types(Fs)}. map_pair_types(Fs) -> - tuple_type(Fs, fun map_pair_type/1). + tuple_type(Fs, fun map_pair_type/2). -map_pair_type({type,_Line,map_field_assoc,[Ktype,Vtype]}) -> - map_assoc_typed(ltype(Ktype), Vtype). +map_pair_type({type,_Line,map_field_assoc,[Ktype,Vtype]}, Prec) -> + map_assoc_typed(ltype(Ktype), Vtype, Prec). -map_assoc_typed(B, {type,_,union,Ts}) -> - {first,[B,$\s],{seq,[],[],[],map_assoc_union_type(Ts)}}; -map_assoc_typed(B, Type) -> - {list,[{cstep,[B," =>"],ltype(Type)}]}. +map_assoc_typed(B, {type,_,union,Ts}, Prec) -> + {first,[B,$\s],{seq,[],[],[],map_assoc_union_type(Ts, Prec)}}; +map_assoc_typed(B, Type, Prec) -> + {list,[{cstep,[B," =>"],ltype(Type, Prec)}]}. -map_assoc_union_type([T|Ts]) -> - [[leaf("=> "),ltype(T)] | ltypes(Ts, fun union_elem/1)]. +map_assoc_union_type([T|Ts], Prec) -> + [[leaf("=> "),ltype(T)] | ltypes(Ts, fun union_elem/2, Prec)]. record_type(Name, Fields) -> {first,[record_name(Name)],field_types(Fields)}. field_types(Fs) -> - tuple_type(Fs, fun field_type/1). + tuple_type(Fs, fun field_type/2). -field_type({type,_Line,field_type,[Name,Type]}) -> +field_type({type,_Line,field_type,[Name,Type]}, _Prec) -> typed(lexpr(Name, options(none)), Type). -typed(B, {type,_,union,Ts}) -> - %% Special layout for :: followed by union. - {first,[B,$\s],{seq,[],[],[],union_type(Ts)}}; typed(B, Type) -> - {list,[{cstep,[B,' ::'],ltype(Type)}]}. + {_L,_P,R} = type_inop_prec('::'), + {list,[{cstep,[B,' ::'],ltype(Type, R)}]}. -union_type([T|Ts]) -> - [[leaf(":: "),ltype(T)] | ltypes(Ts, fun union_elem/1)]. - -union_elem(T) -> - [leaf(" | "),ltype(T)]. +union_elem(T, Prec) -> + [leaf(" | "),ltype(T, Prec)]. tuple_type(Ts, F) -> - {seq,${,$},[$,],ltypes(Ts, F)}. + {seq,${,$},[$,],ltypes(Ts, F, 0)}. specattr(SpecKind, {FuncSpec,TypeSpecs}) -> Func = case FuncSpec of @@ -399,16 +410,16 @@ type_args({type,_line,product,Ts}) -> targs(Ts). simple_type(Tag, Types) -> - {first,lexpr(Tag, 0, options(none)),targs(Types)}. + {first,lexpr(Tag, options(none)),targs(Types)}. targs(Ts) -> - {seq,$(,$),[$,],ltypes(Ts)}. + {seq,$(,$),[$,],ltypes(Ts, 0)}. -ltypes(Ts) -> - ltypes(Ts, fun ltype/1). +ltypes(Ts, Prec) -> + ltypes(Ts, fun ltype/2, Prec). -ltypes(Ts, F) -> - [F(T) || T <- Ts]. +ltypes(Ts, F, Prec) -> + [F(T, Prec) || T <- Ts]. attr(Name, Args) -> call({var,a0(),format("-~s", [Name])}, Args, 0, options(none)). diff --git a/lib/stdlib/test/erl_pp_SUITE.erl b/lib/stdlib/test/erl_pp_SUITE.erl index 1d63c8e17e..afeeb5bfd4 100644 --- a/lib/stdlib/test/erl_pp_SUITE.erl +++ b/lib/stdlib/test/erl_pp_SUITE.erl @@ -1149,7 +1149,7 @@ otp_11100(Config) when is_list(Config) -> {a,{type,A1,range,[{integer,A1,1},{foo,bar}]},[]}}), "-type foo(INVALID-FORM:{foo,bar}:) :: A.\n" = pf({attribute,A1,type,{foo,{var,A1,'A'},[{foo,bar}]}}), - "-type foo() :: (INVALID-FORM:{foo,bar}: :: []).\n" = + "-type foo() :: INVALID-FORM:{foo,bar}: :: [].\n" = pf({attribute,A1,type, {foo,{paren_type,A1, [{ann_type,A1,[{foo,bar},{type,A1,nil,[]}]}]}, -- cgit v1.2.3 From b4374be62fcb48ae404b4c7eedacb69ecb5442ad Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Fri, 22 May 2015 14:59:05 +0200 Subject: edoc: Add parentheses to Erlang types and specs when needed Before OTP 18, parentheses are kept by the Erlang Parser, and converted by EDoc ('paren_type' to #t_paren{}). As of OTP 18, the parser no longer keeps parentheses, why EDoc needs to insert them when converting Erlang types and specs to EDoc types and specs. As it seems, it is only annotations that sometimes require parentheses. --- lib/edoc/src/edoc_layout.erl | 2 +- lib/edoc/src/edoc_specs.erl | 142 +++++++++++++++++++++++++------------------ 2 files changed, 83 insertions(+), 61 deletions(-) diff --git a/lib/edoc/src/edoc_layout.erl b/lib/edoc/src/edoc_layout.erl index 62d5eb9a18..b67ec31ae3 100644 --- a/lib/edoc/src/edoc_layout.erl +++ b/lib/edoc/src/edoc_layout.erl @@ -520,7 +520,7 @@ format_spec(Name, Type, Defs, #opts{pretty_printer = erl_pp}=Opts) -> {R, ".\n"} = etypef(L, O), [{pre, R}] catch _:_ -> - %% Example: "@spec ... -> record(a)" + %% Should not happen. format_spec(Name, Type, Defs, Opts#opts{pretty_printer=''}) end; format_spec(Sep, Type, Defs, _Opts) -> diff --git a/lib/edoc/src/edoc_specs.erl b/lib/edoc/src/edoc_specs.erl index 59f6cb8ddf..eb69058148 100644 --- a/lib/edoc/src/edoc_specs.erl +++ b/lib/edoc/src/edoc_specs.erl @@ -295,47 +295,54 @@ arg_name([A | As], Default) -> is_name(A) -> is_atom(A). -d2e({ann_type,_,[V, T0]}) -> +d2e(T) -> + d2e(T, 0). + +d2e({ann_type,_,[V, T0]}, Prec) -> %% Note: the -spec/-type syntax allows annotations everywhere, but %% EDoc does not. The fact that the annotation is added to the %% type here does not necessarily mean that it will be used by the %% layout module. - T = d2e(T0), - ?add_t_ann(T, element(3, V)); -d2e({remote_type,_,[{atom,_,M},{atom,_,F},Ts0]}) -> + {_L,P,R} = erl_parse:type_inop_prec('::'), + T1 = d2e(T0, R), + T = ?add_t_ann(T1, element(3, V)), + maybe_paren(P, Prec, T); % the only necessary call to maybe_paren() +d2e({remote_type,_,[{atom,_,M},{atom,_,F},Ts0]}, _Prec) -> Ts = d2e(Ts0), typevar_anno(#t_type{name = #t_name{module = M, name = F}, args = Ts}, Ts); -d2e({type,_,'fun',[{type,_,product,As0},Ran0]}) -> +d2e({type,_,'fun',[{type,_,product,As0},Ran0]}, _Prec) -> Ts = [Ran|As] = d2e([Ran0|As0]), %% Assume that the linter has checked type variables. typevar_anno(#t_fun{args = As, range = Ran}, Ts); -d2e({type,_,'fun',[A0={type,_,any},Ran0]}) -> +d2e({type,_,'fun',[A0={type,_,any},Ran0]}, _Prec) -> Ts = [A, Ran] = d2e([A0, Ran0]), typevar_anno(#t_fun{args = [A], range = Ran}, Ts); -d2e({type,_,'fun',[]}) -> +d2e({type,_,'fun',[]}, _Prec) -> #t_type{name = #t_name{name = function}, args = []}; -d2e({type,_,any}) -> +d2e({type,_,any}, _Prec) -> #t_var{name = '...'}; % Kludge... not a type variable! -d2e({type,_,nil,[]}) -> +d2e({type,_,nil,[]}, _Prec) -> #t_nil{}; -d2e({paren_type,_,[T]}) -> - #t_paren{type = d2e(T)}; -d2e({type,_,list,[T0]}) -> +d2e({paren_type,_,[T]}, Prec) -> + d2e(T, Prec); +d2e({type,_,list,[T0]}, _Prec) -> T = d2e(T0), typevar_anno(#t_list{type = T}, [T]); -d2e({type,_,nonempty_list,[T0]}) -> +d2e({type,_,nonempty_list,[T0]}, _Prec) -> T = d2e(T0), typevar_anno(#t_nonempty_list{type = T}, [T]); -d2e({type,_,bounded_fun,[T,Gs]}) -> +d2e({type,_,bounded_fun,[T,Gs]}, _Prec) -> [F0|Defs] = d2e([T|Gs]), F = ?set_t_ann(F0, lists:keydelete(type_variables, 1, ?t_ann(F0))), %% Assume that the linter has checked type variables. #t_spec{type = typevar_anno(F, [F0]), defs = Defs}; -d2e({type,_,range,[V1,V2]}) -> +d2e({type,_,range,[V1,V2]}, Prec) -> + {_L,P,_R} = erl_parse:type_inop_prec('..'), {integer,_,I1} = erl_eval:partial_eval(V1), {integer,_,I2} = erl_eval:partial_eval(V2), - #t_integer_range{from = I1, to = I2}; -d2e({type,_,constraint,[Sub,Ts0]}) -> + T0 = #t_integer_range{from = I1, to = I2}, + maybe_paren(P, Prec, T0); +d2e({type,_,constraint,[Sub,Ts0]}, _Prec) -> case {Sub,Ts0} of {{atom,_,is_subtype},[{var,_,N},T0]} -> Ts = [T] = d2e([T0]), @@ -347,50 +354,60 @@ d2e({type,_,constraint,[Sub,Ts0]}) -> _ -> throw_error(get_line(element(2, Sub)), "cannot handle guard", []) end; -d2e({type,_,union,Ts0}) -> - Ts = d2e(Ts0), - typevar_anno(#t_union{types = Ts}, Ts); -d2e({type,_,tuple,any}) -> +d2e({type,_,union,Ts0}, Prec) -> + {_L,P,R} = erl_parse:type_inop_prec('|'), + Ts = d2e(Ts0, R), + T = maybe_paren(P, Prec, #t_union{types = Ts}), + typevar_anno(T, Ts); +d2e({type,_,tuple,any}, _Prec) -> #t_type{name = #t_name{name = tuple}, args = []}; -d2e({type,_,binary,[Base,Unit]}) -> - #t_binary{base_size = element(3, Base), - unit_size = element(3, Unit)}; -d2e({type,_,map,any}) -> - #t_map{ types = []}; -d2e({type,_,map,Es}) -> - #t_map{ types = d2e(Es) }; -d2e({type,_,map_field_assoc,[K,V]}) -> - #t_map_field{ k_type = d2e(K), v_type=d2e(V) }; -d2e({type,_,map_field_exact,K,V}) -> - #t_map_field{ k_type = d2e(K), v_type=d2e(V) }; -d2e({type,_,tuple,Ts0}) -> +d2e({type,_,binary,[Base,Unit]}, _Prec) -> + {integer,_,B} = erl_eval:partial_eval(Base), + {integer,_,U} = erl_eval:partial_eval(Unit), + #t_binary{base_size = B, unit_size = U}; +d2e({type,_,map,any}, _Prec) -> + #t_map{types = []}; +d2e({type,_,map,Es}, _Prec) -> + #t_map{types = d2e(Es) }; +d2e({type,_,map_field_assoc,[K,V]}, Prec) -> + T = #t_map_field{k_type = d2e(K), v_type=d2e(V) }, + {P,_R} = erl_parse:type_preop_prec('#'), + maybe_paren(P, Prec, T); +d2e({type,_,map_field_exact,K,V}, Prec) -> + T = #t_map_field{k_type = d2e(K), v_type=d2e(V) }, + {P,_R} = erl_parse:type_preop_prec('#'), + maybe_paren(P, Prec, T); +d2e({type,_,tuple,Ts0}, _Prec) -> Ts = d2e(Ts0), typevar_anno(#t_tuple{types = Ts}, Ts); -d2e({type,_,record,[Name|Fs0]}) -> +d2e({type,_,record,[Name|Fs0]}, Prec) -> Atom = #t_atom{val = element(3, Name)}, Fs = d2e(Fs0), - typevar_anno(#t_record{name = Atom, fields = Fs}, Fs); -d2e({type,_,field_type,[Name,Type0]}) -> - Type = d2e(Type0), - typevar_anno(#t_field{name = #t_atom{val = element(3, Name)}, type = Type}, - [Type]); -d2e({typed_record_field,{record_field,L,Name},Type}) -> - d2e({type,L,field_type,[Name,Type]}); -d2e({typed_record_field,{record_field,L,Name,_E},Type}) -> - d2e({type,L,field_type,[Name,Type]}); -d2e({record_field,L,_Name,_E}=F) -> - d2e({typed_record_field,F,{type,L,any,[]}}); % Maybe skip... -d2e({record_field,L,_Name}=F) -> - d2e({typed_record_field,F,{type,L,any,[]}}); % Maybe skip... -d2e({type,_,Name,Types0}) -> + {P,_R} = erl_parse:type_preop_prec('#'), + T = maybe_paren(P, Prec, #t_record{name = Atom, fields = Fs}), + typevar_anno(T, Fs); +d2e({type,_,field_type,[Name,Type0]}, Prec) -> + {_L,P,R} = erl_parse:type_inop_prec('::'), + Type = maybe_paren(P, Prec, d2e(Type0, R)), + T = #t_field{name = #t_atom{val = element(3, Name)}, type = Type}, + typevar_anno(T, [Type]); +d2e({typed_record_field,{record_field,L,Name},Type}, Prec) -> + d2e({type,L,field_type,[Name,Type]}, Prec); +d2e({typed_record_field,{record_field,L,Name,_E},Type}, Prec) -> + d2e({type,L,field_type,[Name,Type]}, Prec); +d2e({record_field,L,_Name,_E}=F, Prec) -> + d2e({typed_record_field,F,{type,L,any,[]}}, Prec); % Maybe skip... +d2e({record_field,L,_Name}=F, Prec) -> + d2e({typed_record_field,F,{type,L,any,[]}}, Prec); % Maybe skip... +d2e({type,_,Name,Types0}, _Prec) -> Types = d2e(Types0), typevar_anno(#t_type{name = #t_name{name = Name}, args = Types}, Types); -d2e({user_type,_,Name,Types0}) -> +d2e({user_type,_,Name,Types0}, _Prec) -> Types = d2e(Types0), typevar_anno(#t_type{name = #t_name{name = Name}, args = Types}, Types); -d2e({var,_,'_'}) -> +d2e({var,_,'_'}, _Prec) -> #t_type{name = #t_name{name = ?TOP_TYPE}}; -d2e({var,_,TypeName}) -> +d2e({var,_,TypeName}, _Prec) -> TypeVar = ordsets:from_list([TypeName]), T = #t_var{name = TypeName}, %% Annotate type variables with the name of the variable. @@ -398,13 +415,13 @@ d2e({var,_,TypeName}) -> %% from using the argument name from the source or to invent a new name. T1 = ?add_t_ann(T, {type_variables, TypeVar}), ?add_t_ann(T1, TypeName); -d2e(L) when is_list(L) -> - [d2e(T) || T <- L]; -d2e({atom,_,A}) -> +d2e(L, Prec) when is_list(L) -> + [d2e(T, Prec) || T <- L]; +d2e({atom,_,A}, _Prec) -> #t_atom{val = A}; -d2e(undefined = U) -> % opaque +d2e(undefined = U, _Prec) -> % opaque U; -d2e(Expr) -> +d2e(Expr, _Prec) -> {integer,_,I} = erl_eval:partial_eval(Expr), #t_integer{val = I}. @@ -422,6 +439,11 @@ typevars(Ts) -> get_typevars(Ts) -> [Vs || T <- Ts, T =/= undefined, {type_variables, Vs} <- ?t_ann(T)]. +maybe_paren(P, Prec, T) when P < Prec -> + #t_paren{type = T}; +maybe_paren(_P, _Prec, T) -> + T. + -record(parms, {tab, warn, file, line}). %% Expands record references. Explicitly given record fields are kept, @@ -484,11 +506,11 @@ xrecs(#t_fun{args = Args0, range = Range0}=T, P) -> Args = xrecs(Args0, P), Range = xrecs(Range0, P), T#t_fun{args = Args, range = Range}; -xrecs(#t_map{ types = Ts0 }=T,P) -> +xrecs(#t_map{types = Ts0 }=T,P) -> Ts = xrecs(Ts0, P), - T#t_map{ types = Ts }; -xrecs(#t_map_field{ k_type=Kt, v_type=Vt}=T, P) -> - T#t_map_field{ k_type=xrecs(Kt,P), v_type=xrecs(Vt,P)}; + T#t_map{types = Ts }; +xrecs(#t_map_field{k_type=Kt, v_type=Vt}=T, P) -> + T#t_map_field{k_type=xrecs(Kt,P), v_type=xrecs(Vt,P)}; xrecs(#t_tuple{types = Types0}=T, P) -> Types = xrecs(Types0, P), T#t_tuple{types = Types}; -- cgit v1.2.3 From 14e4f3f119c06a8d2115bebdd7ba1c923d31d7a4 Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Wed, 27 May 2015 10:23:57 +0200 Subject: Update primary bootstrap --- bootstrap/lib/stdlib/ebin/erl_parse.beam | Bin 84396 -> 308128 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/bootstrap/lib/stdlib/ebin/erl_parse.beam b/bootstrap/lib/stdlib/ebin/erl_parse.beam index adf1cfb43e..0522f5c05e 100644 Binary files a/bootstrap/lib/stdlib/ebin/erl_parse.beam and b/bootstrap/lib/stdlib/ebin/erl_parse.beam differ -- cgit v1.2.3 From d1fbf82a0a43f91e2bbf95060dc09a1573e487c2 Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Sat, 8 Feb 2014 01:39:57 +0100 Subject: Document abstract format of type-related trees --- erts/doc/src/absform.xml | 163 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 163 insertions(+) diff --git a/erts/doc/src/absform.xml b/erts/doc/src/absform.xml index 835a4fc692..12cb06c151 100644 --- a/erts/doc/src/absform.xml +++ b/erts/doc/src/absform.xml @@ -80,6 +80,28 @@ If F is a record declaration , then Rep(F) = . For Rep(V), see below. + If F is a type attribute (i.e. or + ) + where each + is a variable, then Rep(F) = + . + For Rep(T), see below. + If F is a type spec (i.e. or + ) + , + where each is a fun type clause with an + argument sequence of the same length , then + Rep(F) = + . + For Rep(Tc_i), see below. + If F is a type spec (i.e. or + ) + , + where each is a fun type clause with an + argument sequence of the same length , then + Rep(F) = + . + For Rep(Tc_i), see below. If F is a wild attribute , then Rep(F) = .

@@ -89,6 +111,132 @@ Rep(F) = . +
+ Type clauses + + If T is a fun type clause + Ret]]>, where each + and are types, then + Rep(T) = + . + + If T is a bounded fun type clause , + where is an unbounded fun type clause and + is a type guard sequence, then Rep(T) = + . + +
+ +
+ Type guards + + If G is a constraint , where + is an atom and each is a + type, then Rep(G) = + . + + If G is a type definition , + where is a variable and + is a type, then Rep(G) = + . + +
+ +
+ Types + + If T is a type definition , + where is a variable and + is a type, then Rep(T) = + . + If T is a type union , + where each is a type, then Rep(T) = + . + If T is a type range , + where and are types, then + Rep(T) = . + If T is a binary operation , + where is an arithmetic or bitwise binary operator + and and are types, then + Rep(T) = . + If T is , where is an + arithmetic or bitwise unary operator and is a + type, then Rep(T) = . + If T is a fun type , then Rep(T) = + . + If T is a parenthesized type , then + Rep(T) = , i.e. parenthesized + types are distinguished from their bodies. It should be noted though + that parenthesized types that are immediate subtrees of operator + expressions and binary types are peeled off. + If T is a variable , then Rep(T) = + , where is an atom + with a printname consisting of the same characters as + . + If T is an atomic literal L and L is not a string literal, then + Rep(T) = Rep(L). + If T is a tuple or map type (i.e. + or ), then Rep(T) = + . + If T is a type , where each + is a type, then Rep(T) = + . + If T is a remote type , where + each is a type and and + , then Rep(T) = + . + + If T is the nil type , then Rep(T) = + . + If T is a list type , where + is a type, then Rep(T) = + . + If T is a non-empty list type , where + is a type, then Rep(T) = + . + If T is a map type , where each + is a map pair type, then Rep(T) = + . + If T is a map pair type V]]>, where + and are types, + then Rep(T) = + . + If T is a tuple type , where + each is a type, then Rep(T) = + . + If T is a record type , where + is an atom, then Rep(T) = + . + If T is a record type , + where is an atom, then Rep(T) = + . + + If T is a record field type , + where is an atom, then Rep(T) = + . + If T is a record field type >]]>, then Rep(T) = + . + + If T is a binary type >]]>, where + is a type, then Rep(T) = + . + If T is a binary type >]]>, + where is a type, then Rep(T) = + . + If T is a binary type >]]>, + where and is a type, then + Rep(T) = + . + + If T is a fun type Ret)]]>, then + Rep(T) = . + + If T is a fun type , where + is an unbounded fun type clause, + then Rep(T) = . + +
+
Record fields

Each field in a record declaration may have an optional @@ -98,6 +246,21 @@ Rep(V) = . If V is , then Rep(V) = . + If V is , where is + an atom and is a type and it does not contain + syntactically, then Rep(V) = + . + Note that if is an annotated type, it will be wrapped in + parentheses. + If V is , where is + an atom and is a type, then Rep(V) = + . + + If V is , where + is an atom, is an expression and + is a type, then Rep(V) = + . +

-- cgit v1.2.3 From 417f9960371607e6d618d9dda108787558a9cef5 Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Wed, 10 Jun 2015 16:30:53 +0200 Subject: Update the documentation of the abstract format The parenthesized type with tag 'paren_type' is no longer created by the Erlang Parser as of OTP 18.0. The tag 'user_type' is used for user defined types as of OTP 18.0. In releases before commit 7ad783 'type' is used. --- erts/doc/src/absform.xml | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/erts/doc/src/absform.xml b/erts/doc/src/absform.xml index 12cb06c151..e1a8c2e517 100644 --- a/erts/doc/src/absform.xml +++ b/erts/doc/src/absform.xml @@ -4,7 +4,7 @@
- 20012013 + 20012015 Ericsson AB. All Rights Reserved. @@ -164,11 +164,6 @@ type, then Rep(T) = . If T is a fun type , then Rep(T) = . - If T is a parenthesized type , then - Rep(T) = , i.e. parenthesized - types are distinguished from their bodies. It should be noted though - that parenthesized types that are immediate subtrees of operator - expressions and binary types are peeled off. If T is a variable , then Rep(T) = , where is an atom with a printname consisting of the same characters as @@ -180,7 +175,7 @@ . If T is a type , where each is a type, then Rep(T) = - . + . If T is a remote type , where each is a type and and , then Rep(T) = -- cgit v1.2.3