diff options
Diffstat (limited to 'lib/syntax_tools')
-rw-r--r-- | lib/syntax_tools/doc/Makefile | 2 | ||||
-rw-r--r-- | lib/syntax_tools/doc/src/notes.xml | 62 | ||||
-rw-r--r-- | lib/syntax_tools/examples/Makefile | 2 | ||||
-rw-r--r-- | lib/syntax_tools/src/Makefile | 2 | ||||
-rw-r--r-- | lib/syntax_tools/src/epp_dodger.erl | 20 | ||||
-rw-r--r-- | lib/syntax_tools/src/erl_prettypr.erl | 36 | ||||
-rw-r--r-- | lib/syntax_tools/src/erl_syntax.erl | 245 | ||||
-rw-r--r-- | lib/syntax_tools/src/erl_syntax_lib.erl | 48 | ||||
-rw-r--r-- | lib/syntax_tools/src/merl.erl | 2 | ||||
-rw-r--r-- | lib/syntax_tools/src/prettypr.erl | 2 | ||||
-rw-r--r-- | lib/syntax_tools/src/syntax_tools.app.src | 2 | ||||
-rw-r--r-- | lib/syntax_tools/test/Makefile | 3 | ||||
-rw-r--r-- | lib/syntax_tools/test/syntax_tools_SUITE.erl | 59 | ||||
-rw-r--r-- | lib/syntax_tools/test/syntax_tools_SUITE_data/syntax_tools_SUITE_test_module.erl | 47 | ||||
-rw-r--r-- | lib/syntax_tools/vsn.mk | 2 |
15 files changed, 480 insertions, 54 deletions
diff --git a/lib/syntax_tools/doc/Makefile b/lib/syntax_tools/doc/Makefile index 87604c4e7f..87b7660c26 100644 --- a/lib/syntax_tools/doc/Makefile +++ b/lib/syntax_tools/doc/Makefile @@ -57,7 +57,7 @@ info: -debug opt: +$(TYPES): clean: diff --git a/lib/syntax_tools/doc/src/notes.xml b/lib/syntax_tools/doc/src/notes.xml index b3629d4477..a4633065d0 100644 --- a/lib/syntax_tools/doc/src/notes.xml +++ b/lib/syntax_tools/doc/src/notes.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2007</year><year>2021</year> + <year>2007</year><year>2022</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -32,6 +32,64 @@ <p>This document describes the changes made to the Syntax_Tools application.</p> +<section><title>Syntax_Tools 3.0.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p><c>erl_syntax_lib:annotate_bindings/1,2</c> will now + properly annotate named functions and their + arguments.</p> + <p> + Own Id: OTP-18380 Aux Id: PR-6523, GH-4733 </p> + </item> + </list> + </section> + +</section> + +<section><title>Syntax_Tools 3.0</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p>The <c>erl_syntax_lib:analyze_attribute/1</c> function + would return <c>{Name, {Name, Value}}</c> instead of + <c>{Name, Value}</c> (which is the documented return + value).</p> + <p> + *** POTENTIAL INCOMPATIBILITY ***</p> + <p> + Own Id: OTP-17894 Aux Id: PR-5509 </p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Added support for configurable features as described in + EEP-60. Features can be enabled/disabled during + compilation with options (<c>-enable-feature Feature</c>, + <c>-disable-feature Feature</c> and <c>+{feature, + Feature, enable|disable}</c>) to <c>erlc</c> as well as + with directives (<c>-feature(Feature, + enable|disable).</c>) in the file. Similar options can be + used to <c>erl</c> for enabling/disabling features + allowed at runtime. The new <c>maybe</c> expression + (EEP-49) is fully supported as the feature + <c>maybe_expr</c>. The features support is documented in + the reference manual.</p> + <p> + Own Id: OTP-17988</p> + </item> + </list> + </section> + +</section> + <section><title>Syntax_Tools 2.6</title> <section><title>Fixed Bugs and Malfunctions</title> @@ -860,7 +918,7 @@ <list> <item> <p> - Miscellanous updates.</p> + Miscellaneous updates.</p> <p> Own Id: OTP-8038</p> </item> diff --git a/lib/syntax_tools/examples/Makefile b/lib/syntax_tools/examples/Makefile index f5e96a8285..0712898f65 100644 --- a/lib/syntax_tools/examples/Makefile +++ b/lib/syntax_tools/examples/Makefile @@ -37,7 +37,7 @@ EXAMPLE_FILES = demo.erl # ---------------------------------------------------- # Make Rules # ---------------------------------------------------- -debug opt: +$(TYPES): clean: diff --git a/lib/syntax_tools/src/Makefile b/lib/syntax_tools/src/Makefile index af6a472f3d..dc0ac61734 100644 --- a/lib/syntax_tools/src/Makefile +++ b/lib/syntax_tools/src/Makefile @@ -50,7 +50,7 @@ APPUP_TARGET= $(EBIN)/$(APPUP_FILE) # Targets # ---------------------------------------------------- -debug opt: $(OBJECTS) +$(TYPES): $(OBJECTS) all: $(OBJECTS) diff --git a/lib/syntax_tools/src/epp_dodger.erl b/lib/syntax_tools/src/epp_dodger.erl index eacfbc5d6b..2e0694c2cf 100644 --- a/lib/syntax_tools/src/epp_dodger.erl +++ b/lib/syntax_tools/src/epp_dodger.erl @@ -430,7 +430,14 @@ quick_parse_form(Dev, L0, Options) -> parse_form(Dev, L0, Parser, Options) -> NoFail = proplists:get_bool(no_fail, Options), Opt = #opt{clever = proplists:get_bool(clever, Options)}, - case io:scan_erl_form(Dev, "", L0) of + + %% This as the *potential* to read options for enabling/disabling + %% features for the parsing of the file. + {ok, {_Ftrs, ResWordFun}} = + erl_features:keyword_fun(Options, + fun erl_scan:f_reserved_word/1), + + case io:scan_erl_form(Dev, "", L0, [{reserved_word_fun,ResWordFun}]) of {ok, Ts, L1} -> case catch {ok, Parser(Ts, Opt)} of {'EXIT', Term} -> @@ -504,7 +511,9 @@ quickscan_form([{'-', _Anno}, {'if', AnnoA} | _Ts]) -> kill_form(AnnoA); quickscan_form([{'-', _Anno}, {atom, AnnoA, elif} | _Ts]) -> kill_form(AnnoA); -quickscan_form([{'-', _Anno}, {atom, AnnoA, else} | _Ts]) -> +quickscan_form([{'-', _Anno}, {atom, AnnoA, 'else'} | _Ts]) -> + kill_form(AnnoA); +quickscan_form([{'-', _Anno}, {'else', AnnoA} | _Ts]) -> kill_form(AnnoA); quickscan_form([{'-', _Anno}, {atom, AnnoA, endif} | _Ts]) -> kill_form(AnnoA); @@ -648,9 +657,12 @@ scan_form([{'-', _Anno}, {'if', AnnoA} | Ts], Opt) -> scan_form([{'-', _Anno}, {atom, AnnoA, elif} | Ts], Opt) -> [{atom, AnnoA, ?pp_form}, {'(', AnnoA}, {')', AnnoA}, {'->', AnnoA}, {atom, AnnoA, 'elif'} | scan_macros(Ts, Opt)]; -scan_form([{'-', _Anno}, {atom, AnnoA, else} | Ts], Opt) -> +scan_form([{'-', _Anno}, {atom, AnnoA, 'else'} | Ts], Opt) -> + [{atom, AnnoA, ?pp_form}, {'(', AnnoA}, {')', AnnoA}, {'->', AnnoA}, + {atom, AnnoA, 'else'} | scan_macros(Ts, Opt)]; +scan_form([{'-', _Anno}, {'else', AnnoA} | Ts], Opt) -> [{atom, AnnoA, ?pp_form}, {'(', AnnoA}, {')', AnnoA}, {'->', AnnoA}, - {atom, AnnoA, else} | scan_macros(Ts, Opt)]; + {atom, AnnoA, 'else'} | scan_macros(Ts, Opt)]; scan_form([{'-', _Anno}, {atom, AnnoA, endif} | Ts], Opt) -> [{atom, AnnoA, ?pp_form}, {'(', AnnoA}, {')', AnnoA}, {'->', AnnoA}, {atom, AnnoA, endif} | scan_macros(Ts, Opt)]; diff --git a/lib/syntax_tools/src/erl_prettypr.erl b/lib/syntax_tools/src/erl_prettypr.erl index af1f2b4d11..d6c1c1e005 100644 --- a/lib/syntax_tools/src/erl_prettypr.erl +++ b/lib/syntax_tools/src/erl_prettypr.erl @@ -54,7 +54,7 @@ -type hook() :: 'none' | fun((erl_syntax:syntaxTree(), _, _) -> prettypr:document()). -type clause_t() :: 'case_expr' | 'fun_expr' - | 'if_expr' | 'receive_expr' | 'try_expr' + | 'if_expr' | 'maybe_expr' | 'receive_expr' | 'try_expr' | {'function', prettypr:document()} | 'spec'. @@ -588,6 +588,8 @@ lay_2(Node, Ctxt) -> make_if_clause(D1, D2, D3, Ctxt); case_expr -> make_case_clause(D1, D2, D3, Ctxt); + maybe_expr -> + make_case_clause(D1, D2, D3, Ctxt); receive_expr -> make_case_clause(D1, D2, D3, Ctxt); try_expr -> @@ -648,6 +650,34 @@ lay_2(Node, Ctxt) -> set_prec(Ctxt, PrecR)), beside(D1, beside(text(":"), D2)); + maybe_expr -> + Ctxt1 = reset_prec(Ctxt), + D1 = vertical(seq(erl_syntax:maybe_expr_body(Node), + floating(text(",")), Ctxt1, fun lay/2)), + Es0 = [text("end")], + Es1 = case erl_syntax:maybe_expr_else(Node) of + none -> Es0; + ElseNode -> + ElseCs = erl_syntax:else_expr_clauses(ElseNode), + D3 = lay_clauses(ElseCs, maybe_expr, Ctxt1), + [text("else"), + nest(Ctxt1#ctxt.break_indent, D3) + | Es0] + end, + sep([par([text("maybe"), nest(Ctxt1#ctxt.break_indent, D1), + hd(Es1)]) | tl(Es1)]); + + maybe_match_expr -> + {PrecL, Prec, PrecR} = inop_prec('='), + D1 = lay(erl_syntax:maybe_match_expr_pattern(Node), + set_prec(Ctxt, PrecL)), + D2 = lay(erl_syntax:maybe_match_expr_body(Node), + set_prec(Ctxt, PrecR)), + D3 = follow(beside(D1, floating(text(" ?="))), D2, + Ctxt#ctxt.break_indent), + maybe_parentheses(D3, Prec, Ctxt); + + %% %% The rest is in alphabetical order (except map and types) %% @@ -759,7 +789,7 @@ lay_2(Node, Ctxt) -> nest(Ctxt1#ctxt.break_indent, sep(Es)), text("end")]); - catch_expr -> + 'catch_expr' -> %Quoted to help Emacs. {Prec, PrecR} = preop_prec('catch'), D = lay(erl_syntax:catch_expr_body(Node), set_prec(Ctxt, PrecR)), @@ -801,7 +831,7 @@ lay_2(Node, Ctxt) -> sep(seq(erl_syntax:disjunction_body(Node), floating(text(";")), reset_prec(Ctxt), fun lay/2)); - + error_marker -> E = erl_syntax:error_marker_info(Node), beside(text("** "), diff --git a/lib/syntax_tools/src/erl_syntax.erl b/lib/syntax_tools/src/erl_syntax.erl index 354b65c9a3..4da1635aec 100644 --- a/lib/syntax_tools/src/erl_syntax.erl +++ b/lib/syntax_tools/src/erl_syntax.erl @@ -193,6 +193,8 @@ constraint_body/1, disjunction/1, disjunction_body/1, + else_expr/1, + else_expr_clauses/1, eof_marker/0, error_marker/1, error_marker_info/1, @@ -266,6 +268,13 @@ match_expr/2, match_expr_body/1, match_expr_pattern/1, + maybe_expr/1, + maybe_expr/2, + maybe_expr_body/1, + maybe_expr_else/1, + maybe_match_expr/2, + maybe_match_expr_pattern/1, + maybe_match_expr_body/1, module_qualifier/2, module_qualifier_argument/1, module_qualifier_body/1, @@ -505,13 +514,14 @@ %% <td>constrained_function_type</td> %% <td>constraint</td> %% <td>disjunction</td> -%% <td>eof_marker</td> %% </tr><tr> +%% <td>else_expr</td> +%% <td>eof_marker</td> %% <td>error_marker</td> +%% </tr><tr> %% <td>float</td> %% <td>form_list</td> %% <td>fun_expr</td> -%% </tr><tr> %% <td>fun_type</td> %% <td>function</td> %% <td>function_type</td> @@ -535,6 +545,9 @@ %% <td>map_type_assoc</td> %% <td>map_type_exact</td> %% <td>match_expr</td> +%% </tr><tr> +%% <td>maybe_expr</td> +%% <td>maybe_match_expr</td> %% <td>module_qualifier</td> %% </tr><tr> %% <td>named_fun_expr</td> @@ -597,6 +610,7 @@ %% @see constrained_function_type/2 %% @see constraint/2 %% @see disjunction/1 +%% @see else_expr/1 %% @see eof_marker/0 %% @see error_marker/1 %% @see float/1 @@ -623,6 +637,9 @@ %% @see map_type_assoc/2 %% @see map_type_exact/2 %% @see match_expr/2 +%% @see maybe_expr/1 +%% @see maybe_expr/2 +%% @see maybe_match_expr/2 %% @see module_qualifier/2 %% @see named_fun_expr/2 %% @see nil/0 @@ -683,6 +700,9 @@ type(Node) -> {'fun', _, {function, _, _}} -> implicit_fun; {'fun', _, {function, _, _, _}} -> implicit_fun; {'if', _, _} -> if_expr; + {'maybe', _, _} -> maybe_expr; + {'maybe', _, _, _} -> maybe_expr; + {'else', _, _} -> else_expr; {'receive', _, _, _, _} -> receive_expr; {'receive', _, _} -> receive_expr; {attribute, _, _, _} -> attribute; @@ -702,6 +722,7 @@ type(Node) -> {map, _, _} -> map_expr; {map_field_assoc, _, _, _} -> map_field_assoc; {map_field_exact, _, _, _} -> map_field_exact; + {maybe_match, _, _, _} -> maybe_match_expr; {op, _, _, _, _} -> infix_expr; {op, _, _, _} -> prefix_expr; {record, _, _, _, _} -> record_expr; @@ -4135,6 +4156,71 @@ match_expr_body(Node) -> %% ===================================================================== +%% @doc Creates an abstract maybe-expression, as used in <code>maybe</code> +%% blocks. The result represents +%% "<code><em>Pattern</em> ?= <em>Body</em></code>". +%% +%% @see maybe_match_expr_pattern/1 +%% @see maybe_match_expr_body/1 +%% @see maybe_expr/2 + +-record(maybe_match_expr, {pattern :: syntaxTree(), body :: syntaxTree()}). + +%% type(Node) = maybe_expr +%% data(Node) = #maybe_expr{pattern :: Pattern, body :: Body} +%% +%% Pattern = Body = syntaxTree() +%% +%% `erl_parse' representation: +%% +%% {maybe_match, Pos, Pattern, Body} +%% +%% Pattern = Body = erl_parse() +%% + +-spec maybe_match_expr(syntaxTree(), syntaxTree()) -> syntaxTree(). + +maybe_match_expr(Pattern, Body) -> + tree(maybe_match_expr, #maybe_match_expr{pattern = Pattern, body = Body}). + +revert_maybe_match_expr(Node) -> + Pos = get_pos(Node), + Pattern = maybe_match_expr_pattern(Node), + Body = maybe_match_expr_body(Node), + {maybe_match, Pos, Pattern, Body}. + +%% ===================================================================== +%% @doc Returns the pattern subtree of a `maybe_expr' node. +%% +%% @see maybe_match_expr/2 + +-spec maybe_match_expr_pattern(syntaxTree()) -> syntaxTree(). + +maybe_match_expr_pattern(Node) -> + case unwrap(Node) of + {maybe_match, _, Pattern, _} -> + Pattern; + Node1 -> + (data(Node1))#maybe_match_expr.pattern + end. + + +%% ===================================================================== +%% @doc Returns the body subtree of a `maybe_expr' node. +%% +%% @see maybe_match_expr/2 + +-spec maybe_match_expr_body(syntaxTree()) -> syntaxTree(). + +maybe_match_expr_body(Node) -> + case unwrap(Node) of + {maybe_match, _, _, Body} -> + Body; + Node1 -> + (data(Node1))#maybe_match_expr.body + end. + +%% ===================================================================== %% @doc Creates an abstract operator. The name of the operator is the %% character sequence represented by `Name'. This is %% analogous to the print name of an atom, but an operator is never @@ -6361,6 +6447,130 @@ case_expr_clauses(Node) -> (data(Node1))#case_expr.clauses end. +%% ===================================================================== +%% @doc Creates an abstract else-expression. If `Clauses' is `[C1, +%% ..., Cn]', the result represents "<code>else <em>C1</em>; ...; <em>Cn</em> +%% end</code>". More exactly, if each `Ci' represents +%% "<code>(<em>Pi</em>) <em>Gi</em> -> <em>Bi</em></code>", then the +%% result represents "<code>else <em>G1</em> -> <em>B1</em>; ...; +%% <em>Pn</em> <em>Gn</em> -> <em>Bn</em> end</code>". +%% +%% @see maybe_expr/2 +%% @see else_expr_clauses/1 +%% @see clause/3 + +else_expr(Clauses) -> + tree(else_expr, Clauses). + +revert_else_expr(Node) -> + Pos = get_pos(Node), + Clauses = else_expr_clauses(Node), + {'else', Pos, Clauses}. + +%% ===================================================================== +%% @doc Returns the list of clause subtrees of an `else_expr' node. +%% +%% @see else_expr/1 + +-spec else_expr_clauses(syntaxTree()) -> [syntaxTree()]. + +else_expr_clauses(Node) -> + case unwrap(Node) of + {'else', _, Clauses} -> + Clauses; + Node1 -> + data(Node1) + end. + +%% ===================================================================== +%% @equiv maybe_expr(Body, none) + +-spec maybe_expr([syntaxTree()]) -> syntaxTree(). + +maybe_expr(Body) -> + maybe_expr(Body, none). + +%% ===================================================================== +%% @doc Creates an abstract maybe-expression. If `Body' is `[B1, ..., +%% Bn]', and `OptionalElse' is `none', the result represents +%% "<code>maybe <em>B1</em>, ..., <em>Bn</em> end</code>". If `Body' +%% is `[B1, ..., Bn]', and `OptionalElse' reprsents an `else_expr' node +%% with clauses `[C1, ..., Cn]', the result represents "<code>maybe +%% <em>B1</em>, ..., <em>Bn</em> else <em>C1</em>; ..., <em>Cn</em> +%% end</code>". +%% +%% See `clause' for documentation on `erl_parse' clauses. +%% +%% @see maybe_expr_body/1 +%% @see maybe_expr_else/1 + +-record(maybe_expr, {body :: [syntaxTree()], + 'else' = none :: 'none' | syntaxTree()}). + +%% type(Node) = maybe_expr +%% data(Node) = #maybe_expr{body :: Body, 'else' :: 'none' | Else} +%% +%% Body = [syntaxTree()] +%% Else = syntaxTree() +%% +%% `erl_parse' representation: +%% +%% {block, Pos, Body} +%% {block, Pos, Body, Else} +%% +%% Body = [erl_parse()] \ [] +%% Else = {'else', Pos, Clauses} +%% Clauses = [Clause] \ [] +%% Clause = {clause, ...} + +-spec maybe_expr([syntaxTree()], 'none' | syntaxTree()) -> syntaxTree(). + +maybe_expr(Body, OptionalElse) -> + tree(maybe_expr, #maybe_expr{body = Body, + 'else' = OptionalElse}). +revert_maybe_expr(Node) -> + Pos = get_pos(Node), + Body = maybe_expr_body(Node), + case maybe_expr_else(Node) of + none -> + {'maybe', Pos, Body}; + Else -> + {'maybe', Pos, Body, Else} + end. + +%% ===================================================================== +%% @doc Returns the list of body subtrees of a `maybe_expr' node. +%% +%% @see maybe_expr/2 + +-spec maybe_expr_body(syntaxTree()) -> [syntaxTree()]. + +maybe_expr_body(Node) -> + case unwrap(Node) of + {'maybe', _, Body} -> + Body; + {'maybe', _, Body, _Else} -> + Body; + Node1 -> + (data(Node1))#maybe_expr.body + end. + +%% ===================================================================== +%% @doc Returns the else subtree of a `maybe_expr' node. +%% +%% @see maybe_expr/2 + +-spec maybe_expr_else(syntaxTree()) -> 'none' | syntaxTree(). + +maybe_expr_else(Node) -> + case unwrap(Node) of + {'maybe', _, _Body} -> + none; + {'maybe', _, _Body, Else} -> + Else; + Node1 -> + (data(Node1))#maybe_expr.'else' + end. %% ===================================================================== %% @equiv receive_expr(Clauses, none, []) @@ -7310,7 +7520,7 @@ concrete(Node) -> eval_bits:expr_grp(Fs, [], fun(F, _) -> {value, concrete(F), []} - end, [], true), + end), B; arity_qualifier -> A = erl_syntax:arity_qualifier_argument(Node), @@ -7470,9 +7680,9 @@ revert_root(Node) -> revert_bitstring_type(Node); block_expr -> revert_block_expr(Node); - case_expr -> + 'case_expr' -> %Quoted to help Emacs. revert_case_expr(Node); - catch_expr -> + 'catch_expr' -> %Quoted to help Emacs. revert_catch_expr(Node); char -> revert_char(Node); @@ -7482,6 +7692,8 @@ revert_root(Node) -> revert_constrained_function_type(Node); constraint -> revert_constraint(Node); + else_expr -> + revert_else_expr(Node); eof_marker -> revert_eof_marker(Node); error_marker -> @@ -7526,6 +7738,10 @@ revert_root(Node) -> revert_map_type_exact(Node); match_expr -> revert_match_expr(Node); + maybe_match_expr -> + revert_maybe_match_expr(Node); + maybe_expr -> + revert_maybe_expr(Node); module_qualifier -> revert_module_qualifier(Node); named_fun_expr -> @@ -7730,7 +7946,7 @@ subtrees(T) -> case_expr -> [[case_expr_argument(T)], case_expr_clauses(T)]; - catch_expr -> + 'catch_expr' -> %Quoted to help Emacs. [[catch_expr_body(T)]]; class_qualifier -> [[class_qualifier_argument(T)], @@ -7755,6 +7971,8 @@ subtrees(T) -> constraint_body(T)]; disjunction -> [disjunction_body(T)]; + else_expr -> + [else_expr_clauses(T)]; form_list -> [form_list_elements(T)]; fun_expr -> @@ -7823,6 +8041,17 @@ subtrees(T) -> match_expr -> [[match_expr_pattern(T)], [match_expr_body(T)]]; + maybe_expr -> + case maybe_expr_else(T) of + none -> + [maybe_expr_body(T)]; + E -> + [maybe_expr_body(T), + [E]] + end; + maybe_match_expr -> + [[maybe_match_expr_pattern(T)], + [maybe_match_expr_body(T)]]; module_qualifier -> [[module_qualifier_argument(T)], [module_qualifier_body(T)]]; @@ -7962,6 +8191,7 @@ make_tree(constrained_function_type, [[F],C]) -> constrained_function_type(F, C); make_tree(constraint, [[N], Ts]) -> constraint(N, Ts); make_tree(disjunction, [E]) -> disjunction(E); +make_tree(else_expr, [E]) -> else_expr(E); make_tree(form_list, [E]) -> form_list(E); make_tree(fun_expr, [C]) -> fun_expr(C); make_tree(function, [[N], C]) -> function(N, C); @@ -7985,6 +8215,9 @@ make_tree(map_type, [Fs]) -> map_type(Fs); 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(maybe_expr, [Body]) -> maybe_expr(Body); +make_tree(maybe_expr, [Body, [Else]]) -> maybe_expr(Body, Else); +make_tree(maybe_match_expr, [[P], [E]]) -> maybe_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); make_tree(parentheses, [[E]]) -> parentheses(E); diff --git a/lib/syntax_tools/src/erl_syntax_lib.erl b/lib/syntax_tools/src/erl_syntax_lib.erl index 6185007235..caaa322dfb 100644 --- a/lib/syntax_tools/src/erl_syntax_lib.erl +++ b/lib/syntax_tools/src/erl_syntax_lib.erl @@ -332,8 +332,8 @@ variables_2([], S) -> -define(MINIMUM_RANGE, 100). -define(START_RANGE_FACTOR, 100). -define(MAX_RETRIES, 3). % retries before enlarging range --define(ENLARGE_ENUM, 8). % range enlargment enumerator --define(ENLARGE_DENOM, 1). % range enlargment denominator +-define(ENLARGE_ENUM, 8). % range enlargement enumerator +-define(ENLARGE_DENOM, 1). % range enlargement denominator default_variable_name(N) -> list_to_atom("V" ++ integer_to_list(N)). @@ -538,6 +538,8 @@ vann(Tree, Env) -> vann_function(Tree, Env); fun_expr -> vann_fun_expr(Tree, Env); + named_fun_expr -> + vann_named_fun_expr(Tree, Env); list_comp -> vann_list_comp(Tree, Env); binary_comp -> @@ -583,6 +585,18 @@ vann_fun_expr(Tree, Env) -> Bound = [], {ann_bindings(Tree1, Env, Bound, Free), Bound, Free}. +vann_named_fun_expr(Tree, Env) -> + N = erl_syntax:named_fun_expr_name(Tree), + NBound = [erl_syntax:variable_name(N)], + NFree = [], + N1 = ann_bindings(N, Env, NBound, NFree), + Env1 = ordsets:union(Env, NBound), + Cs = erl_syntax:named_fun_expr_clauses(Tree), + {Cs1, {_, Free}} = vann_clauses(Cs, Env1), + Tree1 = rewrite(Tree, erl_syntax:named_fun_expr(N1,Cs1)), + Bound = [], + {ann_bindings(Tree1, Env, Bound, Free), Bound, Free}. + vann_match_expr(Tree, Env) -> E = erl_syntax:match_expr_body(Tree), {E1, Bound1, Free1} = vann(E, Env), @@ -1106,7 +1120,7 @@ collect_attribute(file, _, Info) -> Info; collect_attribute(record, {R, L}, Info) -> finfo_add_record(R, L, Info); -collect_attribute(_, {N, V}, Info) -> +collect_attribute(N, V, Info) -> finfo_add_attribute(N, V, Info). %% Abstract datatype for collecting module information. @@ -1254,7 +1268,7 @@ analyze_form(Node) -> %% @doc Analyzes an attribute node. If `Node' represents a %% preprocessor directive, the atom `preprocessor' is %% returned. Otherwise, if `Node' represents a module -%% attribute "`-<em>Name</em>...'", a tuple `{Name, +%% attribute "`-Name...'", a tuple `{Name, %% Info}' is returned, where `Info' depends on %% `Name', as follows: %% <dl> @@ -1314,7 +1328,7 @@ analyze_attribute(Node) -> ifndef -> preprocessor; 'if' -> preprocessor; elif -> preprocessor; - else -> preprocessor; + 'else' -> preprocessor; endif -> preprocessor; A -> {A, analyze_attribute(A, Node)} @@ -1335,7 +1349,8 @@ analyze_attribute(record, Node) -> analyze_record_attribute(Node); analyze_attribute(_, Node) -> %% A "wild" attribute (such as e.g. a `compile' directive). - analyze_wild_attribute(Node). + {_, Info} = analyze_wild_attribute(Node), + Info. %% ===================================================================== @@ -1632,8 +1647,8 @@ analyze_wild_attribute(Node) -> %% `Node' represents "`-record(Name, {...}).'", %% where `Fields' is a list of pairs `{Label, %% {Default, Type}}' for each field "`Label'", "`Label = -%% <em>Default</em>'", "`Label :: <em>Type</em>'", or -%% "`Label = <em>Default</em> :: <em>Type</em>'" in the declaration, +%% Default'", "`Label :: Type'", or +%% "`Label = Default :: Type'" in the declaration, %% listed in left-to-right %% order. If the field has no default-value declaration, the value for %% `Default' will be the atom `none'. If the field has no type declaration, @@ -1708,7 +1723,7 @@ analyze_record_attribute_tuple(Node) -> %% For a `record_expr' node, `Info' represents %% the record name and the list of descriptors for the involved fields, %% listed in the order they appear. A field descriptor is a pair -%% `{Label, Value}', if `Node' represents "`Label = <em>Value</em>'". +%% `{Label, Value}', if `Node' represents "`Label = Value'". %% For a `record_access' node, %% `Info' represents the record name and the field name. For a %% `record_index_expr' node, `Info' represents the @@ -1783,9 +1798,8 @@ analyze_record_expr(Node) -> %% %% @doc Returns the label, value-expression, and type of a record field %% specifier. The result is a pair `{Label, {Default, Type}}', if -%% `Node' represents "`Label'", "`Label = <em>Default</em>'", -%% "`Label :: <em>Type</em>'", or -%% "`Label = <em>Default</em> :: <em>Type</em>'". +%% `Node' represents "`Label'", "`Label = Default'", +%% "`Label :: Type'", or "`Label = Default :: Type'". %% If the field has no value-expression, the value for %% `Default' will be the atom `none'. If the field has no type, %% the value for `Type' will be the atom `none'. @@ -1862,7 +1876,7 @@ analyze_file_attribute(Node) -> %% %% @doc Returns the name and arity of a function definition. The result %% is a pair `{Name, A}' if `Node' represents a -%% function definition "`Name(<em>P_1</em>, ..., <em>P_A</em>) -> +%% function definition "`Name(P_1, ..., P_A) -> %% ...'". %% %% The evaluation throws `syntax_error' if @@ -1895,7 +1909,7 @@ analyze_function(Node) -> %% ModuleName = atom() %% %% @doc Returns the name of an implicit fun expression "`fun -%% <em>F</em>'". The result is a representation of the function +%% F'". The result is a representation of the function %% name `F'. (Cf. `analyze_function_name/1'.) %% %% The evaluation throws `syntax_error' if @@ -1925,7 +1939,7 @@ analyze_implicit_fun(Node) -> %% @doc Returns the name of a called function. The result is a %% representation of the name of the applied function `F/A', %% if `Node' represents a function application -%% "`<em>F</em>(<em>X_1</em>, ..., <em>X_A</em>)'". If the +%% "`F(X_1, ..., X_A)'". If the %% function is not explicitly named (i.e., `F' is given by %% some expression), only the arity `A' is returned. %% @@ -1966,10 +1980,10 @@ analyze_application(Node) -> %% @doc Returns the name of a used type. The result is a %% representation of the name of the used pre-defined or local type `N/A', %% if `Node' represents a local (user) type application -%% "`<em>N</em>(<em>T_1</em>, ..., <em>T_A</em>)'", or +%% "`N(T_1, ..., T_A)'", or %% a representation of the name of the used remote type `M:N/A' %% if `Node' represents a remote user type application -%% "`<em>M</em>:<em>N</em>(<em>T_1</em>, ..., <em>T_A</em>)'". +%% "`M:N(T_1, ..., T_A)'". %% %% The evaluation throws `syntax_error' if `Node' does not represent a %% well-formed (user) type application expression. diff --git a/lib/syntax_tools/src/merl.erl b/lib/syntax_tools/src/merl.erl index 97ef68ce4c..28cd1283e9 100644 --- a/lib/syntax_tools/src/merl.erl +++ b/lib/syntax_tools/src/merl.erl @@ -467,7 +467,7 @@ quote(Text) -> %% @doc Parse text. Takes an initial scanner starting position as first %% argument. %% -%% The macro `?Q(Text)' expands to `merl:quote(?LINE, Text, Env)'. +%% The macro `?Q(Text)' expands to `merl:quote(?LINE, Text)'. %% %% @see quote/1 diff --git a/lib/syntax_tools/src/prettypr.erl b/lib/syntax_tools/src/prettypr.erl index 1f2dfffbdb..caafa9b8b9 100644 --- a/lib/syntax_tools/src/prettypr.erl +++ b/lib/syntax_tools/src/prettypr.erl @@ -432,7 +432,7 @@ follow(D1, D2) -> %% document() %% %% @doc Separates two documents by either a single space, or a line -%% break and intentation. In other words, one of the layouts +%% break and indentation. In other words, one of the layouts %% ```abc def''' %% or %% ```abc diff --git a/lib/syntax_tools/src/syntax_tools.app.src b/lib/syntax_tools/src/syntax_tools.app.src index 9bbf20d5a5..a6eeeba5dc 100644 --- a/lib/syntax_tools/src/syntax_tools.app.src +++ b/lib/syntax_tools/src/syntax_tools.app.src @@ -16,4 +16,4 @@ {applications, [stdlib]}, {env, []}, {runtime_dependencies, - ["compiler-7.0","erts-9.0","kernel-5.0","stdlib-3.4"]}]}. + ["compiler-7.0","erts-9.0","kernel-5.0","stdlib-4.0"]}]}. diff --git a/lib/syntax_tools/test/Makefile b/lib/syntax_tools/test/Makefile index 4ace860223..deee5ab814 100644 --- a/lib/syntax_tools/test/Makefile +++ b/lib/syntax_tools/test/Makefile @@ -27,6 +27,7 @@ RELSYSDIR = $(RELEASE_PATH)/syntax_tools_test ERL_MAKE_FLAGS += ERL_COMPILE_FLAGS += +ERL_COMPILE_FLAGS := $(filter-out +deterministic,$(ERL_COMPILE_FLAGS)) EBIN = . @@ -40,7 +41,7 @@ make_emakefile: $(ERL_TOP)/make/make_emakefile $(ERL_COMPILE_FLAGS) -o$(EBIN) '*_SUITE_make' \ >> $(EMAKEFILE) -tests debug opt: make_emakefile +tests $(TYPES): make_emakefile erl $(ERL_MAKE_FLAGS) -make clean: diff --git a/lib/syntax_tools/test/syntax_tools_SUITE.erl b/lib/syntax_tools/test/syntax_tools_SUITE.erl index 3246ce7010..3553f7a71f 100644 --- a/lib/syntax_tools/test/syntax_tools_SUITE.erl +++ b/lib/syntax_tools/test/syntax_tools_SUITE.erl @@ -27,7 +27,7 @@ revert_map_type/1,wrapped_subtrees/1, t_abstract_type/1,t_erl_parse_type/1,t_type/1, t_epp_dodger/1,t_epp_dodger_clever/1, - t_comment_scan/1,t_prettypr/1]). + t_comment_scan/1,t_prettypr/1,test_named_fun_bind_ann/1]). suite() -> [{ct_hooks,[ts_install_cth]}]. @@ -36,7 +36,7 @@ all() -> wrapped_subtrees, t_abstract_type,t_erl_parse_type,t_type, t_epp_dodger,t_epp_dodger_clever, - t_comment_scan,t_prettypr]. + t_comment_scan,t_prettypr,test_named_fun_bind_ann]. groups() -> []. @@ -106,7 +106,8 @@ revert(Config) when is_list(Config) -> test_server:timetrap_cancel(Dog). revert_file(File, Path) -> - case epp:parse_file(File, Path, []) of + case epp:parse_file(File, [{includes,Path}, + res_word_option()]) of {ok,Fs0} -> Fs1 = erl_syntax:form_list(Fs0), Fs2 = erl_syntax_lib:map(fun (Node) -> Node end, Fs1), @@ -350,11 +351,37 @@ t_comment_scan(Config) when is_list(Config) -> t_prettypr(Config) when is_list(Config) -> DataDir = ?config(data_dir, Config), PrivDir = ?config(priv_dir, Config), - Filenames = ["type_specs.erl", - "specs_and_funs.erl"], + Filenames = test_files(), ok = test_prettypr(Filenames,DataDir,PrivDir), ok. +%% Test bug (#4733) fix for annotating bindings for named fun expressions +test_named_fun_bind_ann(Config) when is_list(Config) -> + Fn = {named_fun,{6,5}, + 'F', + [{clause,{6,9}, + [{var,{6,11},'Test'}], + [], + [{var,{7,13},'Test'}]}]}, + AnnT = erl_syntax_lib:annotate_bindings(Fn, []), + [Env, Bound, Free] = erl_syntax:get_ann(AnnT), + {'env',[]} = Env, + {'bound',[]} = Bound, + {'free',[]} = Free, + + NameVar = erl_syntax:named_fun_expr_name(AnnT), + Name = erl_syntax:variable_name(NameVar), + [NEnv, NBound, NFree] = erl_syntax:get_ann(NameVar), + {'env',[]} = NEnv, + {'bound',[Name]} = NBound, + {'free',[]} = NFree, + + [Clause] = erl_syntax:named_fun_expr_clauses(AnnT), + [CEnv, CBound, CFree] = erl_syntax:get_ann(Clause), + {'env',[Name]} = CEnv, + {'bound',['Test']} = CBound, + {'free', []} = CFree. + test_files(Config) -> DataDir = ?config(data_dir, Config), [ filename:join(DataDir,Filename) || Filename <- test_files() ]. @@ -391,15 +418,16 @@ test_comment_scan([File|Files],DataDir) -> test_prettypr([],_,_) -> ok; test_prettypr([File|Files],DataDir,PrivDir) -> Filename = filename:join(DataDir,File), + Options = [res_word_option()], io:format("Parsing ~p~n", [Filename]), - {ok, Fs0} = epp:parse_file(Filename, [], []), + {ok, Fs0} = epp:parse_file(Filename, Options), Fs = erl_syntax:form_list(Fs0), PP = erl_prettypr:format(Fs, [{paper, 120}, {ribbon, 110}]), io:put_chars(PP), OutFile = filename:join(PrivDir, File), ok = file:write_file(OutFile,unicode:characters_to_binary(PP)), io:format("Parsing OutFile: ~ts~n", [OutFile]), - {ok, Fs2} = epp:parse_file(OutFile, [], []), + {ok, Fs2} = epp:parse_file(OutFile, Options), case [Error || {error, _} = Error <- Fs2] of [] -> ok; @@ -408,22 +436,23 @@ test_prettypr([File|Files],DataDir,PrivDir) -> end, test_prettypr(Files,DataDir,PrivDir). - test_epp_dodger([], _, _) -> ok; test_epp_dodger([Filename|Files],DataDir,PrivDir) -> io:format("Parsing ~p~n", [Filename]), + Options = [{feature, maybe_expr, enable}], InFile = filename:join(DataDir, Filename), - Parsers = [{fun epp_dodger:parse_file/1,parse_file}, - {fun epp_dodger:quick_parse_file/1,quick_parse_file}, + Parsers = [{fun(File) -> epp_dodger:parse_file(File, Options) end,parse_file}, + {fun(File) -> epp_dodger:quick_parse_file(File, + Options) end,quick_parse_file}, {fun (File) -> {ok,Dev} = file:open(File,[read]), - Res = epp_dodger:parse(Dev), + Res = epp_dodger:parse(Dev, Options), file:close(File), Res end, parse}, {fun (File) -> {ok,Dev} = file:open(File,[read]), - Res = epp_dodger:quick_parse(Dev), + Res = epp_dodger:quick_parse(Dev, Options), file:close(File), Res end, quick_parse}], @@ -617,3 +646,9 @@ p_run_loop(Test, List, N, Refs0, Errors0) -> Refs = Refs0 -- [Ref], p_run_loop(Test, List, N, Refs, Errors) end. + +res_word_option() -> + Options = [{feature, maybe_expr, enable}], + {ok, {_Ftrs, ResWordFun}} = + erl_features:keyword_fun(Options, fun erl_scan:f_reserved_word/1), + {reserved_word_fun, ResWordFun}. diff --git a/lib/syntax_tools/test/syntax_tools_SUITE_data/syntax_tools_SUITE_test_module.erl b/lib/syntax_tools/test/syntax_tools_SUITE_data/syntax_tools_SUITE_test_module.erl index 07c419b4b7..9035139fea 100644 --- a/lib/syntax_tools/test/syntax_tools_SUITE_data/syntax_tools_SUITE_test_module.erl +++ b/lib/syntax_tools/test/syntax_tools_SUITE_data/syntax_tools_SUITE_test_module.erl @@ -8,6 +8,7 @@ sub_word/2,sub_word/3,left/2,left/3,right/2,right/3, sub_string/2,sub_string/3,centre/2,centre/3, join/2]). -export([to_upper/1, to_lower/1]). +-export([eep49/0]). -import(lists,[reverse/1,member/2]). @@ -235,8 +236,8 @@ substr1(String, _L) when is_list(String) -> []. %Be nice! substr2(String, 1) when is_list(String) -> String; substr2([_|String], S) -> substr2(String, S-1). -%% tokens(String, Seperators). -%% Return a list of tokens seperated by characters in Seperators. +%% tokens(String, Separators). +%% Return a list of tokens separated by characters in Separators. -spec tokens(String, SeparatorList) -> Tokens when String :: string(), @@ -538,3 +539,45 @@ join([], Sep) when is_list(Sep) -> []; join([H|T], Sep) -> H ++ lists:append([Sep ++ X || X <- T]). + +eep49() -> + maybe ok ?= ok end, + + {a,b} = + maybe + {ok,A} ?= {ok,a}, + {ok,B} ?= {ok,b}, + {A,B} + end, + + maybe + ok ?= {ok,x} + else + error -> error; + {error,_} -> error + end, + + maybe + ok ?= {ok,x} + else + error -> error + end, + + maybe + {ok,X} ?= {ok,x}, + {ok,Y} ?= {ok,y}, + {X,Y} + else + error -> error; + {error,_} -> error + end, + + maybe + {ok,X2} ?= {ok,x}, + {ok,Y2} ?= {ok,y}, + {X2,Y2} + else + error -> error + end, + + ok. diff --git a/lib/syntax_tools/vsn.mk b/lib/syntax_tools/vsn.mk index 87a6cb0158..1955fbdbc1 100644 --- a/lib/syntax_tools/vsn.mk +++ b/lib/syntax_tools/vsn.mk @@ -1 +1 @@ -SYNTAX_TOOLS_VSN = 2.6 +SYNTAX_TOOLS_VSN = 3.0.1 |