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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bcfc0c63c88755686e526182cb7ec46e5d6c7781adfce9671e8f24bb254d023e
4
- data.tar.gz: a366bb2532d7f3734b2d0b47854bf585c513509acdb0cc07882b313194efc476
3
+ metadata.gz: fa378221e9a155214e277debd0451dc3ee7f0aa16c35f71ed838151824371c56
4
+ data.tar.gz: ecf227095d069fdde0639b5e685f7e000cd272358daea498910eca648cfb42b5
5
5
  SHA512:
6
- metadata.gz: ce81943acf58d09751d44c643d33e1efa3089e4921886ddfde30e5148e607b0cbde69bafc132c6d613b11b1066667535dcbc4c73842535674a2945dbc17963ae
7
- data.tar.gz: 51a14597a059f3d24fd30a88eb7406386074768afbf67b0fc9f7533059477018181583a6b2d56c205acf3f82ef9e6b4963ba423d93bb7343f128982ff3daaf13
6
+ metadata.gz: e7f9508863c480a8597cd6b35e343429ca1b9afcf4751116b4ea1694d6b260da05067128eefae21bc8873f816a8f85a4da9bef128ff873f25fe16f8ad1e4a3ab
7
+ data.tar.gz: 5e319c993b59af76d23f29a52f8cb70dcdc22b33f5110cc96e5608aed214ff0fdff61068e97a1e6d2e04190350861cd2bb701ebf95f1af93a8bbe105eae8ce24
data/History.txt CHANGED
@@ -1,3 +1,7 @@
1
+ == 0.2.0
2
+ * add Upl::Query class for nicer api
3
+ * faster term parsing
4
+
1
5
  == 0.1.0
2
6
  * register ruby predicates with exception handling
3
7
  * Some DSL improvements
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)> enum = Upl.query <<~prolog
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
- => #<Enumerator: ...>
25
- [2] pry(main) enum.to_a
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)> ha, = Array Upl.query 'person(A,B,C)'
83
- => [{:A=>john,
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
- query_term, query_vars = Upl::Runtime.term_vars 'person(A,B,C)'
104
- Array Upl::Runtime.term_vars_query query_term, query_vars
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
- query_term, query_vars = Upl::Runtime.term_vars 'person(A,B,C)'
112
- query_vars.C = thing2
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
- Array Upl::Runtime.term_vars_query query_term, query_vars
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
- query_term, query_vars = Upl::Runtime.term_vars "mcall(O,voicemail,St),string_codes(St,Co)"
129
- query_vars.O = obj
130
- Array Upl::Runtime.term_vars_query query_term, query_vars
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
- Array Upl.query 'special_concat(hello, A)'
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
- Extern::NULL, # module
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
- # use swipl config to find the .so file
11
- def self.so_path
12
- begin
13
- swipl_exe = 'swipl'
14
- values = `#{swipl_exe} --dump-runtime-variables=sh`.each_line.with_object Hash.new do |line,ha|
15
- line.chomp!
16
- # split by = and for rhs strip surrounding quotes and trailing ;
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
- begin
26
- # should result in something like
27
- # /usr/lib64/swipl-7.7.18/lib/x86_64-linux/libswipl.so
28
- # which should actually exist
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
34
- end
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
- NULL = Fiddle::Pointer.new 0
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
- REP_UTF8 = 0x1000
150
- BUF_MALLOC = 0x0200
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
- # TODO deprecated, use something else
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 PL_chars_to_term(const char *chars, term_t term)'
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.PL_put_string_nchars (term_t = Upl::Extern.PL_new_term_ref), length, Fiddle::Pointer[self]
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, Extern::NULL
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 = nil
83
- Extern.PL_predicate Fiddle::Pointer[name.to_s], arity, NULL
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
- Extern::NULL, # module
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 ||= Extern::NULL
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, Extern::NULL
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, Extern::NULL
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
- @term_t = Extern.PL_new_term_ref
9
- rv = Extern.PL_chars_to_term Fiddle::Pointer[term_or_string], @term_t
10
- rv == 1 or raise "failure parsing term #{term_or_string}"
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
- int_ptr = Runtime::Ptr[0].ref
49
- atom_ptr = Runtime::Ptr[0].ref
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, Extern::NULL
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
- rv = Extern.PL_get_string term_t, (str_ptr = Fiddle::Pointer[0].ref), (len_ptr = Fiddle::Pointer[0].ref)
57
- value_ptr = Fiddle::Pointer.new str_ptr.ptr, len_ptr.ptr.to_i
58
- value_ptr.to_s[0,len_ptr.ptr.to_i]
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 = Runtime::Ptr[0].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 = Runtime.swipl_free_fn
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
@@ -1,3 +1,3 @@
1
1
  module Upl
2
- VERSION = '0.1.0'
2
+ VERSION = '0.2.0'
3
3
  end
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.1.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 00:00:00.000000000 Z
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