Source file swipl.ml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
module Raw = Raw
let module_sep = lazy (Raw.Functor.functor_ (Raw.Atom.atom ":") 2)
let conjunction = lazy (Raw.Functor.functor_ (Raw.Atom.atom ",") 2)
let disjunction = lazy (Raw.Functor.functor_ (Raw.Atom.atom ";") 2)
let apply = lazy (Raw.Functor.functor_ (Raw.Atom.atom "apply") 2)
let call1 = lazy (Raw.Functor.functor_ (Raw.Atom.atom "call") 1)
type ty =
[ `Atom | `Blob | `Bool | `Dict | `Float | `Integer
| `ListPair | `Nil | `Rational | `String | `Term | `Variable ]
type ctx = Raw.ForeignFrame.t option
type query = Raw.Query.qid
type module_ = Raw.Module.t [@@deriving eq, ord]
let module_ str = Raw.Module.new_module (Raw.Atom.atom str)
type fn = Raw.Functor.t [@@deriving eq, ord]
type atom = Raw.Atom.t [@@deriving eq, ord]
type t =
| Term of Raw.Term.t
| Atom of Raw.Atom.t
| App of Raw.Module.t option * Raw.Functor.t * t list
| Conjunction of t * t
| Disjunction of t * t
[@@deriving eq, ord]
let create_term (_ctx: ctx) =
Raw.Term.new_ref ()
let create_terms (_ctx: ctx) n =
Raw.Term.new_refs n
let of_term term = Term term
let encode_string ctx str =
let t = create_term ctx in
assert(Raw.Term.put_string_chars t str);
t
let encode_list ctx ls =
let rec loop result = function
| [] -> assert (Raw.Term.put_nil result); result
| h :: t ->
let result = loop result t in
assert (Raw.Term.cons_list result h result);
result in
loop (create_term ctx) ls
let array_of_list ctx args =
let length = List.length args in
if length = 0
then Raw.Term.Array.empty
else begin
let terms = create_terms ctx length in
List.iteri (fun ind term ->
assert (Raw.Term.put_term (Raw.Term.Array.get terms ind) term)) args;
terms
end
let atom ctx v =
let result = create_term ctx in
Raw.Term.put_atom result v;
result
let app2 ctx fn arg1 arg2 =
let result = create_term ctx in
assert (Raw.Term.cons_functor2 result fn arg1 arg2);
result
let app ctx fn args =
let result = create_term ctx in
let args = array_of_list ctx args in
assert (Raw.Term.cons_functor result fn args);
result
let rec to_term ctx = function
| Term t -> t
| App (None, fn, args) -> app ctx fn (List.map (to_term ctx) args)
| App (Some module_, fn, args) ->
let fn =
let lazy sep = module_sep in
let module_ = Raw.Module.module_name module_ in
let fn = Raw.Functor.name fn in
app2 ctx sep (atom ctx module_) (atom ctx fn) in
let args = List.map (to_term ctx) args in
let args = encode_list ctx args in
let lazy apply = apply in
app2 ctx apply fn args
| Conjunction (l,r) ->
let lazy conjunction = conjunction in
let l = to_term ctx l in
let r = to_term ctx r in
app2 ctx conjunction l r
| Disjunction (l,r) ->
let lazy disjunction = disjunction in
let l = to_term ctx l in
let r = to_term ctx r in
app2 ctx disjunction l r
| Atom a -> atom ctx a
let eval ctx =
let flags = Raw.Q.(normal + ext_status + pass_exception) in
function
| Term t ->
let lazy call = call1 in
let pred = Raw.Predicate.pred call in
Raw.Query.open_query ~flags pred (array_of_list ctx [t])
| App (module_, fn, args) ->
let pred = Raw.Predicate.pred ?module_ fn in
let args = array_of_list ctx (List.map (to_term ctx) args) in
Raw.Query.open_query ?module_ ~flags pred args
| Atom a ->
let fn = Raw.Functor.functor_ a 0 in
let pred = Raw.Predicate.pred fn in
Raw.Query.open_query ~flags pred Raw.Term.Array.empty
| Conjunction (l,r) ->
let lazy conjunction = conjunction in
let conjunction = Raw.Predicate.pred conjunction in
Raw.Query.open_query ~flags conjunction (array_of_list ctx [to_term ctx l; to_term ctx r])
| Disjunction (l,r) ->
let lazy disjunction = disjunction in
let disjunction = Raw.Predicate.pred disjunction in
Raw.Query.open_query ~flags disjunction (array_of_list ctx [to_term ctx l; to_term ctx r])
let initialise () = Raw.initialise ()
let with_ctx f =
let frame = Raw.ForeignFrame.open_frame () in
let res = f (Some frame) in
Raw.ForeignFrame.close_frame frame;
res
let show t =
with_ctx (fun ctx -> to_term ctx t |> Raw.Term.get_chars) |> Option.value ~default:"None"
let pp fmt t = Format.pp_print_string fmt (show t)
let fresh ctx = of_term (create_term ctx)
module Syntax = struct
let (/@) fn x = Raw.Functor.functor_ (Raw.Atom.atom fn) x
let (!) x = Atom (Raw.Atom.atom x)
let app ?module_ fn args = App (module_, fn, args)
let (&&) l r = Conjunction (l,r)
let (||) l r = Disjunction (l,r)
end
let fold_solutions fn query =
let any_seen = ref false in
let rec loop () =
match Raw.Query.next_solution query with
| Raw.Query.Result.Bool true ->
any_seen := true;
begin match fn `Solution with
| Some `Close -> assert (Raw.Query.close_query query)
| Some `Cut -> assert (Raw.Query.cut_query query)
| None -> loop ()
end
| Raw.Query.Result.Bool false -> assert (Raw.Query.close_query query)
| Raw.Query.Result.Last ->
any_seen := true;
begin match fn `Last with
| Some `Close -> assert (Raw.Query.close_query query)
| Some `Cut -> assert (Raw.Query.cut_query query)
| None -> assert (Raw.Query.close_query query)
end
| Raw.Query.Result.Exception ->
match Raw.Exception.exn query with
| None -> assert (Raw.Query.close_query query)
| Some exn ->
ignore @@ begin
try
fn (`Exception (Term exn))
with
e -> (Raw.Exception.clear_exn (); raise e)
end;
Raw.Exception.clear_exn ();
assert (Raw.Query.close_query query) in
begin
try
loop ()
with
| e -> assert (Raw.Query.close_query query); raise e
end;
!any_seen
let iter_solutions ?(on_error=fun _ -> ()) query fn =
fold_solutions (function
| `Exception e -> on_error e; None
| _ -> fn (); None
) query
let first_solution query =
fold_solutions (function
| `Exception e -> failwith ("Prolog query raised exception: " ^ show e)
| `Last -> Some `Cut
| `Solution -> Some `Cut
) query
let last_solution query =
fold_solutions (function
| `Exception e -> failwith ("Prolog query raised exception" ^ show e)
| `Last -> Some `Cut
| `Solution -> None
) query
let call ctx t =
let query = eval ctx t in
ignore @@ first_solution query
let ctx term =
let term = to_term ctx term in
let rec loop term =
if Raw.Term.get_nil term
then []
else
let hd = create_term ctx in
let tl = create_term ctx in
assert (Raw.Term.get_list term hd tl);
(of_term hd) :: (loop tl) in
loop term
let atom t = Raw.Atom.atom t
let ctx term = Raw.Term.get_atom (to_term ctx term) |> Option.get
let show_atom atom = Raw.Atom.chars atom
let pp_atom fmt atom = Format.pp_print_string fmt (show_atom atom)
let ctx term = Raw.Term.get_bool (to_term ctx term) |> Option.get
let ctx term = Raw.Term.get_integer (to_term ctx term) |> Option.get
let ctx term = Raw.Term.get_float (to_term ctx term) |> Option.get
let ctx term = Raw.Term.get_string_chars (to_term ctx term) |> Option.get
let ctx term =
let (name, arity) = Raw.Term.get_name_arity (to_term ctx term) |> Option.get in
let rec loop acc ind =
if ind < arity
then
let arg = create_term ctx in
assert (Raw.Term.get_arg (ind + 1) (to_term ctx term) arg);
loop ((of_term arg) :: acc) (ind + 1)
else List.rev acc in
(name, loop [] 0)
let typeof = function
| Term t -> Raw.Term.term_type t
| Atom _ -> `Atom
| App (_, _, _)
| Conjunction (_, _)
| Disjunction (_, _) -> `Term
let encode_list ctx args = of_term (List.map (to_term ctx) args |> encode_list ctx)
let encode_string ctx str = of_term (encode_string ctx str)
let load_source txt =
let user = lazy Syntax.(! "user") in
let open_string = lazy Syntax.("open_string" /@ 2) in
let load_files = lazy Syntax.("load_files" /@ 2) in
let stream = lazy Syntax.("stream" /@ 1) in
let open_string txt out = Syntax.(app (Lazy.force open_string) [txt; out]) in
let load_files int opts = Syntax.(app (Lazy.force load_files) [int; opts]) in
let stream out = Syntax.(app (Lazy.force stream) [out]) in
let open_source ctx src =
let out = fresh ctx in
Syntax.(open_string (encode_string ctx src) out &&
load_files (Lazy.force user) (encode_list ctx [stream out])) in
with_ctx (fun ctx ->
call ctx (open_source ctx txt)
)