%%
%% %CopyrightBegin%
%% 
%% Copyright Ericsson AB 2000-2021. All Rights Reserved.
%% 
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%%     http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%% 
%% %CopyrightEnd%
%%
%% Purpose: Core Erlang abstract syntax functions.

-module(core_lib).

-export([make_values/1]).
-export([is_var_used/2]).

-include("core_parse.hrl").

%% Removed functions

-removed([{get_anno,1,"use cerl:get_ann/1 instead"},
          {set_anno,2,"use cerl:set_ann/2 instead"}]).

-removed([{is_literal,1,"use cerl:is_literal/1 instead"},
          {is_literal_list,1,"use cerl:is_literal_list/1 instead"},
          {literal_value,1,"use cerl:concrete/1 instead"}]).

%% Make a suitable values structure, expr or values, depending on Expr.
-spec make_values([cerl:cerl()] | cerl:cerl()) -> cerl:cerl().

make_values([E]) -> E;
make_values([H|_]=Es) -> #c_values{anno=cerl:get_ann(H),es=Es};
make_values([]) -> #c_values{es=[]};
make_values(E) -> E.

%% Test if the variable VarName is used in Expr.
-spec is_var_used(cerl:var_name(), cerl:cerl()) -> boolean().

is_var_used(V, B) -> vu_expr(V, B).

vu_expr(V, #c_values{es=Es}) ->
    vu_expr_list(V, Es);
vu_expr(V, #c_var{name=V2}) -> V =:= V2;
vu_expr(_, #c_literal{}) -> false;
vu_expr(V, #c_cons{hd=H,tl=T}) ->
    vu_expr(V, H) orelse vu_expr(V, T);
vu_expr(V, #c_tuple{es=Es}) ->
    vu_expr_list(V, Es);
vu_expr(V, #c_map{arg=M,es=Es}) ->
    vu_expr(V, M) orelse vu_expr_list(V, Es);
vu_expr(V, #c_map_pair{key=Key,val=Val}) ->
    vu_expr_list(V, [Key,Val]);
vu_expr(V, #c_binary{segments=Ss}) ->
    vu_seg_list(V, Ss);
vu_expr(V, #c_fun{vars=Vs,body=B}) ->
    %% Variables in fun shadow previous variables
    case vu_var_list(V, Vs) of
	true -> false;
	false -> vu_expr(V, B)
    end;
vu_expr(V, #c_let{vars=Vs,arg=Arg,body=B}) ->
    case vu_expr(V, Arg) of
	true -> true;
	false ->
	    %% Variables in let shadow previous variables.
	    case vu_var_list(V, Vs) of
		true -> false;
		false -> vu_expr(V, B)
	    end
    end;
vu_expr(V, #c_letrec{defs=Fs,body=B}) ->
    lists:any(fun ({_,Fb}) -> vu_expr(V, Fb) end, Fs) orelse vu_expr(V, B);
vu_expr(V, #c_seq{arg=Arg,body=B}) ->
    vu_expr(V, Arg) orelse vu_expr(V, B);
vu_expr(V, #c_case{arg=Arg,clauses=Cs}) ->
    vu_expr(V, Arg) orelse vu_clauses(V, Cs);
vu_expr(V, #c_apply{op=Op,args=As}) ->
    vu_expr_list(V, [Op|As]);
vu_expr(V, #c_call{module=M,name=N,args=As}) ->
    vu_expr_list(V, [M,N|As]);
vu_expr(V, #c_primop{args=As}) ->		%Name is an atom
    vu_expr_list(V, As);
vu_expr(V, #c_catch{body=B}) ->
    vu_expr(V, B);
vu_expr(V, #c_try{arg=E,vars=Vs,body=B,evars=Evs,handler=H}) ->
    case vu_expr(V, E) of
	true -> true;
	false ->
	    %% Variables shadow previous ones.
	    case case vu_var_list(V, Vs) of
		     true -> false;
		     false -> vu_expr(V, B)
		 end of
		true -> true;
		false ->
		    case vu_var_list(V, Evs) of
			true -> false;
			false -> vu_expr(V, H)
		    end
	    end
    end.

vu_expr_list(V, Es) ->
    lists:any(fun(E) -> vu_expr(V, E) end, Es).

vu_seg_list(V, Ss) ->
    lists:any(fun (#c_bitstr{val=Val,size=Size}) ->
		      vu_expr(V, Val) orelse vu_expr(V, Size)
	      end, Ss).

-spec vu_clause(cerl:var_name(), cerl:c_clause()) -> boolean().

vu_clause(V, #c_clause{pats=Ps,guard=G,body=B}) ->
    vu_pattern_list(V, Ps) orelse  vu_expr(V, G) orelse vu_expr(V, B).

-spec vu_clauses(cerl:var_name(), [cerl:c_clause()]) -> boolean().

vu_clauses(V, Cs) ->
    lists:any(fun(C) -> vu_clause(V, C) end, Cs).

%% vu_pattern(VarName, Pattern) -> Used.
%% vu_pattern_list(VarName, [Pattern]) -> Used.
%%  Binary and map patterns can use variables.

vu_pattern(V, #c_var{name=V2}) ->
    V =:= V2;
vu_pattern(V, #c_cons{hd=H,tl=T}) ->
    vu_pattern(V, H) orelse vu_pattern(V, T);
vu_pattern(V, #c_tuple{es=Es}) ->
    vu_pattern_list(V, Es);
vu_pattern(V, #c_binary{segments=Ss}) ->
    vu_pat_seg_list(V, Ss);
vu_pattern(V, #c_map{es=Es}) ->
    vu_map_pairs(V, Es);
vu_pattern(V, #c_alias{var=Var,pat=P}) ->
    vu_pattern(V, Var) orelse vu_pattern(V, P);
vu_pattern(_V, #c_literal{}) -> false.

vu_pattern_list(V, Ps) ->
    lists:any(fun(P) -> vu_pattern(V, P) end, Ps).

vu_pat_seg_list(V, Ss) ->
    lists:any(fun(#c_bitstr{size=Size}) ->
                      vu_pattern(V, Size)
              end, Ss).

vu_map_pairs(V, [#c_map_pair{key=Key,val=Pat}|T]) ->
    vu_expr(V, Key) orelse
        vu_pattern(V, Pat) orelse
        vu_map_pairs(V, T);
vu_map_pairs(_, []) -> false.

-spec vu_var_list(cerl:var_name(), [cerl:c_var()]) -> boolean().

vu_var_list(V, Vs) ->
    lists:any(fun (#c_var{name=V2}) -> V =:= V2 end, Vs).
