upl 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/History.txt +4 -0
- data/README.md +14 -20
- data/lib/upl.rb +3 -0
- data/lib/upl/dict.rb +1 -1
- data/lib/upl/extern.rb +42 -31
- data/lib/upl/inter.rb +10 -1
- data/lib/upl/query.rb +51 -0
- data/lib/upl/runtime.rb +14 -16
- data/lib/upl/term.rb +23 -7
- data/lib/upl/tree.rb +27 -3
- data/lib/upl/variable.rb +4 -2
- data/lib/upl/version.rb +1 -1
- metadata +3 -3
- data/scratch/register_predicate.rb +0 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fa378221e9a155214e277debd0451dc3ee7f0aa16c35f71ed838151824371c56
|
4
|
+
data.tar.gz: ecf227095d069fdde0639b5e685f7e000cd272358daea498910eca648cfb42b5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e7f9508863c480a8597cd6b35e343429ca1b9afcf4751116b4ea1694d6b260da05067128eefae21bc8873f816a8f85a4da9bef128ff873f25fe16f8ad1e4a3ab
|
7
|
+
data.tar.gz: 5e319c993b59af76d23f29a52f8cb70dcdc22b33f5110cc96e5608aed214ff0fdff61068e97a1e6d2e04190350861cd2bb701ebf95f1af93a8bbe105eae8ce24
|
data/History.txt
CHANGED
data/README.md
CHANGED
@@ -17,12 +17,12 @@ The query api always returns an ```Enumerable``` of all values which satisfy the
|
|
17
17
|
Query a built-in predicate, with a full expression:
|
18
18
|
|
19
19
|
``` ruby
|
20
|
-
[1] pry(main)>
|
20
|
+
[1] pry(main)> q = Upl::Query.new <<~prolog
|
21
21
|
member(K,[home,executable,shared_object_extension]),
|
22
22
|
current_prolog_flag(K,V)
|
23
23
|
prolog
|
24
|
-
=> #<
|
25
|
-
[2] pry(main)
|
24
|
+
=> #<Upl::Query...>
|
25
|
+
[2] pry(main) q.to_a
|
26
26
|
=> [{:K=>:executable, :V=>:upl},
|
27
27
|
{:K=>:home, :V=>:"/usr/lib64/swipl"},
|
28
28
|
{:K=>:shared_object_extension, :V=>:so}]
|
@@ -79,12 +79,8 @@ Alician proportions. So, here we GOOOoooo...
|
|
79
79
|
=> person/3(john,anderson,#<Object:0x0000563346a08e38 @_upl_atom=439429>)
|
80
80
|
[2] pry(main)> Upl.assertz fact
|
81
81
|
=> true
|
82
|
-
[3] pry(main)>
|
83
|
-
=>
|
84
|
-
:B=>anderson,
|
85
|
-
:C=>#<Object:0x0000563346a08e38 @_upl_atom=439429>}]
|
86
|
-
[4] pry(main)> ha[:C].equal? o
|
87
|
-
=> true
|
82
|
+
[3] pry(main)> Upl::Query.new('person(A,B,C)').first[:C]
|
83
|
+
=> #<Object:0x0000563346a08e38 @_upl_atom=439429>}]
|
88
84
|
```
|
89
85
|
|
90
86
|
Woo. An object disappears into prolog, and comes back out again. Having gained
|
@@ -100,20 +96,20 @@ fact2 = Upl::Term :person, :thomas, :paine, (thing2 = Object.new)
|
|
100
96
|
Upl.assertz fact2
|
101
97
|
|
102
98
|
# Note that both facts are in the result and the values for C are different
|
103
|
-
|
104
|
-
|
99
|
+
q = Upl::Query.new 'person(A,B,C)'
|
100
|
+
q.to_a
|
105
101
|
=>[
|
106
102
|
{:A=>james, :B=>madison, :C=>#<Object:0x0000563f56e35580 @_upl_atom=439429>},
|
107
103
|
{:A=>thomas, :B=>paine, :C=>#<Object:0x0000563f56d2b5b8 @_upl_atom=439813>}
|
108
104
|
]
|
109
105
|
|
110
106
|
# Unify C with thing2
|
111
|
-
|
112
|
-
|
107
|
+
q = Upl::Query.new 'person(A,B,C)'
|
108
|
+
q.C = thing2
|
113
109
|
|
114
110
|
# ... and we get the correct result
|
115
111
|
# Note that the first fact is not in the result.
|
116
|
-
|
112
|
+
q.first
|
117
113
|
=> [{:A=>thomas, :B=>paine, :C=>#<Object:0x0000563f56d2b5b8 @_upl_atom=439813>}]
|
118
114
|
```
|
119
115
|
|
@@ -125,9 +121,9 @@ def (obj = Object.new).voicemail
|
|
125
121
|
"Hey. For your logs."
|
126
122
|
end
|
127
123
|
|
128
|
-
|
129
|
-
|
130
|
-
|
124
|
+
q = Upl::Query.new "mcall(O,voicemail,St),string_codes(St,Co)"
|
125
|
+
q.O = obj
|
126
|
+
q.to_a
|
131
127
|
=> [{:O=>#<Object:0x00005610453b0528 @_upl_atom=495109>,
|
132
128
|
:St=>"Hey. For your logs.",
|
133
129
|
:Co=>[72, 101, 121, 46, 32, 70, 111, 114, 32, 121, 111, 117, 114, 32, 108, 111, 103, 115, 46]}]
|
@@ -142,7 +138,7 @@ Upl::Foreign.register_semidet :special_concat do |arg0, arg1|
|
|
142
138
|
arg1 === "#{arg0}-special"
|
143
139
|
end
|
144
140
|
|
145
|
-
|
141
|
+
Upl::Query.new('special_concat(hello, A)').to_a
|
146
142
|
=> [{:A=>"hello-special"}]
|
147
143
|
```
|
148
144
|
|
@@ -169,8 +165,6 @@ You cannot talk to swipl from a Thread other than ```Thread::main```. See https:
|
|
169
165
|
|
170
166
|
I've used it to drive an address DCG on 50,000 addresses. Memory usage was stable.
|
171
167
|
|
172
|
-
You cannot talk to swipl from a Thread other than ```Thread::main```
|
173
|
-
|
174
168
|
ruby has a GC. swipl has a GC. At some point they will disagree. I haven't reached that point yet.
|
175
169
|
|
176
170
|
UTF8-passthrough is not implemented, but there's a good chance you'll get what you want with the help of ```String#force_encoding('UTF-8')```.
|
data/lib/upl.rb
CHANGED
@@ -12,8 +12,11 @@ require_relative 'upl/tree'
|
|
12
12
|
require_relative 'upl/inter'
|
13
13
|
require_relative 'upl/term_vector'
|
14
14
|
require_relative 'upl/foreign'
|
15
|
+
require_relative 'upl/query'
|
15
16
|
|
16
17
|
module Upl
|
18
|
+
# todo need .call and .consult at this level?
|
19
|
+
|
17
20
|
# an enumerator yielding hashes keyed by the variables, mapping to the term
|
18
21
|
module_function def query string_or_term, vars = nil, &blk
|
19
22
|
if string_or_term.is_a?(Term) && vars
|
data/lib/upl/dict.rb
CHANGED
@@ -14,7 +14,7 @@ module Upl
|
|
14
14
|
# eg, need to check that args.size == predicate.arity
|
15
15
|
# otherwise segfaults and other weird stuff ensue
|
16
16
|
rv = Extern::PL_call_predicate \
|
17
|
-
|
17
|
+
Fiddle::NULL, # module
|
18
18
|
0, # flags, see PL_open_query
|
19
19
|
(Runtime.predicate 'is_dict', args.size),
|
20
20
|
args.terms
|
data/lib/upl/extern.rb
CHANGED
@@ -7,36 +7,41 @@ module Upl
|
|
7
7
|
module Extern
|
8
8
|
extend Fiddle::Importer
|
9
9
|
|
10
|
-
#
|
11
|
-
def self.
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
line =~ /^([^=]+)="([^"]*)";$/
|
18
|
-
ha[$1] = $2.strip
|
19
|
-
end
|
20
|
-
rescue Errno::ENOENT => ex
|
21
|
-
puts "#{swipl_exe} not found on path #{ENV['PATH']}"
|
22
|
-
exit 1
|
10
|
+
# fetch config values from swipl executable
|
11
|
+
def self.swipl_config_values
|
12
|
+
swipl_exe = 'swipl'
|
13
|
+
values = `#{swipl_exe} --dump-runtime-variables=sh`.each_line.with_object Hash.new do |line,ha|
|
14
|
+
# split by = and for rhs strip surrounding quotes and trailing ;
|
15
|
+
line =~ /^([^=]+)="([^"]*)";\s*$/
|
16
|
+
ha[$1] = $2.strip
|
23
17
|
end
|
18
|
+
rescue Errno::ENOENT => ex
|
19
|
+
puts "#{swipl_exe} not found on path #{ENV['PATH']}"
|
20
|
+
exit 1
|
21
|
+
end
|
24
22
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
23
|
+
# use swipl config to find the .so file
|
24
|
+
# should result in something like
|
25
|
+
# /usr/lib64/swipl-7.7.18/lib/x86_64-linux/libswipl.so
|
26
|
+
# which should actually exist
|
27
|
+
def self.so_path
|
28
|
+
values = swipl_config_values
|
29
|
+
p = Pathname "#{values['PLBASE']}/lib/#{values['PLARCH']}/#{values['PLLIB'].gsub('-l', 'lib')}.#{values['PLSOEXT']}"
|
30
|
+
p.realpath.to_s
|
31
|
+
rescue Errno::ENOENT => ex
|
32
|
+
puts "problem with library #{p.to_s}: #{ex.message}"
|
33
|
+
exit 1
|
35
34
|
end
|
36
35
|
|
37
36
|
dlload so_path
|
38
37
|
|
39
|
-
|
38
|
+
def self.ruby_free_fn
|
39
|
+
@ruby_free_fn ||= Fiddle::Function.new Fiddle::RUBY_FREE, [Fiddle::TYPE_VOIDP], Fiddle::TYPE_VOID
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.swipl_free_fn
|
43
|
+
@swipl_free_fn ||= Fiddle::Function.new self['PL_free'], [Fiddle::TYPE_VOIDP], Fiddle::TYPE_VOID
|
44
|
+
end
|
40
45
|
|
41
46
|
typealias 'term_t', 'void *'
|
42
47
|
typealias 'module_t', 'void *'
|
@@ -145,9 +150,16 @@ module Upl
|
|
145
150
|
|
146
151
|
extern 'int PL_register_foreign_in_module(char *mod, char *name, int arity, foreign_t (*f)(), int flags, ...)'
|
147
152
|
|
153
|
+
# /usr/lib64/swipl/include/SWI-Prolog.h
|
148
154
|
module Convert
|
149
|
-
|
150
|
-
|
155
|
+
BUF_DISCARDABLE = 0x0000
|
156
|
+
BUF_RING = 0x0100
|
157
|
+
BUF_MALLOC = 0x0200
|
158
|
+
BUF_ALLOW_STACK = 0x0400
|
159
|
+
|
160
|
+
REP_ISO_LATIN_1 = 0x0000
|
161
|
+
REP_UTF8 = 0x1000
|
162
|
+
REP_MB = 0x2000
|
151
163
|
|
152
164
|
CVT_ATOM = 0x0001
|
153
165
|
CVT_STRING = 0x0002
|
@@ -220,8 +232,9 @@ module Upl
|
|
220
232
|
extern 'int PL_is_number(term_t t)'
|
221
233
|
extern 'int PL_is_acyclic(term_t t)'
|
222
234
|
|
223
|
-
extern 'int PL_put_atom(term_t t, atom_t a)'
|
224
235
|
extern 'int PL_put_variable(term_t t)'
|
236
|
+
extern 'int PL_put_atom(term_t t, atom_t a)'
|
237
|
+
extern 'int PL_put_chars(term_t t, int flags, size_t len, const char *chars)'
|
225
238
|
extern 'int PL_put_functor(term_t t, functor_t functor)'
|
226
239
|
extern 'int PL_put_term(term_t t1, term_t t2)' # Make t1 point to the same term as t2.
|
227
240
|
extern 'int PL_put_integer(term_t t, long i)'
|
@@ -234,8 +247,7 @@ module Upl
|
|
234
247
|
extern 'int PL_unify_arg(int index, term_t t, term_t a)' # set index-th arg of t to a
|
235
248
|
|
236
249
|
extern 'int PL_get_atom_chars(term_t t, char **a)'
|
237
|
-
|
238
|
-
extern 'int PL_get_string(term_t t, char **s, size_t *len)'
|
250
|
+
extern 'int PL_get_nchars(term_t t, size_t *len, char **s, unsigned int flags)'
|
239
251
|
extern 'int PL_get_integer(term_t t, int *i)'
|
240
252
|
extern 'int PL_get_int64(term_t t, int64_t *i)'
|
241
253
|
extern 'int PL_get_float(term_t t, double *f)'
|
@@ -271,8 +283,7 @@ module Upl
|
|
271
283
|
# PL_EXPORT(int) PL_put_term_from_chars(term_t t, int flags, size_t len, const char *s);
|
272
284
|
# extern 'int PL_put_term_from_chars(term_t t, int flags, size_t len, const char *s)'
|
273
285
|
|
274
|
-
extern 'int
|
275
|
-
extern 'int PL_wchars_to_term(const pl_wchar_t *chars, term_t term)'
|
286
|
+
extern 'int PL_put_term_from_chars(term_t t, int flags, size_t len, const char *s)'
|
276
287
|
|
277
288
|
extern 'void PL_unregister_atom(atom_t a)'
|
278
289
|
|
data/lib/upl/inter.rb
CHANGED
@@ -138,6 +138,7 @@ protected
|
|
138
138
|
end
|
139
139
|
|
140
140
|
class Symbol
|
141
|
+
# TODO does this also need a to_term_t for atoms?
|
141
142
|
def to_atom
|
142
143
|
Upl::Extern.PL_new_atom to_s
|
143
144
|
end
|
@@ -153,8 +154,16 @@ end
|
|
153
154
|
|
154
155
|
class String
|
155
156
|
def to_term_t
|
156
|
-
rv = Upl::Extern.
|
157
|
+
rv = Upl::Extern.PL_put_chars \
|
158
|
+
(term_t = Upl::Extern.PL_new_term_ref),
|
159
|
+
(Upl::Extern::PL_STRING | Upl::Extern::Convert::REP_UTF8),
|
160
|
+
# swipl PL_chars_t struct uses number of octets | or number of wide chars.
|
161
|
+
# So use bytesize of string here.
|
162
|
+
bytesize,
|
163
|
+
Fiddle::Pointer[self]
|
164
|
+
|
157
165
|
rv == 1 or raise "can't convert #{self} to term"
|
166
|
+
|
158
167
|
term_t
|
159
168
|
end
|
160
169
|
end
|
data/lib/upl/query.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
module Upl
|
2
|
+
# Mostly sugar
|
3
|
+
# TODO needs a row_proc like Sequel uses to generate the yielded values
|
4
|
+
# TODO maybe cache @count (after first enumeration)
|
5
|
+
class Query
|
6
|
+
# TODO can only be string at this point
|
7
|
+
# One-use only. If you want a new query, create another instance.
|
8
|
+
# Is an enumerable for the result set.
|
9
|
+
def initialize term_or_string
|
10
|
+
@source = term_or_string
|
11
|
+
@term, @vars = Upl::Runtime.term_vars term_or_string
|
12
|
+
end
|
13
|
+
|
14
|
+
attr_reader :source, :term, :vars
|
15
|
+
|
16
|
+
def names; @vars.keys end
|
17
|
+
|
18
|
+
def method_missing meth, *args, &blk
|
19
|
+
if meth.end_with? '='
|
20
|
+
assign = true
|
21
|
+
name = meth[..-2].to_sym
|
22
|
+
else
|
23
|
+
name = meth
|
24
|
+
end
|
25
|
+
|
26
|
+
return super unless @vars.include? name
|
27
|
+
|
28
|
+
if assign
|
29
|
+
@vars[name] === args.first or raise "Unification failure"
|
30
|
+
else
|
31
|
+
@vars[name]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def [] name; @vars[name] end
|
36
|
+
|
37
|
+
def []= name, value
|
38
|
+
@vars[name] === value or raise "Unification failure"
|
39
|
+
end
|
40
|
+
|
41
|
+
def call
|
42
|
+
@results ||= Upl::Runtime.term_vars_query @term, @vars
|
43
|
+
end
|
44
|
+
|
45
|
+
def each &blk
|
46
|
+
call.each &blk
|
47
|
+
end
|
48
|
+
|
49
|
+
include Enumerable
|
50
|
+
end
|
51
|
+
end
|
data/lib/upl/runtime.rb
CHANGED
@@ -2,6 +2,7 @@ require 'fiddle'
|
|
2
2
|
|
3
3
|
require_relative 'extern'
|
4
4
|
|
5
|
+
# TODO move this to inter, or maybe a refinement
|
5
6
|
class Fiddle::Pointer
|
6
7
|
def term_type
|
7
8
|
::Upl::Extern.PL_term_type self
|
@@ -15,6 +16,7 @@ end
|
|
15
16
|
|
16
17
|
module Upl
|
17
18
|
module Runtime
|
19
|
+
# shortcuttery
|
18
20
|
Ptr = Fiddle::Pointer
|
19
21
|
|
20
22
|
class PrologException < RuntimeError
|
@@ -43,18 +45,10 @@ module Upl
|
|
43
45
|
raise "dunno bout #{st_or_term}"
|
44
46
|
end
|
45
47
|
|
46
|
-
rv = Extern.PL_call term.term_t,
|
48
|
+
rv = Extern.PL_call term.term_t, Fiddle::NULL
|
47
49
|
rv == 1 # don't raise
|
48
50
|
end
|
49
51
|
|
50
|
-
def self.ruby_free_fn
|
51
|
-
@ruby_free_fn ||= Fiddle::Function.new Fiddle::RUBY_FREE, [Fiddle::TYPE_VOIDP], Fiddle::TYPE_VOID
|
52
|
-
end
|
53
|
-
|
54
|
-
def self.swipl_free_fn
|
55
|
-
@swipl_free_fn ||= Fiddle::Function.new Extern['PL_free'], [Fiddle::TYPE_VOIDP], Fiddle::TYPE_VOID
|
56
|
-
end
|
57
|
-
|
58
52
|
def self.init
|
59
53
|
# set up no output so we don't get swipl command line interfering in ruby
|
60
54
|
# TODO exception handling should not kick off a prolog terminal
|
@@ -62,8 +56,9 @@ module Upl
|
|
62
56
|
args = %w[upl --tty=false --signals=false --debug=false --quiet=true]
|
63
57
|
|
64
58
|
# convert args to char **
|
59
|
+
# TODO Fiddle::SIZEOF_VOIDP would be faster
|
65
60
|
ptr_size = Extern.sizeof 'char*'
|
66
|
-
arg_ptrs = Ptr.malloc ptr_size * args.size, ruby_free_fn
|
61
|
+
arg_ptrs = Ptr.malloc ptr_size * args.size, Extern::ruby_free_fn
|
67
62
|
args.each_with_index do |rg,i|
|
68
63
|
(arg_ptrs + i*ptr_size)[0,ptr_size] = Ptr[rg].ref
|
69
64
|
end
|
@@ -77,10 +72,13 @@ module Upl
|
|
77
72
|
end
|
78
73
|
|
79
74
|
# once_only. Should probably be a singleton or something.
|
75
|
+
# TODO Nope, wrong. init is for the entire engine.
|
76
|
+
# PL_thread_attach_engine is for one-to-one threads,
|
77
|
+
# PL_create_engine is for engine pool
|
80
78
|
Thread::current[:upl] ||= init
|
81
79
|
|
82
|
-
def self.predicate name, arity, module_name =
|
83
|
-
Extern.PL_predicate
|
80
|
+
def self.predicate name, arity, module_name = 0
|
81
|
+
Extern.PL_predicate Ptr[name.to_s], arity, Fiddle::Pointer[module_name]
|
84
82
|
end
|
85
83
|
|
86
84
|
def self.unify( term_a, term_b )
|
@@ -103,7 +101,7 @@ module Upl
|
|
103
101
|
# remember Atom can also be a string for swipl
|
104
102
|
def self.term_vars st
|
105
103
|
rv = Extern::PL_call_predicate \
|
106
|
-
|
104
|
+
Fiddle::NULL, # module
|
107
105
|
0, # flags, see PL_open_query
|
108
106
|
(predicate 'atom_to_term', 3),
|
109
107
|
# 3 variables, first one determined
|
@@ -124,7 +122,7 @@ module Upl
|
|
124
122
|
def self.open_query qterm, args, mod: nil, flags: nil, &blk
|
125
123
|
# This will need a string for the module, eventually
|
126
124
|
# module is NULL, flags is 0
|
127
|
-
mod ||=
|
125
|
+
mod ||= Fiddle::NULL
|
128
126
|
flags ||= flags=Extern::Flags::PL_Q_EXT_STATUS | Extern::Flags::PL_Q_CATCH_EXCEPTION
|
129
127
|
|
130
128
|
query_id_p = Extern.PL_open_query mod, flags, qterm.to_predicate, args.terms
|
@@ -186,7 +184,7 @@ module Upl
|
|
186
184
|
end
|
187
185
|
|
188
186
|
def self.predicate name, arity
|
189
|
-
pred_p = Extern.PL_predicate Ptr[name.to_s], arity,
|
187
|
+
pred_p = Extern.PL_predicate Ptr[name.to_s], arity, Fiddle::NULL
|
190
188
|
end
|
191
189
|
|
192
190
|
# Simple query with predicate / arity
|
@@ -201,7 +199,7 @@ module Upl
|
|
201
199
|
qterm = Object.new
|
202
200
|
qterm.define_singleton_method :to_predicate do
|
203
201
|
p_functor = Extern::PL_new_functor predicate_str.to_sym.to_atom, arity
|
204
|
-
Extern::PL_pred p_functor,
|
202
|
+
Extern::PL_pred p_functor, Fiddle::NULL
|
205
203
|
end
|
206
204
|
|
207
205
|
args = TermVector.new arity
|
data/lib/upl/term.rb
CHANGED
@@ -5,9 +5,20 @@ module Upl
|
|
5
5
|
def initialize term_or_string
|
6
6
|
case term_or_string
|
7
7
|
when String
|
8
|
-
|
9
|
-
rv = Extern.
|
10
|
-
|
8
|
+
# sadly, this doesn't keep variable names
|
9
|
+
rv = Extern.PL_put_term_from_chars \
|
10
|
+
(@term_t = Extern.PL_new_term_ref),
|
11
|
+
Extern::Convert::REP_UTF8,
|
12
|
+
term_or_string.bytesize,
|
13
|
+
Fiddle::Pointer[term_or_string]
|
14
|
+
|
15
|
+
case rv
|
16
|
+
when 1; true # all ok
|
17
|
+
when 0
|
18
|
+
raise "failure parsing term #{term_or_string}, #{Tree.of_term(@term_t).inspect}"
|
19
|
+
else
|
20
|
+
raise "unknown api return value #{rv}"
|
21
|
+
end
|
11
22
|
|
12
23
|
when Fiddle::Pointer
|
13
24
|
# assume this is a pointer to a term. Unsafe, but there's no choice really
|
@@ -45,10 +56,11 @@ module Upl
|
|
45
56
|
end
|
46
57
|
|
47
58
|
def populate
|
48
|
-
|
49
|
-
|
59
|
+
rv = Extern::PL_get_name_arity \
|
60
|
+
term_t,
|
61
|
+
(atom_ptr = Fiddle::Pointer[0].ref),
|
62
|
+
(int_ptr = Fiddle::Pointer[0].ref)
|
50
63
|
|
51
|
-
rv = Extern::PL_get_name_arity term_t, atom_ptr, int_ptr
|
52
64
|
# This happens when the term_t is not a PL_TERM (ie a compound)
|
53
65
|
rv == 1 or raise "can't populate term"
|
54
66
|
|
@@ -85,7 +97,7 @@ module Upl
|
|
85
97
|
end
|
86
98
|
|
87
99
|
def to_predicate
|
88
|
-
Extern::PL_pred to_functor,
|
100
|
+
Extern::PL_pred to_functor, Fiddle::NULL
|
89
101
|
end
|
90
102
|
|
91
103
|
def tree; @tree || (Tree.of_term term_t) end
|
@@ -106,6 +118,10 @@ module Upl
|
|
106
118
|
def first; self[0] end
|
107
119
|
def last; self[arity-1] end
|
108
120
|
|
121
|
+
def deconstruct
|
122
|
+
map{|t| Term.new t}
|
123
|
+
end
|
124
|
+
|
109
125
|
def [](idx)
|
110
126
|
# remember args for terms are 1-based
|
111
127
|
rv = Extern::PL_get_arg idx+1, term_t, (arg = Extern.PL_new_term_ref)
|
data/lib/upl/tree.rb
CHANGED
@@ -5,6 +5,7 @@ module Upl
|
|
5
5
|
# queries give back their results as terms which are invalidated as soon as
|
6
6
|
# the next set of results is calculated. So we need to turn those terms into a
|
7
7
|
# ruby representation and keep them around.
|
8
|
+
# TODO rename to Ast
|
8
9
|
class Tree
|
9
10
|
# term is either a Term instance, or a Fiddle::Pointer to a term_t
|
10
11
|
def initialize( term )
|
@@ -53,9 +54,20 @@ module Upl
|
|
53
54
|
bytes.unpack('D').first
|
54
55
|
|
55
56
|
when Extern::PL_STRING
|
56
|
-
|
57
|
-
|
58
|
-
|
57
|
+
# TODO could possibly get away with BUF_DISCARDABLE here?
|
58
|
+
|
59
|
+
flag_mod = Upl::Extern::Convert
|
60
|
+
rv = Extern.PL_get_nchars \
|
61
|
+
term_t,
|
62
|
+
(len_ptr = Fiddle::Pointer.malloc(Fiddle::SIZEOF_VOIDP, Extern::ruby_free_fn)),
|
63
|
+
(str_ptr = Fiddle::Pointer.malloc(Fiddle::SIZEOF_VOIDP, Extern::ruby_free_fn)),
|
64
|
+
flag_mod::CVT_STRING | flag_mod::BUF_MALLOC | flag_mod::REP_UTF8
|
65
|
+
|
66
|
+
# note that length here is number of octets, not number of utf8 chars
|
67
|
+
string = String.new (str_ptr.ptr.to_s len_ptr.ptr.to_i), encoding: Encoding::UTF_8
|
68
|
+
# eventually free the malloc'd memory for the string
|
69
|
+
str_ptr.ptr.free = Extern::swipl_free_fn
|
70
|
+
string
|
59
71
|
|
60
72
|
when Extern::PL_NIL
|
61
73
|
# TODO maybe this should be [] - see what happens when term_vars has no vars
|
@@ -79,6 +91,18 @@ module Upl
|
|
79
91
|
|
80
92
|
def arity; args.size end
|
81
93
|
|
94
|
+
# for 2.7 case ... in pattern matching
|
95
|
+
# for case term; in functor,arg1,arg; arg1
|
96
|
+
def deconstruct
|
97
|
+
[atom, *args]
|
98
|
+
end
|
99
|
+
|
100
|
+
def inspect
|
101
|
+
pp = PP.new
|
102
|
+
pretty_print pp
|
103
|
+
pp.output
|
104
|
+
end
|
105
|
+
|
82
106
|
def pretty_print(pp)
|
83
107
|
unless atom.to_sym == :','
|
84
108
|
pp.text atom.to_s
|
data/lib/upl/variable.rb
CHANGED
@@ -31,6 +31,7 @@ module Upl
|
|
31
31
|
|
32
32
|
def === value; unify value end
|
33
33
|
|
34
|
+
# TODO remove
|
34
35
|
# bit of a hack to create empty variables for a functor
|
35
36
|
def self.to_term
|
36
37
|
Extern.PL_new_term_ref
|
@@ -47,13 +48,14 @@ module Upl
|
|
47
48
|
@_string ||= begin
|
48
49
|
rv = Extern::PL_get_chars \
|
49
50
|
term_t,
|
50
|
-
(str_ref =
|
51
|
+
(str_ref = Fiddle::Pointer[0].ref),
|
51
52
|
# need cvt_variable for normal variables, and cvt_write for clpfd variables
|
52
53
|
Extern::Convert::CVT_VARIABLE | Extern::Convert::CVT_WRITE | Extern::Convert::REP_UTF8 | Extern::Convert::BUF_MALLOC
|
53
54
|
# | Extern::CVT_ALL
|
54
55
|
|
55
|
-
str_ref.ptr.free =
|
56
|
+
str_ref.ptr.free = Extern::swipl_free_fn
|
56
57
|
# TODO might need to force utf8 encoding here?
|
58
|
+
# Just use CVT_UTF8
|
57
59
|
str_ref.ptr.to_s
|
58
60
|
end
|
59
61
|
end
|
data/lib/upl/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: upl
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- John Anderson
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-02-
|
11
|
+
date: 2020-02-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -104,6 +104,7 @@ files:
|
|
104
104
|
- lib/upl/foreign.rb
|
105
105
|
- lib/upl/functor.rb
|
106
106
|
- lib/upl/inter.rb
|
107
|
+
- lib/upl/query.rb
|
107
108
|
- lib/upl/runtime.rb
|
108
109
|
- lib/upl/term.rb
|
109
110
|
- lib/upl/term_vector.rb
|
@@ -111,7 +112,6 @@ files:
|
|
111
112
|
- lib/upl/variable.rb
|
112
113
|
- lib/upl/variables.rb
|
113
114
|
- lib/upl/version.rb
|
114
|
-
- scratch/register_predicate.rb
|
115
115
|
- upl.gemspec
|
116
116
|
homepage: https://github.com/djellemah/upl
|
117
117
|
licenses:
|
@@ -1,9 +0,0 @@
|
|
1
|
-
def doit
|
2
|
-
fact = Upl::Term :person, :john, :anderson, Object.new
|
3
|
-
Upl.assertz fact
|
4
|
-
vs = Array Upl.query 'person(A,B,C)'
|
5
|
-
p vs
|
6
|
-
1000.times{Array Upl.query 'current_prolog_flag(K,V)'}
|
7
|
-
Upl.retract fact
|
8
|
-
Upl::Runtime.call Upl::Term :garbage_collect_atoms
|
9
|
-
end
|