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 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