summaryrefslogtreecommitdiff
path: root/lib/syntax_tools
diff options
context:
space:
mode:
Diffstat (limited to 'lib/syntax_tools')
-rw-r--r--lib/syntax_tools/doc/Makefile2
-rw-r--r--lib/syntax_tools/doc/src/notes.xml62
-rw-r--r--lib/syntax_tools/examples/Makefile2
-rw-r--r--lib/syntax_tools/src/Makefile2
-rw-r--r--lib/syntax_tools/src/epp_dodger.erl20
-rw-r--r--lib/syntax_tools/src/erl_prettypr.erl36
-rw-r--r--lib/syntax_tools/src/erl_syntax.erl245
-rw-r--r--lib/syntax_tools/src/erl_syntax_lib.erl48
-rw-r--r--lib/syntax_tools/src/merl.erl2
-rw-r--r--lib/syntax_tools/src/prettypr.erl2
-rw-r--r--lib/syntax_tools/src/syntax_tools.app.src2
-rw-r--r--lib/syntax_tools/test/Makefile3
-rw-r--r--lib/syntax_tools/test/syntax_tools_SUITE.erl59
-rw-r--r--lib/syntax_tools/test/syntax_tools_SUITE_data/syntax_tools_SUITE_test_module.erl47
-rw-r--r--lib/syntax_tools/vsn.mk2
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