Current section
Files
Jump to
Current section
Files
src/do.erl
%% This file is a copy of erl_id_trans.erl from the R14B02 Erlang/OTP
%% distribution, with modifications to make it implement Haskell-style
%% 'do' syntax in Erlang.
%% All modifications are (C) 2011-2013 VMware, Inc; Eduard Sergeev.
%%
%% ``The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved via the world wide web at http://www.erlang.org/.
%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
%%
%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
%% AB. All Rights Reserved.''
%%
-module(do).
-export([parse_transform/2, format_error/1]).
parse_transform(Forms, _Options) ->
Forms1 = forms(Forms),
%%io:format("~s~n", [erl_prettypr:format(erl_syntax:form_list(Forms1))]),
Forms1.
%% forms(Fs) -> lists:map(fun (F) -> form(F) end, Fs).
forms([F0|Fs0]) ->
F1 = try form(F0)
catch throw:{Error, Line} ->
{error, {Line, ?MODULE, Error}}
end,
Fs1 = forms(Fs0),
[F1|Fs1];
forms([]) -> [].
%% -type form(Form) -> Form.
form({attribute,Line,Attr,Val}) -> %The general attribute.
{attribute,Line,Attr,Val};
form({function,Line,Name0,Arity0,Clauses0}) ->
{Name,Arity,Clauses} = function(Name0, Arity0, Clauses0),
{function,Line,Name,Arity,Clauses};
% Mnemosyne, ignore...
form({rule,Line,Name,Arity,Body}) ->
{rule,Line,Name,Arity,Body}; % Dont dig into this
%% Extra forms from the parser.
form({error,E}) -> {error,E};
form({warning,W}) -> {warning,W};
form({eof,Line}) -> {eof,Line}.
%% -type function(atom(), integer(), [Clause]) -> {atom(),integer(),[Clause]}.
function(Name, Arity, Clauses0) ->
Clauses1 = clauses(Clauses0),
{Name, Arity, Clauses1}.
%% -type clauses([Clause]) -> [Clause].
clauses([C0|Cs]) ->
C1 = clause(C0, []),
[C1|clauses(Cs)];
clauses([]) -> [].
%% -type clause(Clause) -> Clause.
clause({clause, Line, Head, Guard, Body}, MonadStack) ->
{clause, Line, Head, Guard, exprs(Body, MonadStack)}.
%% -type pattern(Pattern) -> Pattern.
%% N.B. Only valid patterns are included here.
pattern({var,Line,V}) -> {var,Line,V};
pattern({match,Line,L0,R0}) ->
L1 = pattern(L0),
R1 = pattern(R0),
{match,Line,L1,R1};
pattern({integer,Line,I}) -> {integer,Line,I};
pattern({char,Line,C}) -> {char,Line,C};
pattern({float,Line,F}) -> {float,Line,F};
pattern({atom,Line,A}) -> {atom,Line,A};
pattern({string,Line,S}) -> {string,Line,S};
pattern({nil,Line}) -> {nil,Line};
pattern({cons,Line,H0,T0}) ->
H1 = pattern(H0),
T1 = pattern(T0),
{cons,Line,H1,T1};
pattern({tuple,Line,Ps0}) ->
Ps1 = pattern_list(Ps0),
{tuple,Line,Ps1};
%% OTP 17.0: EEP 443: Map pattern
pattern({map, Line, Fields0}) ->
Fields1 = map_fields(Fields0, []),
{map, Line, Fields1};
%%pattern({struct,Line,Tag,Ps0}) ->
%% Ps1 = pattern_list(Ps0),
%% {struct,Line,Tag,Ps1};
pattern({record,Line,Name,Pfs0}) ->
Pfs1 = pattern_fields(Pfs0),
{record,Line,Name,Pfs1};
pattern({record_index,Line,Name,Field0}) ->
Field1 = pattern(Field0),
{record_index,Line,Name,Field1};
%% record_field occurs in query expressions
pattern({record_field,Line,Rec0,Name,Field0}) ->
Rec1 = expr(Rec0, []),
Field1 = expr(Field0, []),
{record_field,Line,Rec1,Name,Field1};
pattern({record_field,Line,Rec0,Field0}) ->
Rec1 = expr(Rec0, []),
Field1 = expr(Field0, []),
{record_field,Line,Rec1,Field1};
pattern({bin,Line,Fs}) ->
Fs2 = pattern_grp(Fs),
{bin,Line,Fs2};
pattern({op,Line,Op,A}) ->
{op,Line,Op,A};
pattern({op,Line,Op,L,R}) ->
{op,Line,Op,L,R}.
pattern_grp([{bin_element,L1,E1,S1,T1} | Fs]) ->
S2 = case S1 of
default ->
default;
_ ->
expr(S1, [])
end,
T2 = case T1 of
default ->
default;
_ ->
bit_types(T1)
end,
[{bin_element,L1,expr(E1, []),S2,T2} | pattern_grp(Fs)];
pattern_grp([]) ->
[].
bit_types([]) ->
[];
bit_types([Atom | Rest]) when is_atom(Atom) ->
[Atom | bit_types(Rest)];
bit_types([{Atom, Integer} | Rest])
when is_atom(Atom) andalso is_integer(Integer) ->
[{Atom, Integer} | bit_types(Rest)].
%% -type pattern_list([Pattern]) -> [Pattern].
%% These patterns are processed "in parallel" for purposes of variable
%% definition etc.
pattern_list([P0|Ps]) ->
P1 = pattern(P0),
[P1|pattern_list(Ps)];
pattern_list([]) -> [].
%% -type pattern_fields([Field]) -> [Field].
%% N.B. Field names are full expressions here but only atoms are allowed
%% by the *linter*!.
pattern_fields([{record_field,Lf,{atom,La,F},P0}|Pfs]) ->
P1 = pattern(P0),
[{record_field,Lf,{atom,La,F},P1}|pattern_fields(Pfs)];
pattern_fields([{record_field,Lf,{var,La,'_'},P0}|Pfs]) ->
P1 = pattern(P0),
[{record_field,Lf,{var,La,'_'},P1}|pattern_fields(Pfs)];
pattern_fields([]) -> [].
%% -type exprs([Expression]) -> [Expression].
%% These expressions are processed "sequentially" for purposes of variable
%% definition etc.
exprs([E0|Es], MonadStack) ->
E1 = expr(E0, MonadStack),
[E1|exprs(Es, MonadStack)];
exprs([], _MonadStack) -> [].
%% -type expr(Expression) -> Expression.
expr({var, Line, V}, _MonadStack) -> {var, Line, V};
expr({integer, Line, I}, _MonadStack) -> {integer, Line, I};
expr({float, Line, F}, _MonadStack) -> {float, Line, F};
expr({atom, Line, A}, _MonadStack) -> {atom, Line, A};
expr({string, Line, S}, _MonadStack) -> {string, Line, S};
expr({char, Line, C}, _MonadStack) -> {char, Line, C};
expr({nil, Line}, _MonadStack) -> {nil, Line};
expr({cons, Line, H0, T0}, MonadStack) ->
H1 = expr(H0, MonadStack),
T1 = expr(T0, MonadStack), %% They see the same variables
{cons, Line, H1, T1};
expr({lc, Line, E0, Qs0}, MonadStack) ->
Qs1 = lc_bc_quals(Qs0, MonadStack),
E1 = expr(E0, MonadStack),
{lc, Line, E1, Qs1};
expr({bc, Line, E0, Qs0}, MonadStack) ->
Qs1 = lc_bc_quals(Qs0, MonadStack),
E1 = expr(E0, MonadStack),
{bc, Line, E1, Qs1};
expr({tuple, Line, Es0}, MonadStack) ->
Es1 = expr_list(Es0, MonadStack),
{tuple, Line, Es1};
%% OTP 17.0: EEP 443: Map construction
expr({map, Line, Fields0}, MonadStack) ->
Fields1 = map_fields(Fields0, MonadStack),
{map, Line, Fields1};
%% OTP 17.0: EEP 443: Map update
expr({map, Line, Expr0, Fields0}, MonadStack) ->
Expr1 = expr(Expr0, MonadStack),
Fields1 = map_fields(Fields0, MonadStack),
{map, Line, Expr1, Fields1};
expr({record_index, Line, Name, Field0}, MonadStack) ->
Field1 = expr(Field0, MonadStack),
{record_index, Line, Name, Field1};
expr({record, Line, Name, Inits0}, MonadStack) ->
Inits1 = record_inits(Inits0, MonadStack),
{record, Line, Name, Inits1};
expr({record_field, Line, Rec0, Name, Field0}, MonadStack) ->
Rec1 = expr(Rec0, MonadStack),
Field1 = expr(Field0, MonadStack),
{record_field, Line, Rec1, Name, Field1};
expr({record, Line, Rec0, Name, Upds0}, MonadStack) ->
Rec1 = expr(Rec0, MonadStack),
Upds1 = record_updates(Upds0, MonadStack),
{record, Line, Rec1, Name, Upds1};
expr({record_field, Line, Rec0, Field0}, MonadStack) ->
Rec1 = expr(Rec0, MonadStack),
Field1 = expr(Field0, MonadStack),
{record_field, Line, Rec1, Field1};
expr({block, Line, Es0}, MonadStack) ->
%% Unfold block into a sequence.
Es1 = exprs(Es0, MonadStack),
{block, Line, Es1};
expr({'if', Line, Cs0}, MonadStack) ->
Cs1 = icr_clauses(Cs0, MonadStack),
{'if', Line, Cs1};
expr({'case', Line, E0, Cs0}, MonadStack) ->
E1 = expr(E0, MonadStack),
Cs1 = icr_clauses(Cs0, MonadStack),
{'case', Line, E1, Cs1};
expr({'receive', Line, Cs0}, MonadStack) ->
Cs1 = icr_clauses(Cs0, MonadStack),
{'receive', Line, Cs1};
expr({'receive', Line, Cs0, To0, ToEs0}, MonadStack) ->
To1 = expr(To0, MonadStack),
ToEs1 = exprs(ToEs0, MonadStack),
Cs1 = icr_clauses(Cs0, MonadStack),
{'receive', Line, Cs1, To1, ToEs1};
expr({'try', Line, Es0, Scs0, Ccs0, As0}, MonadStack) ->
Es1 = exprs(Es0, MonadStack),
Scs1 = icr_clauses(Scs0, MonadStack),
Ccs1 = icr_clauses(Ccs0, MonadStack),
As1 = exprs(As0, MonadStack),
{'try', Line, Es1, Scs1, Ccs1, As1};
expr({'fun', Line, Body}, MonadStack) ->
case Body of
{clauses, Cs0} ->
Cs1 = fun_clauses(Cs0, MonadStack),
{'fun', Line, {clauses, Cs1}};
{function, F, A} ->
{'fun', Line, {function, F, A}};
{function, M, F, A} -> %% R10B-6: fun M:F/A.
{'fun', Line, {function, M, F, A}}
end;
%% OTP 17.0: EEP 37: Funs with names
expr({named_fun, Line, Name, Cs0}, MonadStack) ->
Cs1 = fun_clauses(Cs0, MonadStack),
{named_fun, Line, Name, Cs1};
%% do syntax detection:
expr({call, Line, {atom, _Line1, do},
[{lc, _Line2, {AtomOrVar, _Line3, _MonadModule} = Monad, Qs}]},
MonadStack) when AtomOrVar =:= atom orelse AtomOrVar =:= var ->
%% 'do' calls of a particular form:
%% do([ MonadMod || Qualifiers ])
{call, Line,
{'fun', Line,
{clauses,
[{clause, Line, [], [], do_syntax(Qs, [Monad | MonadStack])}]}}, []};
%% 'return' and 'fail' syntax detection and transformation:
expr({call, Line, {atom, Line1, ReturnOrFail}, As0},
[Monad|_Monads] = MonadStack) when ReturnOrFail =:= return orelse
ReturnOrFail =:= fail->
%% 'return' calls of a particular form:
%% return(Arguments), and
%% 'fail' calls of a particular form:
%% fail(Arguments)
%% Transformed to:
%% "Monad:return(Args)" or "Monad:fail(Args)" in monadic context
{call, Line, {remote, Line1, Monad, {atom, Line1, ReturnOrFail}},
expr_list(As0, MonadStack)};
expr({call, Line, F0, As0}, MonadStack) ->
%% N.B. If F an atom then call to local function or BIF, if F a
%% remote structure (see below) then call to other module,
%% otherwise apply to "function".
F1 = expr(F0, MonadStack),
As1 = expr_list(As0, MonadStack),
{call, Line, F1, As1};
expr({'catch', Line, E0}, MonadStack) ->
%% No new variables added.
E1 = expr(E0, MonadStack),
{'catch', Line, E1};
expr({'query', Line, E0}, MonadStack) ->
%% lc expression
E = expr(E0, MonadStack),
{'query', Line, E};
expr({match, Line, P0, E0}, MonadStack) ->
E1 = expr(E0, MonadStack),
P1 = pattern(P0),
{match, Line, P1, E1};
expr({bin, Line, Fs}, _MonadStack) ->
Fs2 = pattern_grp(Fs),
{bin, Line, Fs2};
expr({op, Line, Op, A0}, MonadStack) ->
A1 = expr(A0, MonadStack),
{op, Line, Op, A1};
expr({op, Line, Op, L0, R0}, MonadStack) ->
L1 = expr(L0, MonadStack),
R1 = expr(R0, MonadStack), %% They see the same variables
{op, Line, Op, L1, R1};
%% The following are not allowed to occur anywhere!
expr({remote, Line, M0, F0}, MonadStack) ->
M1 = expr(M0, MonadStack),
F1 = expr(F0, MonadStack),
{remote, Line, M1, F1}.
%% -type expr_list([Expression]) -> [Expression].
%% These expressions are processed "in parallel" for purposes of variable
%% definition etc.
expr_list([E0|Es], MonadStack) ->
E1 = expr(E0, MonadStack),
[E1|expr_list(Es, MonadStack)];
expr_list([], _MonadStack) -> [].
%% -type map_fields([MapField]) -> [MapField].
map_fields([{map_field_assoc, Line, ExpK0, ExpV0}|Fs], MonadStack) ->
ExpK1 = expr(ExpK0, MonadStack),
ExpV1 = expr(ExpV0, MonadStack),
[{map_field_assoc, Line, ExpK1, ExpV1}|map_fields(Fs, MonadStack)];
map_fields([{map_field_exact, Line, ExpK0, ExpV0}|Fs], MonadStack) ->
ExpK1 = expr(ExpK0, MonadStack),
ExpV1 = expr(ExpV0, MonadStack),
[{map_field_exact, Line, ExpK1, ExpV1}|map_fields(Fs, MonadStack)];
map_fields([], _MoandStack) -> [].
%% -type record_inits([RecordInit]) -> [RecordInit].
%% N.B. Field names are full expressions here but only atoms are allowed
%% by the *linter*!.
record_inits([{record_field, Lf, {atom, La, F}, Val0}|Is], MonadStack) ->
Val1 = expr(Val0, MonadStack),
[{record_field, Lf, {atom, La, F}, Val1}|record_inits(Is, MonadStack)];
record_inits([{record_field, Lf, {var, La, '_'}, Val0}|Is], MonadStack) ->
Val1 = expr(Val0, MonadStack),
[{record_field, Lf, {var, La, '_'}, Val1}|record_inits(Is, MonadStack)];
record_inits([], _MonadStack) -> [].
%% -type record_updates([RecordUpd]) -> [RecordUpd].
%% N.B. Field names are full expressions here but only atoms are allowed
%% by the *linter*!.
record_updates([{record_field, Lf, {atom, La, F}, Val0}|Us], MonadStack) ->
Val1 = expr(Val0, MonadStack),
[{record_field, Lf, {atom, La, F}, Val1}|record_updates(Us, MonadStack)];
record_updates([], _MonadStack) -> [].
%% -type icr_clauses([Clause]) -> [Clause].
icr_clauses([C0|Cs], MonadStack) ->
C1 = clause(C0, MonadStack),
[C1|icr_clauses(Cs, MonadStack)];
icr_clauses([], _MonadStack) -> [].
%% -type lc_bc_quals([Qualifier]) -> [Qualifier].
%% Allow filters to be both guard tests and general expressions.
lc_bc_quals([{generate, Line, P0, E0}|Qs], MonadStack) ->
E1 = expr(E0, MonadStack),
P1 = pattern(P0),
[{generate, Line, P1, E1}|lc_bc_quals(Qs, MonadStack)];
lc_bc_quals([{b_generate, Line, P0, E0}|Qs], MonadStack) ->
E1 = expr(E0, MonadStack),
P1 = pattern(P0),
[{b_generate, Line, P1, E1}|lc_bc_quals(Qs, MonadStack)];
lc_bc_quals([E0|Qs], MonadStack) ->
E1 = expr(E0, MonadStack),
[E1|lc_bc_quals(Qs, MonadStack)];
lc_bc_quals([], _MonadStack) -> [].
%% -type fun_clauses([Clause]) -> [Clause].
fun_clauses([C0|Cs], MonadStack) ->
C1 = clause(C0, MonadStack),
[C1|fun_clauses(Cs, MonadStack)];
fun_clauses([], _MonadStack) -> [].
%% 'do' syntax transformation:
do_syntax([], [{_AtomOrVar, MLine, _MonadModule} | _MonadStack]) ->
transform_error("A 'do' construct cannot be empty", MLine);
do_syntax([{GenerateOrMatch, Line, _Pattern, _Expr}], _MonadStack)
when GenerateOrMatch =:= generate orelse GenerateOrMatch =:= match ->
transform_error("The last statement in a 'do' construct must be an expression", Line);
do_syntax([{generate, Line, {var, _Line, _Var} = Pattern, Expr} | Exprs],
[Monad | _Monads] = MonadStack) ->
%% "Pattern <- Expr, Tail" where Pattern is a simple variable
%% is transformed to
%% "Monad:'>>='(Expr, fun (Pattern) -> Tail')"
%% without a fail to match clause
[{call, Line, {remote, Line, Monad, {atom, Line, '>>='}},
[expr(Expr, MonadStack),
{'fun', Line,
{clauses,
[{clause, Line, [Pattern], [], do_syntax(Exprs, MonadStack)}]}}]}];
do_syntax([{generate, Line, Pattern, Expr} | Exprs],
[Monad | _Monads] = MonadStack) ->
%% "Pattern <- Expr, Tail" where Pattern is not a simple variable
%% is transformed to
%% "Monad:'>>='(Expr, fun (Pattern) -> Tail')"
%% with a fail clause if the function does not match
[{call, Line, {remote, Line, Monad, {atom, Line, '>>='}},
[expr(Expr, MonadStack),
{'fun', Line,
{clauses,
[{clause, Line, [Pattern], [], do_syntax(Exprs, MonadStack)},
{clause, Line, [{var, Line, '_'}], [],
[{call, Line, {remote, Line, Monad, {atom, Line, 'fail'}},
[{atom, Line, 'monad_badmatch'}]}]}]}}]}];
do_syntax([Expr], MonadStack) ->
[expr(Expr, MonadStack)]; %% Don't do '>>' chaining on the last elem
do_syntax([{match, _Line, _Pattern, _Expr} = Expr | Exprs],
MonadStack) ->
%% Handles 'let binding' in do expression a-la Haskell
[expr(Expr, MonadStack) | do_syntax(Exprs, MonadStack)];
do_syntax([Expr | Exprs], [Monad | _Monads] = MonadStack) ->
%% "Expr, Tail" is transformed to "Monad:'>>='(Expr, fun (_) -> Tail')"
%% Line is always the 2nd element of Expr
Line = element(2, Expr),
[{call, Line, {remote, Line, Monad, {atom, Line, '>>='}},
[expr(Expr, MonadStack),
{'fun', Line,
{clauses,
[{clause, Line,
[{var, Line, '_'}], [], do_syntax(Exprs, MonadStack)}]}}]}].
%% Use this function to report any parse_transform error. The
%% resulting error message will be displayed as an ordinary
%% compilation error in a standard format.
transform_error(Message, Line) ->
throw({Message, Line}).
%% This function is called by the Erlang compiler to obtain an error
%% message which will be shown to the user.
format_error(Message) ->
case io_lib:deep_char_list(Message) of
true -> Message;
_ -> io_lib:write(Message)
end.