upl 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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: []