%% Copyright (c) 2008-2020 Robert Virding
%%
%% 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.

%% File    : lfe_qlc.erl
%% Author  : Robert Virding
%% Purpose : Lisp Flavoured Erlang interface to qlc transform.

%% Interface the standard Erlang QLC transformer. The only problem is
%% that this includes a lint check of the input QLC, which means it
%% will catch unbound variables. Seeing this call can be used from the
%% macro expander where we have no idea of variable binding we have to
%% be a bit cunning.

-module(lfe_qlc).

-export([expand/2]).

%% expand(ListComp, Options) -> {ok,Transform} | {error,Errors}.
%%  Instead of trying to find variables ourselves we first call the
%%  transformer with no variable bindings. We then create dummy
%%  variable bindings and call the transformer again. We will now get
%%  any "real" errors which we return.

expand(LC, _Opts) ->
    case qlc_pt:transform_expression(LC, []) of
        {not_ok,Errs} ->
            Uvs = unbound_vars(Errs, ordsets:new()),
            %% io:format("~p\n", [{Errs,Uvs}]),
            case qlc_pt:transform_expression(LC, Uvs) of
                {not_ok,Errors} -> {error,Errors};
                Ok              -> Ok
            end;
        Ok -> Ok
    end.

unbound_vars([{error,{_,_,{unbound_var,V}}}|Es], Uvs) ->
    unbound_vars(Es, ordsets:add_element({V,null}, Uvs));
unbound_vars([_|Es], Uvs) -> unbound_vars(Es, Uvs);
unbound_vars([], Uvs) -> Uvs.
