%% %% Copyright 2014 - 2018 Dmitry Kolesnikov, 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. %% %% @doc %% evaluates a logical program -module(datalog_vm). -compile({parse_transform, partial}). -compile({parse_transform, category}). -include_lib("datum/include/datum.hrl"). -export([ horn/2, stream/1 ]). %% %% horn(Head, Horn) -> fun(Env) -> [HHorn | THorn] = [Spec#{'@' => F(Env)} || #{'@' := F} = Spec <- Horn], fun(SubQ) -> Heap = maps:from_list(lists:zip(Head, SubQ)), Stream = join(eval(Heap, HHorn), THorn), stream:map( fun(X) -> [maps:get(K, X) || K <- Head] end, Stream ) end end. join(Stream, [#{'.' := pipe, '@' := Pipe} | THorn]) -> join(Pipe(Stream), THorn); join(Stream, [HHorn | THorn]) -> join( stream:flat( stream:map(fun(Heap) -> eval(Heap, HHorn) end, Stream) ), THorn ); join(Stream, []) -> Stream. eval(Heap, #{'_' := Head, '@' := Fun} = Spec) -> SubQ = [term(T, Spec, Heap) || T <- Head], stream:map( fun(Tuple) -> %% Note: we need to give a priority to existed heap values, unless '_' %% maps:merge(Heap, maps:from_list( lists:zip(Head, Tuple) )) Prev = maps:filter(fun(_, X) -> X /= '_' end, Heap), This = maps:from_list( lists:zip(Head, Tuple) ), maps:merge(Heap, maps:merge(This, Prev)) end, Fun(SubQ) ). %% %% term(T, Spec, Heap) -> [undefined || term(T, Spec), term(T, Heap)]. term(T, Predicate) when is_atom(T) -> case Predicate of #{T := Value} -> Value; _ -> undefined end; term(T, _) -> T. %% %% evaluate stream stream(#{'.' := Keys, '>' := Spec, '@' := Gen}) -> fun(Env) -> fun(SubQ) -> (Gen(Keys, SubQ, Spec))(Env) end end; stream(#{'.' := Keys, '@' := Gen}) -> fun(Env) -> fun(SubQ) -> (Gen(Keys, SubQ))(Env) end end; stream(#{'_' := [_], '@' := Gen}) -> fun(Env) -> fun([X1]) -> (Gen(X1))(Env) end end; stream(#{'_' := [_, _], '@' := Gen}) -> fun(Env) -> fun([X1, X2]) -> (Gen(X1, X2))(Env) end end; stream(#{'_' := [_, _, _],'@' := Gen}) -> fun(Env) -> fun([X1, X2, X3]) -> (Gen(X1, X2, X3))(Env) end end; stream(#{'_' := [_, _, _, _], '@' := Gen}) -> fun(Env) -> fun([X1, X2, X3, X4]) -> (Gen(X1, X2, X3, X4))(Env) end end; stream(#{'_' := [_, _, _, _, _], '@' := Gen}) -> fun(Env) -> fun([X1, X2, X3, X4, X5]) -> (Gen(X1, X2, X3, X4, X5))(Env) end end.