Packages

XQuery 3.1 Processor and XMl Database

Current section

Files

Jump to
xqerl src xqerl_module.erl
Raw

src/xqerl_module.erl

%% -------------------------------------------------------------------
%%
%% xqerl - XQuery processor
%%
%% Copyright (c) 2019-2020 Zachary N. Dean All Rights Reserved.
%%
%% This file is provided to you 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.
%%
%% -------------------------------------------------------------------
-module(xqerl_module).
-include("xqerl_parser.hrl").
%% ====================================================================
%% API functions
%% ====================================================================
-export([
merge_library_trees/1,
expand_imports/1,
module_namespace/2
]).
%% ====================================================================
%% Internal functions
%% ====================================================================
%% When given a list of {Uri, #xqModule{}} tuples, follows imports for each
%% module and makes a list of all functions and variable asts in the modules
%% the original module depends on. If an imported module is not in the list
%% nothing is added. It is then assumed that the code is compiled already
%% This function is here to allow cyclic dependencies among modules.
%% Returns: [{Uri, ImportedAstList}]
expand_imports(List) ->
F = fun
({Uri, #xqError{}}) ->
{Uri, {[], [], [], []}};
({Uri, Mod}) ->
I = collect_imports(Mod, List),
Fs = filter(I, 'xqFunctionDef', Uri),
Vs = filter(I, 'xqVar', Uri),
{Uri, {clear_id(Fs), function_sigs(Fs), clear_id(Vs), variable_sigs(Vs)}}
end,
lists:map(F, List).
module_namespace(
#xqModule{
type = library,
declaration = #xqModuleDecl{namespace = ModNs}
},
_Def
) ->
ModNs;
module_namespace(_, Def) ->
Def.
collect_imports(
#xqModule{
type = library,
declaration = #xqModuleDecl{namespace = ModNs},
prolog = Prolog
},
All
) ->
Imports = get_imported(Prolog),
collect_imports(Imports, All, [ModNs]).
collect_imports([ModNs | Imports], All, Acc) ->
case lists:member(ModNs, Acc) of
true ->
% already in the list
collect_imports(Imports, All, Acc);
false ->
% find imports for this mod and add to the Imports
Pro = get_module_prolog(ModNs, All),
Imps = get_imported(Pro),
collect_imports(Imps ++ Imports, All, [ModNs | Acc])
end;
collect_imports([], All, Acc) ->
F = fun(Ns) ->
Prolog = get_module_prolog(Ns, All),
Vars = filter(Prolog, 'xqVar'),
Funs = filter(Prolog, 'xqFunctionDef'),
Funs ++ Vars
end,
lists:flatmap(F, Acc).
get_imported(undefined) -> [];
get_imported(Prolog) -> lists:usort([Ns || #xqImport{uri = Ns} <- Prolog]).
get_module_prolog(Ns, All) ->
P = [
Prolog
|| {ModNs, #xqModule{
type = library,
declaration = #xqModuleDecl{namespace = ModNs},
prolog = Prolog
}} <-
All,
ModNs == Ns
],
lists:flatten(P).
clear_id([#xqFunctionDef{} = H | T]) ->
[H#xqFunctionDef{id = 0} | clear_id(T)];
clear_id([#xqVar{} = H | T]) ->
[H#xqVar{id = 0} | clear_id(T)];
clear_id([]) ->
[].
%% merges library ASTs from the same target namespace into one module
merge_library_trees([Mod]) ->
Mod;
merge_library_trees([
#xqModule{
type = library,
declaration = #xqModuleDecl{namespace = ModNs, prefix = ModPxA},
prolog = PrologA
} = ModA,
#xqModule{
type = library,
declaration = #xqModuleDecl{namespace = ModNs, prefix = ModPxB},
prolog = PrologB
}
| Mods
]) ->
{PrologA1, PrologA2} = xqerl_static:prolog_order(PrologA),
{PrologB1, PrologB2} = xqerl_static:prolog_order(PrologB),
Prolog1 = unique_prolog_1(PrologA1, PrologB1),
Prolog2 = unique_prolog_2(PrologA2, PrologB2),
Prolog11 = patch_imports(Prolog1, ModNs),
Prolog12 =
case ModPxA == ModPxB of
true ->
Prolog11;
false ->
N = #xqNamespaceDecl{uri = ModNs, prefix = ModPxB},
[N | without(N, Prolog11)]
end,
NewA = ModA#xqModule{prolog = Prolog12 ++ Prolog2},
merge_library_trees([NewA | Mods]).
% if self is imported change it to namespace declaration unless already there.
patch_imports(Prolog, ModNs) ->
Self = [
#xqNamespaceDecl{uri = Ns, prefix = Px}
|| #xqImport{kind = module, uri = Ns, prefix = Px} <- Prolog,
Ns == ModNs
],
Flt = fun
(#xqImport{kind = module, uri = Ns}) when Ns == ModNs ->
false;
(_) ->
true
end,
case Self of
[] -> Prolog;
_ -> Self ++ lists:filter(Flt, Prolog)
end.
filter([E | T], Atom) when is_tuple(E), element(1, E) == Atom ->
[E | filter(T, Atom)];
filter([_ | T], Atom) ->
filter(T, Atom);
filter([], _) ->
[].
filter([#xqFunctionDef{name = #xqQName{namespace = Ns0}} = E | T], 'xqFunctionDef', Ns) when
Ns0 =/= Ns
->
[E | filter(T, 'xqFunctionDef', Ns)];
filter([#xqVar{name = #xqQName{namespace = Ns0}} = E | T], 'xqVar', Ns) when Ns0 =/= Ns ->
[E | filter(T, 'xqVar', Ns)];
filter([_ | T], Atom, Ns) ->
filter(T, Atom, Ns);
filter([], _, _) ->
[].
function_sigs(Functions) ->
%ModName = xqerl_static:string_atom(ModNs),
Specs = [
{
Name#xqQName{prefix = <<>>},
Type,
Annos,
begin
{F, A} = xqerl_static:function_hash_name(Name, Arity),
{xqerl_static:string_atom(Name#xqQName.namespace), F, A}
end,
Arity,
param_types(Params)
}
|| %id = Id,
#xqFunctionDef{
annotations = Annos,
arity = Arity,
params = Params,
name = Name,
type = Type
} <- Functions,
not_private(Annos)
],
Specs.
%% {Name, Type, Annos, function_name, External }
variable_sigs(Variables) ->
[
{
Name#xqQName{prefix = <<>>},
Type,
Annos,
{
xqerl_static:string_atom(Name#xqQName.namespace),
xqerl_static:variable_hash_name(Name)
},
External
}
|| #xqVar{
annotations = Annos,
name = Name,
external = External,
type = Type
} <- Variables
].
not_private(Annos) ->
[
ok
|| #xqAnnotation{
name = #xqQName{
namespace = <<"http://www.w3.org/2012/xquery">>,
local_name = <<"private">>
}
} <- Annos
] == [].
param_types(Params) ->
[T || #xqVar{type = T} <- Params].
unique_prolog_1(PrologA1, PrologB1) ->
F = fun(A, Acc) ->
without(A, Acc)
end,
Without = lists:foldl(F, PrologB1, PrologA1),
PrologA1 ++ Without.
unique_prolog_2(PrologA2, PrologB2) ->
F = fun
(#xqVar{}, Acc) -> Acc;
(#xqFunctionDef{}, Acc) -> Acc;
(A, Acc) -> without(A, Acc)
end,
Without = lists:foldl(F, PrologB2, PrologA2),
PrologA2 ++ Without.
without(
#xqOptionDecl{name = #xqQName{namespace = N, local_name = L}} = A,
[#xqOptionDecl{name = #xqQName{namespace = N, local_name = L}} | T]
) ->
without(A, T);
without(
#xqContextItemDecl{type = Y} = A,
[#xqContextItemDecl{type = Y} | T]
) ->
without(A, T);
without(
#xqDefaultNamespaceDecl{kind = K, uri = U} = A,
[#xqDefaultNamespaceDecl{kind = K, uri = U} | T]
) ->
without(A, T);
without(
#xqDecimalFormatDecl{name = #xqQName{namespace = N, local_name = L}} = A,
[#xqDecimalFormatDecl{name = #xqQName{namespace = N, local_name = L}} | T]
) ->
without(A, T);
without(
#xqCopyNamespacesDecl{inh = I, pre = P} = A,
[#xqCopyNamespacesDecl{inh = I, pre = P} | T]
) ->
without(A, T);
without(
#xqEmptyOrderDecl{mode = M} = A,
[#xqEmptyOrderDecl{mode = M} | T]
) ->
without(A, T);
without(
#xqOrderingModeDecl{mode = M} = A,
[#xqOrderingModeDecl{mode = M} | T]
) ->
without(A, T);
without(
#xqConstructionDecl{mode = M} = A,
[#xqConstructionDecl{mode = M} | T]
) ->
without(A, T);
without(
#xqBaseURIDecl{uri = U} = A,
[#xqBaseURIDecl{uri = U} | T]
) ->
without(A, T);
without(
#xqDefaultCollationDecl{uri = U} = A,
[#xqDefaultCollationDecl{uri = U} | T]
) ->
without(A, T);
without(
#xqBoundarySpaceDecl{mode = M} = A,
[#xqBoundarySpaceDecl{mode = M} | T]
) ->
without(A, T);
without(
#xqRevalidationDecl{kind = K} = A,
[#xqRevalidationDecl{kind = K} | T]
) ->
without(A, T);
without(
#xqNamespaceDecl{uri = U, prefix = P} = A,
[#xqNamespaceDecl{uri = U, prefix = P} | T]
) ->
without(A, T);
without(
#xqImport{kind = K, uri = U, prefix = P} = A,
[#xqImport{kind = K, uri = U, prefix = P} | T]
) ->
without(A, T);
without(A, [H | T]) ->
[H | without(A, T)];
without(_, []) ->
[].