aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpukkamustard <pukkamustard@posteo.net>2021-06-04 12:24:31 +0200
committerpukkamustard <pukkamustard@posteo.net>2021-06-04 12:25:50 +0200
commit5cdc359a0f6747debfa381b8a9fa703dfee73348 (patch)
tree467d1dbb437c721c989c3942188d0cbcf21c2795
parent59d0cbaddca1d0377f3c959933c0e453a6cb41f4 (diff)
-rw-r--r--lib/datalog/datalog2.ml269
-rw-r--r--lib/datalog/dune2
-rw-r--r--lib/datalog/rdf_datalog.ml269
-rw-r--r--lib/datalog/rdf_datalog.mli43
-rw-r--r--test/datalog/main.ml13
-rw-r--r--test/datalog/parser.ml33
6 files changed, 227 insertions, 402 deletions
diff --git a/lib/datalog/datalog2.ml b/lib/datalog/datalog2.ml
deleted file mode 100644
index 7669297..0000000
--- a/lib/datalog/datalog2.ml
+++ /dev/null
@@ -1,269 +0,0 @@
-module Triple = Rdf.Triple
-
-module D = Datalog_top_down.Make(struct
-
- (* The datalog implementation makes no differentiation between constants and symbols.
- * This does not make so much sense for us as constants are RDF terms but
- * its hard to make new RDF terms for symbols.
- *
- * Instead we use a simple type to differentiate between terms and symbols.
- *
- * Use polymorphic variant types as we do not want to expose this module anywhere.
- * *)
- type t =
- [ `Term of Rdf.Term.t
- | `Symbol of string
- ]
-
- let equal a b =
- match a,b with
- | `Term a_term, `Term b_term -> Rdf.Term.equal a_term b_term
- | `Symbol a_symbol, `Symbol b_symbol -> String.equal a_symbol b_symbol
- | _ -> false
-
- let hash = Hashtbl.hash
-
- let to_cbor = function
- | `Term term ->
- Cbor.Array [Cbor.TextString "t"; Rdf_cbor.cbor_of_term term]
- | `Symbol symbol ->
- Cbor.Array [Cbor.TextString "s"; Cbor.TextString symbol]
-
- let to_string a =
- Cbor.encode @@ to_cbor a
-
- let parser_symbol =
- let open Angstrom in
- let open Cbor.Parser in
- tuple (any_text_string >>=
- (fun s -> if String.equal s "s" then return () else fail "expecting an s"))
- any_text_string
- >>| (fun (_,s) -> s)
-
- let parser_term =
- let open Angstrom in
- let open Cbor.Parser in
- tuple (any_text_string >>=
- (fun s -> if String.equal s "t" then return () else fail "expecting an t"))
- Rdf_cbor.Parser.term
- >>| (fun (_,t) -> t)
-
- let parser_t =
- Angstrom.(choice [parser_term >>| (fun t -> `Term t);
- parser_symbol >>| (fun s -> `Symbol s)])
-
- let of_string s =
- Angstrom.parse_string ~consume:Angstrom.Consume.All parser_t s
- |> Result.get_ok
-
- let query = `Symbol "?query"
- end)
-
-module Term = struct
- type t = D.T.t
-
- let variable = D.T.mk_var
- let constant term =
- D.T.mk_const @@ `Term term
-
- let equal = D.T.eq
-
- let rec pp ppf = function
- | D.T.Var var_num ->
- Fmt.pf ppf "?%d" var_num
- | D.T.Apply (`Term term, [||]) ->
- Fmt.pf ppf "%a" Rdf.Term.pp term
- | D.T.Apply (`Symbol predicate, [||]) ->
- Fmt.pf ppf "%s" predicate
- | D.T.Apply (`Symbol predicate, terms) ->
- Fmt.pf ppf "@[<2>%s(%a)@]"
- predicate
- Fmt.(array ~sep:comma pp)
- terms
- | D.T.Apply (`Term predicate, terms) ->
- Fmt.pf ppf "@[<2>%a(%a)@]"
- (* A RDF term should not be in predicate position.... *)
- Rdf.Term.pp predicate
- Fmt.(array ~sep:comma pp)
- terms
-end
-
-module Atom = struct
- type t = D.T.t
-
- let make symbol terms =
- D.T.mk_apply_l (`Symbol symbol)
- terms
-
- let equal = D.T.eq
-
- let triple (t:Triple.t) =
- make "graph" [
- t.subject |> Triple.Subject.to_term |> Term.constant;
- t.predicate |> Triple.Predicate.to_term |> Term.constant;
- t.object' |> Triple.Object.to_term |> Term.constant;
- ]
-
- let pp = Term.pp
-end
-
-module Literal = struct
- type t = D.Lit.t
-
- let make_positive atom =
- D.Lit.mk_pos atom
-
- let make_negative atom =
- D.Lit.mk_neg atom
-
- let equal = D.Lit.eq
-
- let pp ppf = function
- | D.Lit.LitPos atom ->
- Atom.pp ppf atom
- | D.Lit.LitNeg atom ->
- Fmt.pf ppf "@[<1>~%a@]"
- Atom.pp atom
- | D.Lit.LitAggr _ ->
- Fmt.pf ppf "How did you do that??"
-
-end
-
-module Clause = struct
- type t = D.C.t
-
- let make = D.C.mk_clause
-
- let make_fact = D.C.mk_fact
-
- let equal = D.C.eq
-
- let pp ppf (clause:t) =
- match clause.body with
- | [] -> Fmt.pf ppf "@[%a.@]"
- Atom.pp clause.head
- | body -> Fmt.pf ppf "@[%a@ :-@%a.@]"
- Atom.pp clause.head
- Fmt.(list ~sep:comma Literal.pp) body
-end
-
-module DB = struct
- type t = D.DB.t
-
- let create () = D.DB.create ()
-
- let assume = D.DB.add_clause
-
- let assume_triple theory triple =
- triple
- |> Atom.triple
- |> Clause.make_fact
- |> assume theory
-
- type query = Atom.t
-
- let prove db query =
- D.ask db query
-end
-
-
-module Parser = struct
- open Angstrom
-
- let whitespace =
- many @@ choice [string " "; string "\n"; string "\t"]
- >>| ignore
-
- let xsd_string =
- char '"'
- *> take_while (fun c -> not @@ Char.equal '"' c)
- <* char '"'
- >>| (fun value -> Rdf.Literal.make value (Rdf.Namespace.xsd "string"))
-
- let iri =
- char '<'
- *> take_while (fun c -> not @@ Char.equal '>' c)
- <* char '>'
- >>| Rdf.Iri.of_string
-
- let constant =
- choice [
- xsd_string >>| Rdf.Term.of_literal;
- iri >>| Rdf.Term.of_iri
- ]
-
- let symbol =
- take_while (fun c ->
- let code = Char.code c in
- (* ASCII upper case *)
- (65 <= code && code <= 90)
- ||
- (* ASCII lower case *)
- (97 <= code && code <= 122)
- ||
- (* ASCII numbers *)
- (48 <= code && code <= 57))
-
- let number =
- take_while1
- (fun c ->
- let code = Char.code c in
- 48 <= code && code <= 57)
- >>| int_of_string
-
- let variable =
- char '?' *> number
-
- let term =
- choice [
- constant >>| Term.constant;
- variable >>| Term.variable]
-
- let comma =
- whitespace
- *> char ','
- <* whitespace
-
- let atom =
- Atom.make
- <$> symbol
- <* char '('
- <* whitespace
- <*> (sep_by1 comma term)
- <* whitespace
- <* char ')'
-
- let literal =
- (* TODO parse negative literals *)
- atom >>| Literal.make_positive
-
- let fact =
- Clause.make_fact
- <$> atom
- <* whitespace
- <* char '.'
-
- let rule =
- Clause.make
- <$> atom
- <* whitespace
- <* string ":-"
- <* whitespace
- <*> (sep_by1 comma literal)
- <* whitespace
- <* char '.'
-
- let clause = choice [ fact; rule ]
-
- let query = literal <* char '?'
-
- let db =
- (fun clauses ->
- let db = D.DB.create () in
- D.DB.add_clauses db clauses;
- db)
- <$> (whitespace
- *> sep_by whitespace clause
- <* whitespace)
-
-end
diff --git a/lib/datalog/dune b/lib/datalog/dune
index 948c35e..155f9c4 100644
--- a/lib/datalog/dune
+++ b/lib/datalog/dune
@@ -1,4 +1,4 @@
(library
(name rdf_datalog)
- (modules rdf_datalog datalog datalog2)
+ (modules rdf_datalog datalog)
(libraries rdf rdf_cbor fmt angstrom datalog.top_down))
diff --git a/lib/datalog/rdf_datalog.ml b/lib/datalog/rdf_datalog.ml
index 67d23a3..d1c97ed 100644
--- a/lib/datalog/rdf_datalog.ml
+++ b/lib/datalog/rdf_datalog.ml
@@ -1,59 +1,119 @@
-(*
- * SPDX-FileCopyrightText: 2021 petites singularit├ęs <ps-dream@lesoiseaux.io>
- * SPDX-FileCopyrightText: 2021 pukkamustard <pukkamustard@posteo.net>
- *
- * SPDX-License-Identifier: AGPL-3.0-or-later
- *)
-
module Triple = Rdf.Triple
-module D = Datalog.Make(struct
- type t = Rdf.Term.t
+module D = Datalog_top_down.Make(struct
+
+ (* The datalog implementation makes no differentiation between constants and symbols.
+ * This does not make so much sense for us as constants are RDF terms but
+ * its hard to make new RDF terms for symbols.
+ *
+ * Instead we use a simple type to differentiate between terms and symbols.
+ *
+ * Use polymorphic variant types as we do not want to expose this module anywhere.
+ * *)
+ type t =
+ [ `Term of Rdf.Term.t
+ | `Symbol of string
+ ]
+
+ let equal a b =
+ match a,b with
+ | `Term a_term, `Term b_term -> Rdf.Term.equal a_term b_term
+ | `Symbol a_symbol, `Symbol b_symbol -> String.equal a_symbol b_symbol
+ | _ -> false
- let equal = Rdf.Term.equal
let hash = Hashtbl.hash
- end)
+ let to_cbor = function
+ | `Term term ->
+ Cbor.Array [Cbor.TextString "t"; Rdf_cbor.cbor_of_term term]
+ | `Symbol symbol ->
+ Cbor.Array [Cbor.TextString "s"; Cbor.TextString symbol]
+
+ let to_string a =
+ Cbor.encode @@ to_cbor a
+
+ let parser_symbol =
+ let open Angstrom in
+ let open Cbor.Parser in
+ tuple (any_text_string >>=
+ (fun s -> if String.equal s "s" then return () else fail "expecting an s"))
+ any_text_string
+ >>| (fun (_,s) -> s)
+
+ let parser_term =
+ let open Angstrom in
+ let open Cbor.Parser in
+ tuple (any_text_string >>=
+ (fun s -> if String.equal s "t" then return () else fail "expecting an t"))
+ Rdf_cbor.Parser.term
+ >>| (fun (_,t) -> t)
+
+ let parser_t =
+ Angstrom.(choice [parser_term >>| (fun t -> `Term t);
+ parser_symbol >>| (fun s -> `Symbol s)])
+
+ let of_string s =
+ Angstrom.parse_string ~consume:Angstrom.Consume.All parser_t s
+ |> Result.get_ok
+
+ let query = `Symbol "?query"
+ end)
module Term = struct
- type t = D.term
- let variable = D.mkvar
- let constant = D.mkval
-
- let equal a b =
- let to_term =
- D.spreadterm
- (fun _ ->"urn:ocaml-rdf:datalog-variable" |> Rdf.Iri.of_string |> Rdf.Term.of_iri)
- (fun term -> term) in
- Rdf.Term.equal (to_term a) (to_term b)
-
-
- let pp ppf term =
- Fmt.pf ppf "@[<h>%s@]"
- (D.spreadterm
- (fun var -> "?" ^ var)
- (fun value -> Fmt.to_to_string Rdf.Term.pp value)
- term)
+ type t = D.T.t
+
+ let variable = D.T.mk_var
+ let constant term =
+ D.T.mk_const @@ `Term term
+
+ let equal = D.T.eq
+
+ let rec pp ppf = function
+ | D.T.Var var_num ->
+ Fmt.pf ppf "?%d" var_num
+ | D.T.Apply (`Term term, [||]) ->
+ Fmt.pf ppf "%a" Rdf.Term.pp term
+ | D.T.Apply (`Symbol predicate, [||]) ->
+ Fmt.pf ppf "%s" predicate
+ | D.T.Apply (`Symbol predicate, terms) ->
+ Fmt.pf ppf "@[<2>%s(%a)@]"
+ predicate
+ Fmt.(array ~sep:comma pp)
+ terms
+ | D.T.Apply (`Term predicate, terms) ->
+ Fmt.pf ppf "@[<2>%a(%a)@]"
+ (* A RDF term should not be in predicate position.... *)
+ Rdf.Term.pp predicate
+ Fmt.(array ~sep:comma pp)
+ terms
end
type predicate = string
-module Literal = struct
- type t = D.literal
+module Atom = struct
+ type t = D.T.t
+
+ let make symbol terms =
+ D.T.mk_apply_l (`Symbol symbol)
+ terms
- let make predicate terms =
- D.mkliteral predicate terms
+ (* Note this is not type safe as ccube/datalog supports recursive terms (e.g. p(q(x), s(t(n))).
+ * We provide a simpler interface that does not support recursive terms. Here this abstraction breaks ...
+ * *)
- let predicate literal =
- D.getpred literal
+ let predicate = function
+ | D.T.Apply (`Symbol predicate, _) ->
+ predicate
+ | _ ->
+ "There goes our type safety..."
- let terms literal =
- D.getterms literal
+ let terms = function
+ | D.T.Apply (_, terms) ->
+ Array.to_list terms
+ | _ ->
+ []
- let equal a b =
- (String.equal (predicate a) (predicate b))
- &&
- (List.for_all2 Term.equal (terms a) (terms b))
+ let equal = D.T.eq
let triple (t:Triple.t) =
make "graph" [
@@ -62,61 +122,69 @@ module Literal = struct
t.object' |> Triple.Object.to_term |> Term.constant;
]
- let pp ppf literal=
- (* TODO the indent should be the length of the predicate + 1 *)
- Fmt.pf ppf "@[<2>%s(%a)@]"
- (predicate literal)
- Fmt.(list ~sep:comma Term.pp)
- (terms literal)
-
+ let pp = Term.pp
end
-module Clause = struct
- type t = D.clause
+module Literal = struct
+ type t = D.Lit.t
+
+ let make_positive atom =
+ D.Lit.mk_pos atom
+
+ let make_negative atom =
+ D.Lit.mk_neg atom
+
+ let equal = D.Lit.eq
- let make head body =
- D.mkclause head body
+ let pp ppf = function
+ | D.Lit.LitPos atom ->
+ Atom.pp ppf atom
+ | D.Lit.LitNeg atom ->
+ Fmt.pf ppf "@[<1>~%a@]"
+ Atom.pp atom
+ | D.Lit.LitAggr _ ->
+ Fmt.pf ppf "How did you do that??"
- let head clause =
- D.gethead clause
+end
+
+module Clause = struct
+ type t = D.C.t
- let body clause =
- D.getbody clause
+ let make = D.C.mk_clause
- let equal a b =
- (Literal.equal (head a) (head b))
- &&
- (try List.for_all2 Literal.equal (body a) (body b) with
- (* catch case where body has different length *)
- Invalid_argument _ -> false
- )
+ let make_fact = D.C.mk_fact
- let fact head = make head []
+ let equal = D.C.eq
- let pp ppf clause =
- match (body clause) with
- | [] -> Fmt.pf ppf "@[%a.@]" Literal.pp (head clause)
- | body -> Fmt.pf ppf "@[%a@ :-@%a.@]" Literal.pp (head clause)
+ let pp ppf (clause:t) =
+ match clause.body with
+ | [] -> Fmt.pf ppf "@[%a.@]"
+ Atom.pp clause.head
+ | body -> Fmt.pf ppf "@[%a@ :-@%a.@]"
+ Atom.pp clause.head
Fmt.(list ~sep:comma Literal.pp) body
end
-module Theory = struct
- type t = D.theory
+module DB = struct
+ type t = D.DB.t
- let create () = D.create 10
+ let create () = D.DB.create ()
- let assume = D.assume
+ let add_clause = D.DB.add_clause
- let assume_triple theory triple =
+ let add_triple theory triple =
triple
- |> Literal.triple
- |> Clause.fact
- |> assume theory
+ |> Atom.triple
+ |> Clause.make_fact
+ |> add_clause theory
- type query = Literal.t
- let prove = D.prove
+ type query = Atom.t
+
+ let prove db query =
+ D.ask db query
end
+
module Parser = struct
open Angstrom
@@ -154,8 +222,15 @@ module Parser = struct
(* ASCII numbers *)
(48 <= code && code <= 57))
+ let number =
+ take_while1
+ (fun c ->
+ let code = Char.code c in
+ 48 <= code && code <= 57)
+ >>| int_of_string
+
let variable =
- char '?' *> symbol
+ char '?' *> number
let term =
choice [
@@ -167,24 +242,28 @@ module Parser = struct
*> char ','
<* whitespace
- let literal =
- return Literal.make
- <*> symbol
+ let atom =
+ Atom.make
+ <$> symbol
<* char '('
<* whitespace
<*> (sep_by1 comma term)
<* whitespace
<* char ')'
+ let literal =
+ (* TODO parse negative literals *)
+ atom >>| Literal.make_positive
+
let fact =
- literal
+ Clause.make_fact
+ <$> atom
<* whitespace
<* char '.'
- >>| Clause.fact
let rule =
- return Clause.make
- <*> literal
+ Clause.make
+ <$> atom
<* whitespace
<* string ":-"
<* whitespace
@@ -194,15 +273,15 @@ module Parser = struct
let clause = choice [ fact; rule ]
- let query = literal <* char '?'
+ let query = atom <* char '?'
- let theory =
- whitespace
- *> sep_by whitespace clause
- <* whitespace
- >>| (fun clauses ->
- let theory = Theory.create () in
- List.iter (fun clause -> Theory.assume theory clause) clauses;
- theory)
+ let db =
+ (fun clauses ->
+ let db = D.DB.create () in
+ D.DB.add_clauses db clauses;
+ db)
+ <$> (whitespace
+ *> sep_by whitespace clause
+ <* whitespace)
end
diff --git a/lib/datalog/rdf_datalog.mli b/lib/datalog/rdf_datalog.mli
index d1368ce..c2acad1 100644
--- a/lib/datalog/rdf_datalog.mli
+++ b/lib/datalog/rdf_datalog.mli
@@ -7,7 +7,7 @@
module Term : sig
type t
- val variable : string -> t
+ val variable : int -> t
val constant: Rdf.Term.t -> t
val equal : t -> t -> bool
@@ -18,7 +18,7 @@ end
type predicate = string
-module Literal : sig
+module Atom: sig
type t
val make : predicate -> Term.t list -> t
@@ -33,29 +33,41 @@ module Literal : sig
[@@ocaml.toplevel_printer]
end
-module Clause : sig
+module Literal : sig
type t
- val make : Literal.t -> Literal.t list -> t
- val head : t -> Literal.t
- val body : t -> Literal.t list
+
+ val make_positive : Atom.t -> t
+ val make_negative : Atom.t -> t
val equal : t -> t -> bool
- val fact : Literal.t -> t
+ val pp : t Fmt.t
+ [@@ocaml.toplevel_printer]
+end
+
+module Clause : sig
+ type t
+ val make : Atom.t -> Literal.t list -> t
+ val make_fact : Atom.t -> t
+
+ (* val head : t -> Literal.t
+ * val body : t -> Literal.t list *)
+
+ val equal : t -> t -> bool
val pp : t Fmt.t
[@@ocaml.toplevel_printer]
end
-module Theory : sig
+module DB: sig
type t
val create : unit -> t
- val assume : t -> Clause.t -> unit
- val assume_triple : t -> Rdf.Triple.t -> unit
+ val add_clause: t -> Clause.t -> unit
+ val add_triple : t -> Rdf.Triple.t -> unit
- type query = Literal.t
- val prove : t -> query -> Literal.t list
+ type query = Atom.t
+ val prove : t -> query -> Atom.t list
end
module Parser : sig
@@ -68,14 +80,15 @@ module Parser : sig
val iri : Rdf.Iri.t Angstrom.t
val constant : Rdf.Term.t Angstrom.t
- val variable : string Angstrom.t
+ val variable : int Angstrom.t
val term : Term.t Angstrom.t
+ val atom : Atom.t Angstrom.t
val literal : Literal.t Angstrom.t
val clause : Clause.t Angstrom.t
- val query : Literal.t Angstrom.t
+ val query : Atom.t Angstrom.t
- val theory : Theory.t Angstrom.t
+ val db: DB.t Angstrom.t
end
diff --git a/test/datalog/main.ml b/test/datalog/main.ml
index ad6e295..29e6a3f 100644
--- a/test/datalog/main.ml
+++ b/test/datalog/main.ml
@@ -7,9 +7,10 @@
module Parser = Rdf_datalog.Parser
module Term = Rdf_datalog.Term
+module Atom = Rdf_datalog.Atom
module Literal = Rdf_datalog.Literal
module Clause = Rdf_datalog.Clause
-module Theory = Rdf_datalog.Theory
+module DB = Rdf_datalog.DB
let example =
{example|
@@ -18,8 +19,8 @@ let example =
edge("b", "c").
edge("c", "d").
- path(?a,?b) :- edge(?a,?b).
- path(?a,?c) :- edge(?a,?b), path(?b,?c).
+ path(?1,?2) :- edge(?1,?2).
+ path(?1,?3) :- edge(?1,?2), path(?2,?3).
|example}
let parse p string =
@@ -28,9 +29,9 @@ let parse p string =
p string
|> Result.get_ok
-let parse_theory = parse Parser.theory
+let parse_db = parse Parser.db
let parse_query = parse Parser.query
let () =
- Format.printf "%a" (Fmt.list Literal.pp) @@
- Theory.prove (parse_theory example) (parse_query {q|path(?a,?b)?|q})
+ Format.printf "%a" (Fmt.list Atom.pp) @@
+ DB.prove (parse_db example) (parse_query {q|path(?1,?2)?|q})
diff --git a/test/datalog/parser.ml b/test/datalog/parser.ml
index 7c100dc..f2897da 100644
--- a/test/datalog/parser.ml
+++ b/test/datalog/parser.ml
@@ -7,6 +7,7 @@
module Parser = Rdf_datalog.Parser
module Term = Rdf_datalog.Term
+module Atom = Rdf_datalog.Atom
module Literal = Rdf_datalog.Literal
module Clause = Rdf_datalog.Clause
@@ -37,18 +38,18 @@ let iri_test_case =
let variable_test_case =
test_case "variable" `Quick
- (fun () -> check (result string string)
+ (fun () -> check (result int string)
"can parse"
- (parse Parser.variable "?abc")
- (Result.ok @@ "abc"))
+ (parse Parser.variable "?42")
+ (Result.ok @@ 42))
let term_testable =
testable Term.pp Term.equal
let term_test_case =
let cases =
- ["?abc", Term.variable "abc";
- "?a", Term.variable "a";
+ ["?1", Term.variable 1;
+ "?2", Term.variable 2;
"<http://example.com>", "http://example.com" |> Rdf.Iri.of_string |> Rdf.Term.of_iri |> Term.constant;
"\"blups\"", Rdf.Literal.make "blups" (Rdf.Namespace.xsd "string") |> Rdf.Term.of_literal |> Term.constant;
] in
@@ -63,22 +64,22 @@ let term_test_case =
(Result.ok v))
cases)
-let literal_testable =
- testable Literal.pp Literal.equal
+let atom_testable =
+ testable Atom.pp Atom.equal
let literal_test_case =
let cases = [
- "p(?a,?b)", Literal.make "p" [Term.variable "a"; Term.variable "b"];
- "graph(?a,<http://example.com>)", Literal.make "graph" [Term.variable "a";
- "http://example.com" |> Rdf.Iri.of_string |> Rdf.Term.of_iri |> Term.constant]
+ "p(?1,?2)", Atom.make "p" [Term.variable 1; Term.variable 2];
+ "graph(?1,<http://example.com>)", Atom.make "graph" [Term.variable 1;
+ "http://example.com" |> Rdf.Iri.of_string |> Rdf.Term.of_iri |> Term.constant]
] in
test_case "literal" `Quick
(fun () ->
List.iter
(fun (enc, v) ->
- check (result literal_testable string)
+ check (result atom_testable string)
"can parse"
- (parse Parser.literal enc)
+ (parse Parser.atom enc)
(Result.ok v))
cases)
@@ -87,10 +88,10 @@ let clause_testable =
let clause_test_case =
let cases = [
- "p(?a, ?b).", Literal.make "p" [Term.variable "a"; Term.variable "b"] |> Clause.fact;
- "p(?a, ?b) :- q(?a,?b).", Clause.make
- (Literal.make "p" [Term.variable "a"; Term.variable "b"])
- [(Literal.make "q" [Term.variable "a"; Term.variable "b"])] ;
+ "p(?1, ?2).", Atom.make "p" [Term.variable 1; Term.variable 2] |> Clause.make_fact;
+ "p(?1, ?2) :- q(?1,?2).", Clause.make
+ (Atom.make "p" [Term.variable 1; Term.variable 2])
+ [Literal.make_positive (Atom.make "q" [Term.variable 1; Term.variable 2])] ;
] in
test_case "clause" `Quick
(fun () ->