upl 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|