Skip to content

Instantly share code, notes, and snippets.

@mprymek
Created February 23, 2015 00:46
Show Gist options
  • Save mprymek/579d02f6c878cc37ef30 to your computer and use it in GitHub Desktop.
Save mprymek/579d02f6c878cc37ef30 to your computer and use it in GitHub Desktop.
defmodule Exlog do
defmacro __using__ _opts do
quote do
import Exlog
end
end
def new do
{:ok,e} = :erlog.new
e
end
# TODO: doesn't work...
#def new_ets do
# {:ok, e} = :erlog.load(:erlog_ets,Exlog.new)
# e
#end
# NOTE: We do not return context here! You can't use assert(...) etc.
defmacro provable?(e,ex_clause) do
clause = ex2erlog ex_clause
quote do
{_e,{result,_bind}} = Exlog.e_prove unquote(e), unquote(clause)
result
end
end
# @TODO: test clause is of form a <- b
defmacro assert!(e,ex_clause) do
meta_predicate :assert!, e, {:assert, ex2erlog(ex_clause)}
end
defmacro assertz!(e,ex_clause) do
meta_predicate :assertz!, e, {:assertz, ex2erlog(ex_clause)}
end
defp meta_predicate(pred_name,e,clause) do
quote do
case Exlog.e_prove unquote(e), unquote(clause) do
{e,{true,_}} -> e
{e,result} ->
raise "Exlog.#{unquote(pred_name)}(#{inspect unquote(clause)}) failed with #{inspect result}."
end
end
end
defmacro prove(e,ex_clause) do
clause = ex2erlog ex_clause
quote do
Exlog.e_prove unquote(e), unquote(clause)
end
end
defmacro prove_all(e,ex_clause) do
clause = ex2erlog ex_clause
quote do
Exlog.e_prove_all unquote(e), unquote(clause)
end
end
# clause
# format: f(a) <- [ g(a), h(b,c) ] LIST IS MANDATORY HERE!
def ex2erlog({:<-, meta, [head,body]}) when is_list(body) do
e_head = ex2erlog head
e_body = body |> Enum.map(&ex2erlog/1)
result = {:{}, meta, [:':-',e_head|e_body]}
#IO.puts "HEAD: #{inspect head}"
#IO.puts "E_HEAD: #{inspect Code.eval_quoted(e_head)}"
#IO.puts "BODY: #{inspect body}"
#IO.puts "E_BODY: #{inspect e_body}"
#IO.puts "RESULT: #{inspect result}"
result
end
# s.x
def ex2erlog(dot_expr={{:., _, [_, _]}, _, _}), do: dot_expr
# atom
def ex2erlog(atom) when is_atom(atom), do: atom
# number
def ex2erlog(num) when is_integer(num) or is_float(num), do: num
# Prolog variable
def ex2erlog({:__aliases__, meta, [atom]}) when is_atom(atom) do
{:{}, meta, [atom]}
end
def ex2erlog({:_, meta, mod}) when is_atom(mod) do
{:{}, meta, [:_]}
end
# functor
def ex2erlog({fun, meta, args}) when is_atom(fun) and is_list(args) and (not (fun in [:<-])) do
e_args = args |> Enum.map(&ex2erlog/1)
{:{}, meta, [fun|e_args]}
end
# Elixir variable
def ex2erlog({atom, meta, mod}) when is_atom(atom) and is_atom(mod) do
{atom, meta, mod}
end
# string
def ex2erlog(str) when is_binary(str), do: str
def ex2erlog(any) do
raise "Invalid clause: #{inspect any}"
end
def e_prove(e,clause) do
case :erlog.prove clause, e do
{{:succeed, bindings}, e} -> {e,{true,bindings}}
{:fail,e} -> {e,{false,[]}}
{{:error, err},e} -> raise "Exlog error: #{inspect err}"
end
end
def e_prove_all(e,clause) do
case :erlog.prove clause, e do
{{:succeed, bindings}, e} ->
next_solution e, [bindings]
{:fail,e} -> {e,[]}
{{:error, err},e} -> raise "Exlog error: #{inspect err}"
end
end
defp next_solution(e,bindings1) do
case :erlog.next_solution e do
{{:succeed, bindings}, e} ->
next_solution e, [bindings|bindings1]
{:fail,e} -> {e,Enum.reverse(bindings1)}
{{:error, err},e} -> raise "Exlog error: #{inspect err}"
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment