upl 0.0.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: d7e5de431546d46c5f4998818e1360e10e5dab6497e0988e783e626393b35cc7
4
+ data.tar.gz: 4cd776d265de52483fe89e239041ee001ba21856c339fb067b661437c6cbfd25
5
+ SHA512:
6
+ metadata.gz: a8ab63130414fe7d8ae8775c4a0f11199c76024952541b2820deb8e607361ca26cb07bcec64fc2a2df8e5790a5042caaa1598a0705d0271d6ac4dca4cd9497de
7
+ data.tar.gz: 58d09609f7888ee559bb626219f7be13576016aa40dff3992c12413a84d3113841e748f7896aa93d204dcceb2b5f0e40528e828bc30b930e7743ccb134efaf1f
data/.gitignore ADDED
@@ -0,0 +1,15 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
12
+
13
+ .rvmrc
14
+ Gemfile.lock
15
+ *.sublime*
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.3.6
5
+ before_install: gem install bundler -v 1.16.2
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/djellemah/upl" }
4
+
5
+ # Specify your gem's dependencies in upl.gemspec
6
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2018 John Anderson
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,174 @@
1
+ # Upl
2
+
3
+ A ruby wrapper for SWI-Prolog that goes both ways.
4
+
5
+ The main idea being that you want to just put in a chunk of prolog, and have the
6
+ wrapper give you back your answers in terms of the variable names you specified.
7
+ Just like what happens in your common-or-garden prolog REPL. But with pry's
8
+ syntax colouring and pretty-printing :-)
9
+
10
+ Also, do prolog-style queries on objects :-DD
11
+
12
+ ## Tutorial
13
+
14
+ ### Queries
15
+
16
+ To read rules from a prolog file:
17
+ ``` ruby
18
+ [1] pry(main)> Upl.consult '/home/yours/funky_data.pl'
19
+ => true
20
+ ```
21
+
22
+ Query a built-in predicate, with a full expression:
23
+ ``` ruby
24
+ [1] pry(main)> enum = Upl.query 'current_prolog_flag(K,V), member(K,[home,executable,shared_object_extension])'
25
+ => #<Enumerator: ...>
26
+ [12] pry(main) enum.to_a
27
+ => [{:K=>home, :V=>/usr/lib64/swipl-7.7.18},
28
+ {:K=>executable, :V=>/usr/local/rvm/rubies/ruby-2.6.0-preview2/bin/ruby},
29
+ {:K=>shared_object_extension, :V=>so}]
30
+ ```
31
+
32
+ ### Facts
33
+ Also we want to be able to construct prolog-queryable facts from ruby objects.
34
+ In prolog:
35
+
36
+ ``` prolog
37
+ ?- assert(person(john,anderson)).
38
+ true.
39
+
40
+ ?- person(A,B).
41
+ A = john,
42
+ B = anderson.
43
+
44
+ ?- retract(person(john,anderson)).
45
+ true.
46
+
47
+ ?- person(A,B).
48
+ false.
49
+ ```
50
+
51
+ And in Upl:
52
+
53
+ ``` ruby
54
+ [1] pry(Upl):1> fact = Term.functor :person, :john, :anderson
55
+ => person/2(john,anderson)
56
+ [2] pry(Upl):1> Runtime.eval Term.functor :assert, fact
57
+ => true
58
+ [3] pry(Upl):1> Array query 'person(A,B)'
59
+ => [{:A=>john, :B=>anderson}]
60
+ [4] pry(Upl):1> Runtime.eval Term.functor :retract, fact
61
+ => true
62
+ [5] pry(Upl):1> Array query 'person(A,B)'
63
+ => []
64
+ ```
65
+
66
+ ### Objective Facts
67
+
68
+ Also, with objects other than symbols. Obviously, this is a rabbit-hole of
69
+ Alician proportions. So, here we GOOOoooo...
70
+
71
+ ``` ruby
72
+ [1] pry(Upl):1> fact = Term.functor :person, :john, :anderson, (o = Object.new)
73
+ => person/3(john,anderson,#<Object:0x0000563346a08e38 @_upl_atom=439429>)
74
+ [2] pry(Upl):1> Runtime.eval Term.functor :assert, fact
75
+ => true
76
+ [3] pry(Upl):1> ha, = Array query 'person(A,B,C)'
77
+ => [{:A=>john,
78
+ :B=>anderson,
79
+ :C=>#<Object:0x0000563346a08e38 @_upl_atom=439429>}]
80
+ [4] pry(Upl):1> ha[:C].equal? o
81
+ => true
82
+ ```
83
+
84
+ Woo. An object disappears into prolog, and comes back out again. Having gained
85
+ much wisdom. Hurhur. And at least one extra instance variable.
86
+
87
+ And now, the pièce de résistance - using an object as an input term:
88
+
89
+ ``` ruby
90
+ fact = Upl::Term.functor :person, :james, :madison, (o = Object.new)
91
+ Upl::Runtime.eval Upl::Term.functor :assert, fact
92
+
93
+ fact2 = Upl::Term.functor :person, :thomas, :paine, (thing2 = Object.new)
94
+ Upl::Runtime.eval Upl::Term.functor :assert, fact2
95
+
96
+ # Note that both facts are in the result
97
+ query_term, query_vars = Upl::Runtime.term_vars 'person(A,B,C)'
98
+ Array Upl::Runtime.term_vars_query query_term, query_vars
99
+ =>[{:A=>james, :B=>madison, :C=>#<Object:0x0000563f56e35580 @_upl_atom=439429>},
100
+ {:A=>thomas, :B=>paine, :C=>#<Object:0x0000563f56d2b5b8 @_upl_atom=439813>}]
101
+
102
+ # Unify C with thing2. This needs a nicer api :-\
103
+ query_term, query_vars = Upl::Runtime.term_vars 'person(A,B,C)'
104
+ Upl::Extern.PL_unify query_vars.last.args.to_a.last, thing2.to_term
105
+
106
+ # ... and we get the correct result
107
+ # Note that the first fact is not in the result.
108
+ Array Upl::Runtime.term_vars_query query_term, query_vars
109
+ => [{:A=>thomas, :B=>paine, :C=>#<Object:0x0000563f56d2b5b8 @_upl_atom=439813>}]
110
+ ```
111
+
112
+ ## Disclaimer
113
+
114
+ This is in-development code. I use it for some things other than just playing with. It might be useful for you too.
115
+
116
+ ruby has a GC. swipl has a GC. At some point they will disagree. I haven't reached that point yet.
117
+
118
+ ## Naming
119
+
120
+ ```Upl```? Wat!? Why?
121
+
122
+ Well. ```swipl``` was taken. ```ripl``` was taken. So maybe in keeping with long tradition: ```rupl```.
123
+
124
+ But that leads to ```pry -I. -rrupl``` which is Not Fun.
125
+
126
+ But ```upl``` gives you ```pry -I. -rupl``` So it's kinda like ```ubygems```.
127
+
128
+ Also, ```Upl``` rhymes with tuple and/or supple. Depending on your pronunciation :-p
129
+
130
+ ## Installation
131
+
132
+ Add this line to your application's Gemfile:
133
+
134
+ ```ruby
135
+ gem 'upl'
136
+ ```
137
+
138
+ And then execute:
139
+
140
+ $ bundle
141
+
142
+ Or install it yourself as:
143
+
144
+ $ gem install upl
145
+
146
+ ## Usage
147
+
148
+ For a REPL say
149
+
150
+ pry -rupl
151
+
152
+ or
153
+
154
+ bin/console
155
+
156
+
157
+ ## Development
158
+
159
+ Install SWI-Prolog with both the swipl executable and libswipl.so
160
+
161
+
162
+ And bundler wants me to tell you the following:
163
+
164
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
165
+
166
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
167
+
168
+ ## Contributing
169
+
170
+ Bug reports and pull requests are welcome on GitHub at https://github.com/djellemah/upl.
171
+
172
+ ## License
173
+
174
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # require "bundler/setup"
4
+ require_relative '../lib/upl'
5
+
6
+ require 'pry'
7
+ Pry.start
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/lib/upl.rb ADDED
@@ -0,0 +1,22 @@
1
+ require 'pathname'
2
+ require_relative 'upl/version'
3
+
4
+ require_relative 'upl/extern'
5
+ require_relative 'upl/term'
6
+ require_relative 'upl/variable'
7
+ require_relative 'upl/atom'
8
+ require_relative 'upl/runtime'
9
+ require_relative 'upl/tree'
10
+ require_relative 'upl/inter'
11
+
12
+ module Upl
13
+ def self.query st, &blk
14
+ term, vars = Runtime::term_vars st
15
+ Runtime::term_vars_query term, vars, &blk
16
+ end
17
+
18
+ def self.consult filename
19
+ p = Pathname filename
20
+ Runtime::eval %Q{["#{p.realpath.to_s}"]}
21
+ end
22
+ end
data/lib/upl/atom.rb ADDED
@@ -0,0 +1,36 @@
1
+ module Upl
2
+ class Atom
3
+ def initialize( atom_ref )
4
+ @atom_ptr = atom_ref.ptr
5
+ atom_chars_ptr = ::Upl::Extern::PL_atom_chars @atom_ptr
6
+ @_symbol = atom_chars_ptr.to_s.to_sym
7
+ end
8
+
9
+ # drop the term immediately, and just keep the atom pointer
10
+ def self.of_term( term_t )
11
+ rv = Extern::PL_get_atom term_t, (atom_ref = Fiddle::Pointer.new(0).ref)
12
+ raise "can't get atom from term" unless rv == 1
13
+ new atom_ref
14
+ end
15
+
16
+ attr_reader :atom_ptr
17
+
18
+ def == rhs
19
+ to_sym == rhs.to_sym
20
+ end
21
+
22
+ def to_sym
23
+ @_symbol or raise "no symbol for atom"
24
+ end
25
+
26
+ def to_s
27
+ @_string ||= to_sym.to_s
28
+ end
29
+
30
+ def inspect; to_sym end
31
+
32
+ def pretty_print pp
33
+ pp.text to_s
34
+ end
35
+ end
36
+ end
data/lib/upl/extern.rb ADDED
@@ -0,0 +1,242 @@
1
+ require 'fiddle'
2
+ require 'fiddle/import'
3
+ require 'pathname'
4
+
5
+ module Upl
6
+ module Extern
7
+ extend Fiddle::Importer
8
+
9
+ # use swipl config to find the .so file
10
+ def self.so_path
11
+ begin
12
+ swipl_exe = 'swipl'
13
+ values = `#{swipl_exe} --dump-runtime-variables=sh`.each_line.with_object Hash.new do |line,ha|
14
+ line.chomp!
15
+ # split by = and for rhs strip surrounding quotes and trailing ;
16
+ line =~ /^([^=]+)="([^"]*)";$/
17
+ ha[$1] = $2.strip
18
+ end
19
+ rescue Errno::ENOENT => ex
20
+ puts "#{swipl_exe} not found on path #{ENV['PATH']}"
21
+ exit 1
22
+ end
23
+
24
+ begin
25
+ # should result in something like
26
+ # /usr/lib64/swipl-7.7.18/lib/x86_64-linux/libswipl.so
27
+ # which should actually exist
28
+ p = Pathname "#{values['PLBASE']}/lib/#{values['PLARCH']}/#{values['PLLIB'].gsub('-l', 'lib')}.#{values['PLSOEXT']}"
29
+ p.realpath.to_s
30
+ rescue Errno::ENOENT => ex
31
+ puts "problem with library #{p.to_s}: #{ex.message}"
32
+ exit 1
33
+ end
34
+ end
35
+
36
+ dlload so_path
37
+
38
+ NULL = Fiddle::Pointer.new 0
39
+
40
+ typealias 'term_t', 'void *'
41
+ typealias 'module_t', 'void *'
42
+ typealias 'predicate_t', 'void *'
43
+ typealias 'atom_t', 'void *'
44
+ typealias 'qid_t', 'void *'
45
+ typealias 'fid_t', 'uintptr_t'
46
+ typealias 'functor_t', 'void *'
47
+
48
+ extern 'int PL_initialise(int argc, char **argv)'
49
+ extern 'int PL_halt(int status)'
50
+
51
+ # for constructing types see https://stackoverflow.com/questions/30293406/is-it-possible-to-use-fiddle-to-pass-or-return-a-struct-to-native-code
52
+ # Predicate_t = struct ['char *data','char *more_data','size_t len']
53
+
54
+ # terms
55
+ extern 'predicate_t PL_predicate(const char *name, int arity, const char *module)'
56
+
57
+ ##############
58
+ # querying and getting results
59
+
60
+ # copied from SWI-Prolog.h
61
+ module Flags
62
+ PL_Q_DEBUG = 0x0001 # = TRUE for backward compatibility
63
+ PL_Q_NORMAL = 0x0002 # normal usage
64
+ PL_Q_NODEBUG = 0x0004 # use this one
65
+ PL_Q_CATCH_EXCEPTION = 0x0008 # handle exceptions in C
66
+ PL_Q_PASS_EXCEPTION = 0x0010 # pass to parent environment
67
+ PL_Q_ALLOW_YIELD = 0x0020 # Support I_YIELD
68
+ PL_Q_EXT_STATUS = 0x0040 # Return extended status
69
+ PL_Q_DETERMINISTIC = 0x0100 # call was deterministic
70
+ end
71
+
72
+ extern 'fid_t PL_open_foreign_frame(void)'
73
+ extern 'void PL_rewind_foreign_frame(fid_t cid)'
74
+ extern 'void PL_close_foreign_frame(fid_t cid)'
75
+ extern 'void PL_discard_foreign_frame(fid_t cid)'
76
+
77
+ PL_VARIABLE = (1)
78
+ PL_ATOM = (2)
79
+ PL_INTEGER = (3)
80
+ PL_FLOAT = (4)
81
+ PL_STRING = (5)
82
+ PL_TERM = (6)
83
+ PL_NIL = (7)
84
+ PL_BLOB = (8)
85
+ PL_LIST_PAIR = (9)
86
+ PL_FUNCTOR = (10)
87
+ PL_LIST = (11)
88
+ PL_CHARS = (12)
89
+ PL_POINTER = (13)
90
+ PL_CODE_LIST = (14)
91
+ PL_CHAR_LIST = (15)
92
+ PL_BOOL = (16)
93
+ PL_FUNCTOR_CHARS = (17)
94
+ _PL_PREDICATE_INDICATOR = (18)
95
+ PL_SHORT = (19)
96
+ PL_INT = (20)
97
+ PL_LONG = (21)
98
+ PL_DOUBLE = (22)
99
+ PL_NCHARS = (23)
100
+ PL_UTF8_CHARS = (24)
101
+ PL_UTF8_STRING = (25)
102
+ PL_INT64 = (26)
103
+ PL_NUTF8_CHARS = (27)
104
+ PL_NUTF8_CODES = (29)
105
+ PL_NUTF8_STRING = (30)
106
+ PL_NWCHARS = (31)
107
+ PL_NWCODES = (32)
108
+ PL_NWSTRING = (33)
109
+ PL_MBCHARS = (34)
110
+ PL_MBCODES = (35)
111
+ PL_MBSTRING = (36)
112
+ PL_INTPTR = (37)
113
+ PL_CHAR = (38)
114
+ PL_CODE = (39)
115
+ PL_BYTE = (40)
116
+ PL_PARTIAL_LIST = (41)
117
+ PL_CYCLIC_TERM = (42)
118
+ PL_NOT_A_LIST = (43)
119
+ PL_DICT = (44)
120
+
121
+ module Convert
122
+ REP_UTF8 = 0x1000
123
+ BUF_MALLOC = 0x0200
124
+
125
+ CVT_ATOM = 0x0001
126
+ CVT_STRING = 0x0002
127
+ CVT_LIST = 0x0004
128
+ CVT_INTEGER = 0x0008
129
+ CVT_FLOAT = 0x0010
130
+ CVT_VARIABLE = 0x0020
131
+ CVT_NUMBER = CVT_INTEGER|CVT_FLOAT
132
+ CVT_ATOMIC = CVT_NUMBER|CVT_ATOM|CVT_STRING
133
+ CVT_WRITE = 0x0040
134
+ CVT_WRITE_CANONICAL = 0x0080
135
+ CVT_WRITEQ = 0x00C0
136
+ CVT_ALL = CVT_ATOMIC|CVT_LIST
137
+ end
138
+
139
+ extern 'predicate_t PL_pred(functor_t f, module_t m)'
140
+
141
+ # ctx can be NULL, p is the predicate with arity, t0 is the collection of terms to be filled by the query
142
+ extern 'qid_t PL_open_query(module_t ctx, int pl_q_flags, predicate_t p, term_t t0)'
143
+ extern 'int PL_next_solution(qid_t qid)'
144
+ extern 'void PL_cut_query(qid_t qid)'
145
+ extern 'void PL_close_query(qid_t qid)'
146
+
147
+ # shortcut when there will only be one answer
148
+ extern 'int PL_call_predicate(module_t m, int pl_q_flags, predicate_t p, term_t t0)'
149
+
150
+ extern 'term_t PL_exception(qid_t qid)'
151
+ extern 'int PL_raise_exception(term_t exception)'
152
+ extern 'int PL_throw(term_t exception)'
153
+ extern 'void PL_clear_exception(void)'
154
+
155
+ # qid_t PL_current_query(void)
156
+
157
+ # module can be NULL
158
+ extern 'int PL_call(term_t t, module_t m)'
159
+
160
+ ####################
161
+ # storage of db records. Not sure if it's useful
162
+ # http://www.swi-prolog.org/pldoc/man?section=foreign-misc
163
+ # void PL_erase(record_t record)
164
+ # record_t PL_record(term_t +t)
165
+
166
+ # create empty term(s)
167
+ extern 'term_t PL_new_term_ref()'
168
+ extern 'term_t PL_new_term_refs(int n)'
169
+ extern 'term_t PL_copy_term_ref(term_t from)'
170
+
171
+ extern 'int PL_new_atom(const char *s)'
172
+ extern 'int PL_new_atom_nchars(size_t len, const char *s)'
173
+ extern 'functor_t PL_new_functor(atom_t f, int a)'
174
+
175
+ extern 'const char * PL_atom_chars(atom_t a)'
176
+ extern 'int PL_get_atom(term_t t, atom_t * a)'
177
+
178
+ # get the type
179
+ extern 'int PL_term_type(term_t t)'
180
+ extern 'int PL_is_variable(term_t t)'
181
+ extern 'int PL_is_ground(term_t t)'
182
+ extern 'int PL_is_atom(term_t t)'
183
+ extern 'int PL_is_integer(term_t t)'
184
+ extern 'int PL_is_string(term_t t)'
185
+ extern 'int PL_is_float(term_t t)'
186
+ extern 'int PL_is_rational(term_t t)'
187
+ extern 'int PL_is_compound(term_t t)'
188
+ extern 'int PL_is_callable(term_t t)'
189
+ extern 'int PL_is_functor(term_t t, functor_t f)'
190
+ extern 'int PL_is_list(term_t t)'
191
+ extern 'int PL_is_pair(term_t t)'
192
+ extern 'int PL_is_atomic(term_t t)'
193
+ extern 'int PL_is_number(term_t t)'
194
+ extern 'int PL_is_acyclic(term_t t)'
195
+
196
+ extern 'int PL_put_atom(term_t t, atom_t a)'
197
+ extern 'int PL_put_variable(term_t t)'
198
+ extern 'int PL_put_functor(term_t t, functor_t functor)'
199
+
200
+ extern 'int PL_cons_functor_v(term_t h, functor_t fd, term_t a0)'
201
+
202
+ extern 'int PL_get_atom_chars(term_t t, char **a)'
203
+ extern 'int PL_get_string(term_t t, char **s, size_t *len)'
204
+ extern 'int PL_get_integer(term_t t, int *i)'
205
+ extern 'int PL_get_chars(term_t t, char **s, unsigned int flags)'
206
+ extern 'int PL_get_name_arity(term_t t, atom_t *name, int *arity)'
207
+ extern 'int PL_get_arg(int index, term_t t, term_t a)'
208
+ extern "int PL_get_blob(term_t t, void **blob, size_t *len, PL_blob_t **type)"
209
+
210
+ extern 'int PL_get_functor(term_t t, functor_t *f)'
211
+
212
+ extern 'int PL_get_nil(term_t l)'
213
+ extern 'int PL_get_list(term_t l, term_t h, term_t t)'
214
+ extern 'int PL_get_head(term_t l, term_t h)'
215
+ extern 'int PL_get_tail(term_t l, term_t t)'
216
+
217
+ extern 'int PL_unify(term_t t1, term_t t2)'
218
+
219
+ extern 'int PL_skip_list(term_t list, term_t tail, size_t *len)'
220
+
221
+ ##################
222
+ # attributed variables. Don't do what I thought they did.
223
+ extern 'int PL_is_attvar(term_t t)'
224
+ extern 'int PL_get_attr(term_t v, term_t a)'
225
+
226
+ ##################
227
+ # memory
228
+ extern 'void PL_free(void *mem)'
229
+
230
+ ####################
231
+ # looks like parsing of terms
232
+ # only >= 7.6.0
233
+ # get version, eval current_prolog_flag(version_data,swi(M,I,P,E)). Major, mInor, Patch, Extra[]
234
+ # PL_EXPORT(int) PL_put_term_from_chars(term_t t, int flags, size_t len, const char *s);
235
+ # extern 'int PL_put_term_from_chars(term_t t, int flags, size_t len, const char *s)'
236
+
237
+ extern 'int PL_chars_to_term(const char *chars, term_t term)'
238
+ extern 'int PL_wchars_to_term(const pl_wchar_t *chars, term_t term)'
239
+
240
+ extern 'void PL_unregister_atom(atom_t a)'
241
+ end
242
+ end
@@ -0,0 +1,51 @@
1
+ module Upl
2
+ # Just an idea, not used yet.
3
+ class Functor
4
+ def initialize( atom, args_or_arity )
5
+ @atom = atom
6
+
7
+ case args_or_arity
8
+ when Array
9
+ @args = args_or_arity
10
+ @arity = args.size
11
+ when Integer
12
+ @arity = args_or_arity
13
+ else
14
+ "dunno bout #{args_or_arity.inspect}"
15
+ end
16
+ end
17
+
18
+ attr_reader :atom
19
+ def args; @args || [] end
20
+ def arity; @arity || args.size end
21
+
22
+ # create a functor_t pointer
23
+ def functor_t
24
+ raise NotImplementedError
25
+ end
26
+
27
+ # create a predicate_t
28
+ def predicate_t
29
+ raise NotImplementedError
30
+ end
31
+
32
+ def pretty_print(pp)
33
+ unless atom.to_sym == :','
34
+ pp.text atom.to_s
35
+ if arity > 0
36
+ pp.text ?/
37
+ pp.text arity.to_s
38
+ end
39
+ end
40
+
41
+ if arity > 0
42
+ pp.group 1, ?(, ?) do
43
+ args.each_with_index do |ruby_term,i|
44
+ ruby_term.pretty_print pp
45
+ pp.text ?, if i < arity - 1
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
data/lib/upl/inter.rb ADDED
@@ -0,0 +1,48 @@
1
+ # TODO not used
2
+ class Object
3
+ def to_atom
4
+ if frozen?
5
+ # TODO must check instance variable here
6
+ _upl_atomize
7
+ else
8
+ @_upl_atom ||= _upl_atomize
9
+ end
10
+ end
11
+
12
+ def to_term
13
+ if frozen?
14
+ # TODO must check instance variable here
15
+ _upl_termize
16
+ else
17
+ # @_upl_termize ||= _upl_termize
18
+ _upl_termize
19
+ end
20
+ end
21
+
22
+ protected
23
+
24
+ def _upl_termize
25
+ term_t = Upl::Extern.PL_new_term_ref
26
+ rv = Upl::Extern.PL_put_atom term_t, to_atom
27
+ rv == 1 or raise "can't create atom from #{self}"
28
+ term_t
29
+ end
30
+
31
+ def _upl_atomize
32
+ # see also PL_agc_hook for hooking into the swipl GC
33
+ ObjectSpace.define_finalizer self do |this_obj|
34
+ # TODO PL_unregister_atom? Finalizer?
35
+ Upl::Extern.PL_unregister_atom this_obj.instance_variable_get :@_upl_atom
36
+ end
37
+ Upl::Extern.PL_new_atom "ruby-#{object_id.to_s}"
38
+ end
39
+ end
40
+
41
+ class Symbol
42
+ def to_atom
43
+ Upl::Extern.PL_new_atom to_s
44
+ end
45
+ end
46
+
47
+ module Inter
48
+ end
@@ -0,0 +1,183 @@
1
+ require 'fiddle'
2
+
3
+ require_relative 'extern'
4
+
5
+ class Fiddle::Pointer
6
+ def term_type
7
+ ::Upl::Extern.PL_term_type self
8
+ end
9
+
10
+ def type_string
11
+ type_int = term_type
12
+ ::Upl::Extern.constants.find{|c| (::Upl::Extern.const_get c) == type_int}
13
+ end
14
+ end
15
+
16
+ module Upl
17
+ module Runtime
18
+ Ptr = Fiddle::Pointer
19
+
20
+ def self.init
21
+ # set up no output so we don't get swipl command line interfering in ruby
22
+ # TODO exception handling should not kick off a prolog terminal
23
+ # TODO see gem-swipl for more useful stuff here
24
+ args = %w[upl -q --tty=false --nosignals]
25
+
26
+ # convert args to char **
27
+ ptr_size = Extern.sizeof 'char*'
28
+ arg_ptrs = Ptr.malloc(ptr_size * args.size)
29
+ args.each_with_index do |rg,i|
30
+ (arg_ptrs + i*ptr_size)[0,ptr_size] = Ptr[rg].ref
31
+ end
32
+
33
+ # call init
34
+ rv = Extern.PL_initialise args.size, arg_ptrs
35
+ rv == 1 or raise 'PL_initialise failed'
36
+ end
37
+
38
+ # once_only. Should probably be a singleton or something.
39
+ @inited ||= init
40
+
41
+ def self.predicate name, arity, module_name = nil
42
+ Extern.PL_predicate Fiddle::Pointer[name.to_s], arity, NULL
43
+ end
44
+
45
+ # Use prolog predicate to parse the string into a term with its named variables
46
+ def self.term_vars st
47
+ # atom_to_term('your_pred(A,B,C,D)',Term,Options).
48
+ terms = Extern.PL_new_term_refs 3
49
+ atom, term, options = terms+0, terms+1, terms+2
50
+
51
+ Extern::PL_put_atom atom, (Extern::PL_new_atom Fiddle::Pointer[st])
52
+ Extern::PL_put_variable term
53
+ Extern::PL_put_variable options
54
+
55
+ # docs say to use read_term_from_atom/3, but it fails with uninstantiated variables for 7.7.18
56
+ rv = Extern::PL_call_predicate \
57
+ Extern::NULL, # module
58
+ 0, # flags, see PL_open_query
59
+ (predicate 'atom_to_term', 3),
60
+ terms
61
+
62
+ # first must be Term.new otherwise Term unhooks the term_t pointer
63
+ # vars *must* be unhooked though ¯\_(ツ)_/¯
64
+ return (Term.new term), (list_to_ary options do |elt| Term.new elt end)
65
+ end
66
+
67
+ def self.unify( term_a, term_b )
68
+ rv = Extern::PL_unify term_a.term_t, term_a.term_t
69
+ rv == 1 or raise "can't unify #{term_a} and #{term_b}"
70
+ end
71
+
72
+ # do a query for the given term and vars, as parsed by term_vars
73
+ def self.term_vars_query qterm, qvars
74
+ raise "not a term" unless Term === qterm
75
+ return enum_for __method__, qterm, qvars unless block_given?
76
+
77
+ fid_t = Extern.PL_open_foreign_frame
78
+
79
+ begin
80
+ # input values
81
+ terms_ptr = Extern.PL_new_term_refs qterm.arity
82
+ qterm.args.each_with_index do |arg,idx|
83
+ Extern::PL_unify (terms_ptr+idx), arg
84
+ end
85
+
86
+ # module is NULL, flags is 0
87
+ query_id_p = Extern.PL_open_query Extern::NULL, 0, qterm.to_predicate, terms_ptr
88
+ query_id_p != 0 or raise 'no space on environment stack, see SWI-Prolog docs for PL_open_query'
89
+
90
+ loop do
91
+ # TODO handle PL_Q_EXT_STATUS
92
+ res = Extern.PL_next_solution query_id_p
93
+ break if res == 0
94
+
95
+ hash = qvars.each_with_object Hash.new do |name_var,ha|
96
+ name_term_t, var_term_t = name_var.args.to_a
97
+ name = Term.new name_term_t
98
+
99
+ # term_t will be invalidated by the next call to PL_next_solution,
100
+ # so we need to construct a ruby tree of the value term
101
+ val = ha[name.atom.to_sym] = Tree.of_term var_term_t
102
+ # binding.pry if val.to_sym == :query_debug_settings rescue false
103
+ end
104
+
105
+ yield hash
106
+ end
107
+
108
+ ensure
109
+ query_id_p&.to_i and Extern.PL_close_query query_id_p
110
+ end
111
+
112
+ ensure
113
+ # this also gets called after enum_for, so test for fid_t
114
+ fid_t and Extern.PL_close_foreign_frame fid_t
115
+ end
116
+
117
+ def self.eval st_or_term
118
+ p_term =
119
+ case st_or_term
120
+ when String
121
+ rv = Extern.PL_chars_to_term Fiddle::Pointer[st_or_term], (p_term = Extern.PL_new_term_ref)
122
+ raise "failure parsing term #{st_or_term}" unless rv == 1
123
+ p_term
124
+ when Term
125
+ st_or_term.term_t
126
+ else
127
+ raise "dunno bout #{st_or_term}"
128
+ end
129
+
130
+ rv = Extern.PL_call p_term, Extern::NULL
131
+ rv == 1 or raise "failure executing term #{st}"
132
+ end
133
+
134
+ def self.predicate name, arity
135
+ pred_p = Extern.PL_predicate Ptr[name.to_s], arity, Extern::NULL
136
+ end
137
+
138
+ def self.list_to_ary lst, &elt_converter
139
+ rv = []
140
+
141
+ while Extern::PL_get_nil(lst) != 1 # not end of list
142
+ res = Extern::PL_get_list \
143
+ lst,
144
+ (head = Extern.PL_new_term_ref),
145
+ (rst = Extern.PL_new_term_ref)
146
+
147
+ break unless res == 1
148
+
149
+ rv << (elt_converter.call head)
150
+ lst = rst
151
+ end
152
+
153
+ rv
154
+ end
155
+
156
+ # simple query with predicate / arity
157
+ def self.squery predicate_str, arity
158
+ return enum_for :squery, predicate_str, arity unless block_given?
159
+ p_atom = Extern::PL_new_atom Fiddle::Pointer[predicate_str]
160
+ p_functor = Extern::PL_new_functor p_atom, arity
161
+ p_predicate = Extern::PL_pred p_functor, Extern::NULL
162
+
163
+ answer_lst = Extern.PL_new_term_refs arity
164
+ query_id_p = Extern.PL_open_query Extern::NULL, 0, p_predicate, answer_lst
165
+
166
+ loop do
167
+ res = Extern.PL_next_solution query_id_p
168
+ break if res == 0
169
+
170
+ answrs =
171
+ arity.times.map do |i|
172
+ term_to_ruby answer_lst+i
173
+ end
174
+
175
+ yield answrs
176
+ end
177
+
178
+ ensure
179
+ # NOTE this also gets called after enum_for
180
+ query_id_p&.to_i and Extern.PL_close_query query_id_p
181
+ end
182
+ end
183
+ end
data/lib/upl/term.rb ADDED
@@ -0,0 +1,132 @@
1
+ module Upl
2
+ # So Prolog terms are a rose tree. Who woulda thunkit?
3
+
4
+ # OK, so I guess this thing's job is interacting with term_t, whereas Tree's
5
+ # job is being a ruby copy of a term-tree.
6
+ class Term
7
+ def initialize term_or_string
8
+ case term_or_string
9
+ when String
10
+ raise "can't do strings yet"
11
+ # PL_chars_to_term term_or_string
12
+ when Fiddle::Pointer
13
+ # assume this is a pointer to a term. Unsafe, but there's no choice really
14
+ @term_t = term_or_string
15
+ else
16
+ raise "can't handle #{term_or_string}"
17
+ end
18
+ end
19
+
20
+ attr_reader :term_t
21
+
22
+ def to_term; term_t end
23
+
24
+ # Make a copy of all the term information. Useful for passing in to queries, apparently.
25
+ def self.copy term_t
26
+ copy_term_t = Extern.PL_copy_term_ref term_t
27
+ new copy_term_t
28
+ end
29
+
30
+ def self.of_atom atom
31
+ term_t = Extern.PL_new_term_ref
32
+ rv = Extern.PL_put_atom term_t, atom.to_atom
33
+ rv == 1 or raise "can't set term to atom #{atom}"
34
+ term_t
35
+ end
36
+
37
+ # args are things that can be converted to term_t pointers using to_term method
38
+ def self.functor name, *args
39
+ # TODO maybe use a frame or something because this allocates quite a few sub-terms
40
+ functor_t = Extern.PL_new_functor name.to_sym.to_atom, args.size
41
+
42
+ arg_terms = Extern.PL_new_term_refs args.size
43
+ args.each_with_index do |arg,i|
44
+ Extern::PL_unify (arg_terms+i), arg.to_term
45
+ end
46
+
47
+ term_t = Extern.PL_new_term_ref
48
+ rv = Extern.PL_cons_functor_v term_t, functor_t, arg_terms
49
+ rv == 1 or raise "can't populate functor #{name}"
50
+
51
+ new term_t
52
+ end
53
+
54
+ def populate
55
+ int_ptr = Runtime::Ptr[0].ref
56
+ atom_ptr = Runtime::Ptr[0].ref
57
+ rv = Extern::PL_get_name_arity term_t, atom_ptr, int_ptr
58
+ # This happens when the term_t is not a PL_TERM (ie a compound)
59
+ raise "can't populate term" unless rv == 1
60
+
61
+ @arity = int_ptr.ptr.to_i
62
+ @atom = Atom.new atom_ptr
63
+
64
+ self
65
+ end
66
+
67
+ def == rhs
68
+ @atom == rhs.atom && @arity == rhs.arity && args == rhs.args
69
+ end
70
+
71
+ def <=> rhs
72
+ [@atom, @arity] <=> [rhs.atom, rhs.arity]
73
+ end
74
+
75
+ # attr_reader :atom, :arity
76
+
77
+ def atom
78
+ @atom or begin
79
+ populate
80
+ @atom
81
+ end
82
+ end
83
+
84
+ def arity
85
+ @arity or begin
86
+ populate
87
+ @arity
88
+ end
89
+ end
90
+
91
+ def to_functor
92
+ Extern::PL_new_functor atom.atom_ptr, arity
93
+ end
94
+
95
+ def to_predicate
96
+ Extern::PL_pred to_functor, Extern::NULL
97
+ end
98
+
99
+ def tree; @tree || (Tree.new self) end
100
+ def to_ruby; tree end
101
+
102
+ # Assume that term_t still has a value. Which means we have to copy all
103
+ # values before the underlying term_t is changed.
104
+ # TODO leaning hard towards each with Enumerable
105
+ def args
106
+ return enum_for :args unless block_given?
107
+
108
+ (1..arity).each do |i|
109
+ rv = Extern::PL_get_arg i, term_t, (subterm = Extern.PL_new_term_ref)
110
+ if rv == 1
111
+ yield subterm
112
+ else
113
+ puts "#{rv}: can't convert #{i} arg of #{atom}"
114
+ yield subterm
115
+ end
116
+ end
117
+ end
118
+
119
+ def []=( idx, val_term_t)
120
+ Extern::PL_get_arg idx+1, term_t, (subterm = Extern.PL_new_term_ref)
121
+ rv = Extern.PL_unify subterm, val_term_t
122
+ rv == 1 or raise "can't set index #{idx}"
123
+ end
124
+
125
+ def pretty_print(pp)
126
+ # to_ruby.pretty_print pp
127
+ pp.text atom.to_s
128
+ pp.text ?/
129
+ pp.text arity.to_s
130
+ end
131
+ end
132
+ end
data/lib/upl/tree.rb ADDED
@@ -0,0 +1,119 @@
1
+ module Upl
2
+ # Convert a term into a tree of ruby objects. This is necessary because
3
+ # queries give back their results as terms which are invalidated as soon as
4
+ # the next set of results is calculated. So we need to turn those terms into a
5
+ # ruby representation and keep them around.
6
+ class Tree
7
+ # term is either a Term instance, or a Fiddle::Pointer to a term_t
8
+ def initialize( term )
9
+ init term
10
+ end
11
+
12
+ def init term
13
+ case term
14
+ when Term
15
+ @atom = term.atom
16
+ @args = term.args.map do |arg|
17
+ self.class.term_to_ruby arg
18
+ end
19
+ when Fiddle::Pointer
20
+ init Term.new term
21
+ end
22
+ end
23
+
24
+ attr_reader :atom, :args
25
+
26
+ def self.of_term term_t
27
+ term_to_ruby term_t
28
+ end
29
+
30
+ def self.term_to_ruby term
31
+ case term.term_type
32
+ when Extern::PL_VARIABLE
33
+ Variable.copy term
34
+
35
+ when Extern::PL_ATOM
36
+ atom = Atom.of_term term
37
+ if atom.to_s =~ /^ruby-(\d+)/
38
+ ObjectSpace._id2ref $1.to_i
39
+ else
40
+ atom
41
+ end
42
+
43
+ when Extern::PL_INTEGER
44
+ Extern.PL_get_integer term, (int_ptr = Fiddle::Pointer[0].ref)
45
+ int_ptr.ptr.to_i
46
+
47
+ when Extern::PL_STRING
48
+ rv = Extern.PL_get_string term, (str_ptr = Fiddle::Pointer[0].ref), (len_ptr = Fiddle::Pointer[0].ref)
49
+ value_ptr = Fiddle::Pointer.new str_ptr.ptr, len_ptr.ptr.to_i
50
+ value_ptr.to_s
51
+
52
+ when Extern::PL_NIL
53
+ # TODO maybe this should be [] - see what happens when term_vars has no vars
54
+ # although nil.to_a == []
55
+ nil
56
+
57
+ when Extern::PL_TERM
58
+ Tree.new term
59
+
60
+ when Extern::PL_LIST_PAIR
61
+ list_to_ary term
62
+
63
+ end
64
+ end
65
+
66
+ def self.list_to_ary lst
67
+ rv = []
68
+
69
+ while Extern::PL_get_nil(lst) != 1 # not end of list
70
+ res = Extern::PL_get_list \
71
+ lst,
72
+ (head = Extern.PL_new_term_ref),
73
+ (rst = Extern.PL_new_term_ref)
74
+
75
+ break unless res == 1
76
+
77
+ rv << (term_to_ruby head)
78
+ lst = rst
79
+ end
80
+
81
+ rv
82
+ end
83
+
84
+ def arity; args.size end
85
+
86
+ def pretty_print(pp)
87
+ unless atom == :','
88
+ pp.text atom.to_s
89
+ if arity > 0
90
+ pp.text ?/
91
+ pp.text arity.to_s
92
+ end
93
+ end
94
+
95
+ if arity > 0
96
+ pp.group 1, ?(, ?) do
97
+ args.each_with_index do |ruby_term,i|
98
+ ruby_term.pretty_print pp
99
+ pp.text ?, if i < arity - 1
100
+ end
101
+ end
102
+ end
103
+ end
104
+
105
+ protected
106
+
107
+ def populate_args count
108
+ (1..arity).each do |i|
109
+ rv = Extern::PL_get_arg i, term_t, (subterm = Extern.PL_new_term_ref)
110
+ if rv == 1
111
+ yield subterm
112
+ else
113
+ puts "#{rv}: can't convert #{i} arg of #{atom}"
114
+ yield subterm
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,61 @@
1
+ module Upl
2
+ # Really this is just an empty term.
3
+ class Variable
4
+ def initialize term_t = nil
5
+ @term_t = term_t || Extern.PL_new_term_ref
6
+ end
7
+
8
+ attr_reader :term_t
9
+
10
+ def self.copy term_t
11
+ inst = new term_t
12
+
13
+ inst.attributed? and inst.attribute
14
+ inst.to_s
15
+
16
+ inst
17
+ end
18
+
19
+ # bit of a hack to create empty variables for a functor
20
+ def self.to_term
21
+ new
22
+ end
23
+
24
+ def to_s; _string end
25
+
26
+ def _string
27
+ @_string ||= begin
28
+ Extern::PL_get_chars \
29
+ term_t,
30
+ (str_ref = Runtime::Ptr[''].ref),
31
+ Extern::Convert::CVT_VARIABLE | Extern::Convert::REP_UTF8 | Extern::Convert::BUF_MALLOC # | Extern::CVT_ALL
32
+
33
+ str_ref.ptr.to_s
34
+ end
35
+ end
36
+
37
+ def attributed?
38
+ if instance_variable_defined? :@attributed
39
+ @attributed
40
+ else
41
+ @attributed = (Extern::PL_is_attvar term_t) == 1
42
+ end
43
+ end
44
+
45
+ def attribute
46
+ @attribute ||= begin
47
+ rv = Extern::PL_get_attr term_t, (val = Extern.PL_new_term_ref)
48
+ rv == 1 or raise "can't get attribute for variable"
49
+ Tree.of_term val
50
+ end
51
+ end
52
+
53
+ def pretty_print pp
54
+ if attributed?
55
+ attribute.pretty_print pp
56
+ else
57
+ pp.text to_s
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,3 @@
1
+ module Upl
2
+ VERSION = '0.0.1'
3
+ end
data/upl.gemspec ADDED
@@ -0,0 +1,38 @@
1
+ lib = File.expand_path('../lib', __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require 'upl/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'upl'
7
+ spec.version = Upl::VERSION
8
+ spec.authors = ['John Anderson']
9
+ spec.email = ['panic@semiosix.com']
10
+
11
+ spec.summary = %q{Access SWI-Prolog engine from ruby}
12
+ spec.description = %q{Access SWI-Prolog engine from ruby}
13
+ spec.homepage = 'https://github.com/djellemah/upl'
14
+ spec.license = 'MIT'
15
+
16
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
17
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
18
+ if spec.respond_to?(:metadata)
19
+ spec.metadata["allowed_push_host"] = 'https://rubygems.org'
20
+ else
21
+ raise 'RubyGems 2.0 or newer is required to protect against public gem pushes.'
22
+ end
23
+
24
+ # Specify which files should be added to the gem when it is released.
25
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
26
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
27
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
28
+ end
29
+ spec.bindir = 'exe'
30
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
31
+ spec.require_paths = ['lib']
32
+
33
+ spec.add_development_dependency 'bundler', '~> 1.16'
34
+ spec.add_development_dependency 'rake', '~> 10.0'
35
+ spec.add_development_dependency 'rspec', '~> 3.0'
36
+ spec.add_dependency 'pry'
37
+ spec.add_dependency 'fiddle'
38
+ end
metadata ADDED
@@ -0,0 +1,135 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: upl
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - John Anderson
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2018-09-13 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.16'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.16'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: pry
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: fiddle
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: Access SWI-Prolog engine from ruby
84
+ email:
85
+ - panic@semiosix.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - ".gitignore"
91
+ - ".rspec"
92
+ - ".travis.yml"
93
+ - Gemfile
94
+ - LICENSE.txt
95
+ - README.md
96
+ - Rakefile
97
+ - bin/console
98
+ - bin/setup
99
+ - lib/upl.rb
100
+ - lib/upl/atom.rb
101
+ - lib/upl/extern.rb
102
+ - lib/upl/functor.rb
103
+ - lib/upl/inter.rb
104
+ - lib/upl/runtime.rb
105
+ - lib/upl/term.rb
106
+ - lib/upl/tree.rb
107
+ - lib/upl/variable.rb
108
+ - lib/upl/version.rb
109
+ - upl.gemspec
110
+ homepage: https://github.com/djellemah/upl
111
+ licenses:
112
+ - MIT
113
+ metadata:
114
+ allowed_push_host: https://rubygems.org
115
+ post_install_message:
116
+ rdoc_options: []
117
+ require_paths:
118
+ - lib
119
+ required_ruby_version: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ required_rubygems_version: !ruby/object:Gem::Requirement
125
+ requirements:
126
+ - - ">="
127
+ - !ruby/object:Gem::Version
128
+ version: '0'
129
+ requirements: []
130
+ rubyforge_project:
131
+ rubygems_version: 2.7.7
132
+ signing_key:
133
+ specification_version: 4
134
+ summary: Access SWI-Prolog engine from ruby
135
+ test_files: []