This means the second item in the AST is no longer an integer (representing the line), but a keywords list. Code that relies on the line information from AST or that manually generate AST nodes need to be properly updated.
65 lines
2.2 KiB
Erlang
65 lines
2.2 KiB
Erlang
-module(elixir_partials).
|
|
-export([handle/2, handle/3, is_sequential/1]).
|
|
-include("elixir.hrl").
|
|
-compile({parse_transform, elixir_transform}).
|
|
|
|
handle(Original, S) ->
|
|
handle(Original, S, default).
|
|
|
|
handle({ _, Meta, Args } = Original, S, Opt) when is_list(Args), S#elixir_scope.context /= assign ->
|
|
case convert(Args, S, Opt) of
|
|
{ Call, Def, SC } when Def /= [] ->
|
|
Final = validate(Meta, Def, SC),
|
|
Block = setelement(3, Original, Call),
|
|
elixir_translator:translate_fn(Meta, [{ Final, Block }], SC);
|
|
_ -> error
|
|
end;
|
|
|
|
handle(_Original, _S, _Opt) ->
|
|
error.
|
|
|
|
validate(Meta, Def, S) ->
|
|
validate(Meta, lists:sort(Def), 1, S).
|
|
|
|
validate(Meta, [{ Pos, Item }|T], Pos, S) ->
|
|
[Item|validate(Meta, T, Pos + 1, S)];
|
|
|
|
validate(Meta, [{ Pos, _ }|_], Expected, S) ->
|
|
elixir_errors:syntax_error(Meta, S#elixir_scope.file, "partial variable &~w cannot be defined without &~w", [Pos, Expected]);
|
|
|
|
validate(_Meta, [], _Pos, _S) ->
|
|
[].
|
|
|
|
%% This function receives arguments and then checks
|
|
%% the args for partial application. It returns a tuple
|
|
%% with three elements where the first element is the list
|
|
%% of call args, the second the list of def args for the
|
|
%% function definition and the third one is the new scope.
|
|
convert(List, S, Opt) -> convert(List, S, Opt, [], []).
|
|
|
|
convert([{'|', Meta, [_, _] = Args}|T], S, allow_tail, CallAcc, DefAcc) ->
|
|
{ NewArgs, NewDef, NewS } = convert(Args, S, allow_tail, [], DefAcc),
|
|
convert(T, NewS, allow_tail, [{ '|', Meta, NewArgs}|CallAcc], NewDef);
|
|
|
|
convert([{'&', Meta, [Pos]}|T], S, Opt, CallAcc, DefAcc) ->
|
|
case lists:keyfind(Pos, 1, DefAcc) of
|
|
false ->
|
|
{ Var, SC } = elixir_scope:build_ex_var(?line(Meta), S),
|
|
convert(T, SC, Opt, [Var|CallAcc], [{Pos,Var}|DefAcc]);
|
|
{Pos,Var} ->
|
|
convert(T, S, Opt, [Var|CallAcc], DefAcc)
|
|
end;
|
|
|
|
convert([H|T], S, Opt, CallAcc, DefAcc) ->
|
|
convert(T, S, Opt, [H|CallAcc], DefAcc);
|
|
|
|
convert([], S, _Opt, CallAcc, DefAcc) ->
|
|
{ lists:reverse(CallAcc), lists:reverse(DefAcc), S }.
|
|
|
|
is_sequential(List) -> is_sequential(List, 1).
|
|
|
|
is_sequential([{ '&', _, [Int] }|T], Int) ->
|
|
is_sequential(T, Int + 1);
|
|
|
|
is_sequential([], Int) when Int > 1 -> true;
|
|
is_sequential(_, _Int) -> false. |