sxp 1.0.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/AUTHORS +1 -1
- data/README.md +204 -45
- data/UNLICENSE +1 -1
- data/VERSION +1 -1
- data/bin/sxp2json +2 -2
- data/bin/sxp2rdf +2 -2
- data/bin/sxp2xml +2 -2
- data/bin/sxp2yaml +2 -2
- data/lib/sxp/extensions.rb +304 -4
- data/lib/sxp/generator.rb +31 -8
- data/lib/sxp/pair.rb +2 -2
- data/lib/sxp/reader/common_lisp.rb +9 -6
- data/lib/sxp/reader/scheme.rb +7 -7
- data/lib/sxp/reader/sparql.rb +21 -23
- data/lib/sxp/reader.rb +18 -18
- data/lib/sxp.rb +9 -24
- metadata +46 -21
- data/lib/sxp/writer.rb +0 -216
data/lib/sxp/extensions.rb
CHANGED
@@ -1,6 +1,79 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require 'bigdecimal'
|
3
|
+
require 'matrix'
|
4
|
+
require 'time'
|
5
|
+
|
6
|
+
##
|
7
|
+
# Extensions for Ruby's `Object` class.
|
8
|
+
class Object
|
9
|
+
##
|
10
|
+
# Returns the SXP representation of this object.
|
11
|
+
#
|
12
|
+
# @return [String]
|
13
|
+
def to_sxp(**options)
|
14
|
+
to_s.to_json
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
##
|
19
|
+
# Extensions for Ruby's `NilClass` class.
|
20
|
+
class NilClass
|
21
|
+
##
|
22
|
+
# Returns the SXP representation of this object.
|
23
|
+
#
|
24
|
+
# @return [String]
|
25
|
+
def to_sxp(**options)
|
26
|
+
'#n'
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
##
|
31
|
+
# Extensions for Ruby's `FalseClass` class.
|
32
|
+
class FalseClass
|
33
|
+
##
|
34
|
+
# Returns the SXP representation of this object.
|
35
|
+
#
|
36
|
+
# @return [String]
|
37
|
+
def to_sxp(**options)
|
38
|
+
'#f'
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
##
|
43
|
+
# Extensions for Ruby's `TrueClass` class.
|
44
|
+
class TrueClass
|
45
|
+
##
|
46
|
+
# Returns the SXP representation of this object.
|
47
|
+
#
|
48
|
+
# @return [String]
|
49
|
+
def to_sxp(**options)
|
50
|
+
'#t'
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
##
|
55
|
+
# Extensions for Ruby's `String` class.
|
56
|
+
class String
|
57
|
+
##
|
58
|
+
# Returns the SXP representation of this object.
|
59
|
+
#
|
60
|
+
# @return [String]
|
61
|
+
def to_sxp(**options)
|
62
|
+
inspect
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
1
66
|
##
|
2
67
|
# Extensions for Ruby's `Symbol` class.
|
3
68
|
class Symbol
|
69
|
+
##
|
70
|
+
# Returns the SXP representation of this object.
|
71
|
+
#
|
72
|
+
# @return [String]
|
73
|
+
def to_sxp(**options)
|
74
|
+
to_s
|
75
|
+
end
|
76
|
+
|
4
77
|
##
|
5
78
|
# Returns `true` if this is a keyword symbol.
|
6
79
|
#
|
@@ -10,17 +83,244 @@ class Symbol
|
|
10
83
|
end
|
11
84
|
end
|
12
85
|
|
13
|
-
|
86
|
+
##
|
87
|
+
# Extensions for Ruby's `Integer` class.
|
88
|
+
class Integer
|
89
|
+
##
|
90
|
+
# Returns the SXP representation of this object.
|
91
|
+
#
|
92
|
+
# @return [String]
|
93
|
+
def to_sxp(**options)
|
94
|
+
to_s
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
##
|
99
|
+
# Extensions for Ruby's `BigDecimal` class.
|
100
|
+
class BigDecimal
|
101
|
+
##
|
102
|
+
# Returns the SXP representation of this object.
|
103
|
+
#
|
104
|
+
# @return [String]
|
105
|
+
def to_sxp(**options)
|
106
|
+
to_f.to_s
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
##
|
111
|
+
# Extensions for Ruby's `Float` class.
|
112
|
+
class Float
|
113
|
+
##
|
114
|
+
# Returns the SXP representation of this object.
|
115
|
+
#
|
116
|
+
# @return [String]
|
117
|
+
def to_sxp(**options)
|
118
|
+
case
|
119
|
+
when nan? then 'nan.0'
|
120
|
+
when infinite? then (infinite? > 0 ? '+inf.0' : '-inf.0')
|
121
|
+
else to_s
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
##
|
127
|
+
# Extensions for Ruby's `Array` class.
|
128
|
+
class Array
|
129
|
+
##
|
130
|
+
# Returns the SXP representation of this object.
|
131
|
+
#
|
132
|
+
# @return [String]
|
133
|
+
def to_sxp()
|
134
|
+
'(' << map { |x| x.to_sxp(**options) }.join(' ') << ')'
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
##
|
139
|
+
# Extensions for Ruby's `Vector` class.
|
140
|
+
class Vector
|
141
|
+
##
|
142
|
+
# Returns the SXP representation of this object.
|
143
|
+
#
|
144
|
+
# @return [String]
|
145
|
+
def to_sxp(**options)
|
146
|
+
'#(' << to_a.map { |x| x.to_sxp(**options) }.join(' ') << ')'
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
##
|
151
|
+
# Extensions for Ruby's `Hash` class.
|
152
|
+
class Hash
|
153
|
+
##
|
154
|
+
# Returns the SXP representation of this object.
|
155
|
+
#
|
156
|
+
# @return [String]
|
157
|
+
def to_sxp(**options)
|
158
|
+
to_a.to_sxp(**options)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
##
|
163
|
+
# Extensions for Ruby's `Time` class.
|
164
|
+
class Time
|
165
|
+
##
|
166
|
+
# Returns the SXP representation of this object.
|
167
|
+
#
|
168
|
+
# @return [String]
|
169
|
+
def to_sxp(**options)
|
170
|
+
'#@' << (respond_to?(:xmlschema) ? xmlschema : to_i).to_s
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
##
|
175
|
+
# Extensions for Ruby's `Regexp` class.
|
176
|
+
class Regexp
|
177
|
+
##
|
178
|
+
# Returns the SXP representation of this object.
|
179
|
+
#
|
180
|
+
# @return [String]
|
181
|
+
def to_sxp(**options)
|
182
|
+
'#' << inspect
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
14
186
|
begin
|
15
|
-
require 'rdf'
|
187
|
+
require 'rdf' # For SPARQL/RDF
|
16
188
|
|
17
189
|
##
|
18
|
-
# Extensions for
|
190
|
+
# Extensions for Ruby's `Array` class.
|
191
|
+
# These extensions depend on RDF being loaded
|
192
|
+
class Array
|
193
|
+
##
|
194
|
+
# Returns the SXP representation of this object.
|
195
|
+
#
|
196
|
+
# If array is of the form `[:base, uri, ..]`, the base_uri is taken from the second value
|
197
|
+
#
|
198
|
+
# If array is of the form `[:prefix, [..], ..]`, prefixes are taken from the second value
|
199
|
+
#
|
200
|
+
# Prefixes always are terminated by a ':'
|
201
|
+
#
|
202
|
+
# @param [Hash{Symbol => RDF::URI}] prefixes(nil)
|
203
|
+
# @param [RDF::URI] base_uri(nil)
|
204
|
+
# @return [String]
|
205
|
+
def to_sxp(prefixes: nil, base_uri: nil, **options)
|
206
|
+
if self.first == :base && self.length == 3 && self[1].is_a?(RDF::URI)
|
207
|
+
base_uri = self[1]
|
208
|
+
'(' << (
|
209
|
+
self[0,2].map(&:to_sxp) <<
|
210
|
+
self.last.to_sxp(prefixes: prefixes, base_uri: base_uri, **options)
|
211
|
+
).join(' ') << ')'
|
212
|
+
elsif self.first == :prefix && self.length == 3 && self[1].is_a?(Array)
|
213
|
+
prefixes = prefixes ? prefixes.dup : {}
|
214
|
+
self[1].each do |defn|
|
215
|
+
prefixes[defn.first.to_s.chomp(':').to_sym] = RDF::URI(defn.last) if
|
216
|
+
defn.is_a?(Array) && defn.length == 2
|
217
|
+
end
|
218
|
+
pfx_sxp = self[1].map {|(p,s)|["#{p.to_s.chomp(':')}:".to_sym, RDF::URI(s)]}.to_sxp
|
219
|
+
'(' << [
|
220
|
+
:prefix,
|
221
|
+
pfx_sxp,
|
222
|
+
self.last.to_sxp(prefixes: prefixes, base_uri: base_uri, **options)
|
223
|
+
].join(' ') << ')'
|
224
|
+
else
|
225
|
+
'(' << map { |x| x.to_sxp(prefixes: prefixes, base_uri: base_uri, **options) }.join(' ') << ')'
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
19
230
|
class RDF::URI
|
231
|
+
##
|
232
|
+
# Returns the SXP representation of this a URI. Uses Lexical representation, if set, otherwise, any PName match, otherwise, the relativized version of the URI if a base_uri is given, otherwise just the URI.
|
233
|
+
#
|
234
|
+
# @param [Hash{Symbol => RDF::URI}] prefixes(nil)
|
235
|
+
# @param [RDF::URI] base_uri(nil)
|
236
|
+
# @return [String]
|
237
|
+
def to_sxp(prefixes: nil, base_uri: nil, **options)
|
238
|
+
return lexical if lexical
|
239
|
+
pn = pname(prefixes: prefixes || {})
|
240
|
+
return pn unless to_s == pn
|
241
|
+
md = self == base_uri ? '' : self.relativize(base_uri)
|
242
|
+
"<#{md}>"
|
243
|
+
end
|
244
|
+
|
20
245
|
# Original lexical value of this URI to allow for round-trip serialization.
|
21
246
|
def lexical=(value); @lexical = value; end
|
22
247
|
def lexical; @lexical; end
|
23
248
|
end
|
249
|
+
|
250
|
+
class RDF::Node
|
251
|
+
##
|
252
|
+
# Returns the SXP representation of this object.
|
253
|
+
#
|
254
|
+
# @return [String]
|
255
|
+
def to_sxp(**options)
|
256
|
+
to_s
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
class RDF::Literal
|
261
|
+
##
|
262
|
+
# Returns the SXP representation of a Literal.
|
263
|
+
#
|
264
|
+
# @return [String]
|
265
|
+
def to_sxp(**options)
|
266
|
+
case datatype
|
267
|
+
when RDF::XSD.boolean, RDF::XSD.integer, RDF::XSD.double, RDF::XSD.decimal, RDF::XSD.time
|
268
|
+
# Retain stated lexical form if possible
|
269
|
+
valid? ? to_s : object.to_sxp(**options)
|
270
|
+
else
|
271
|
+
text = value.dump
|
272
|
+
text << "@#{language}" if self.has_language?
|
273
|
+
text << "^^#{datatype.to_sxp(**options)}" if self.has_datatype?
|
274
|
+
text
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
class Double
|
279
|
+
##
|
280
|
+
# Returns the SXP representation of this object.
|
281
|
+
#
|
282
|
+
# @return [String]
|
283
|
+
def to_sxp(**options)
|
284
|
+
case
|
285
|
+
when nan? then 'nan.0'
|
286
|
+
when infinite? then (infinite? > 0 ? '+inf.0' : '-inf.0')
|
287
|
+
else canonicalize.to_s.downcase
|
288
|
+
end
|
289
|
+
end
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
class RDF::Query
|
294
|
+
# Transform Query into an Array form of an SXP
|
295
|
+
#
|
296
|
+
# If Query is named, it's treated as a GroupGraphPattern, otherwise, a BGP
|
297
|
+
#
|
298
|
+
# @return [Array]
|
299
|
+
def to_sxp(**options)
|
300
|
+
res = [:bgp] + patterns
|
301
|
+
(named? ? [:graph, graph_name, res] : res).to_sxp(**options)
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
class RDF::Query::Pattern
|
306
|
+
# Transform Query Pattern into an SXP
|
307
|
+
#
|
308
|
+
# @return [String]
|
309
|
+
def to_sxp(**options)
|
310
|
+
[:triple, subject, predicate, object].to_sxp(**options)
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
class RDF::Query::Variable
|
315
|
+
##
|
316
|
+
# Transform Query variable into an SXP.
|
317
|
+
#
|
318
|
+
# @return [String]
|
319
|
+
def to_sxp(**options)
|
320
|
+
prefix = distinguished? ? (existential? ? '$' : '?') : (existential? ? '$$' : '??')
|
321
|
+
unbound? ? "#{prefix}#{name}".to_sym.to_sxp : ["#{prefix}#{name}".to_sym, value].to_sxp
|
322
|
+
end
|
323
|
+
end
|
24
324
|
rescue LoadError
|
25
|
-
# Ignore
|
325
|
+
# Ignore if RDF not loaded
|
26
326
|
end
|
data/lib/sxp/generator.rb
CHANGED
@@ -16,18 +16,41 @@ module SXP
|
|
16
16
|
|
17
17
|
##
|
18
18
|
# @param [Object] obj
|
19
|
-
|
19
|
+
# @param [Integer] indent
|
20
|
+
# @param [Hash{Symbol => RDF::URI}] prefixes (nil)
|
21
|
+
# @param [RDF::URI] base_uri (nil)
|
22
|
+
def initialize(obj, indent, prefixes: nil, base_uri: nil)
|
20
23
|
@indent = indent
|
21
24
|
@elements = []
|
25
|
+
@prefixes = prefixes
|
26
|
+
@base_uri = base_uri
|
22
27
|
if obj.is_a?(Array)
|
23
|
-
|
28
|
+
# If this is a base or prefix element, update our representations
|
29
|
+
if obj.first == :base && obj.length == 3 && obj[1].is_a?(RDF::URI)
|
30
|
+
base_uri = obj[1]
|
31
|
+
@elements << Block.new(:base, indent + 1)
|
32
|
+
@elements << Block.new(obj[1], indent + 1)
|
33
|
+
@elements << Block.new(obj.last, indent + 1, prefixes: prefixes, base_uri: base_uri)
|
34
|
+
elsif obj.first == :prefix && obj.length == 3 && obj[1].is_a?(Array)
|
35
|
+
prefixes = prefixes ? prefixes.dup : {}
|
36
|
+
obj[1].each do |defn|
|
37
|
+
prefixes[defn.first.to_s.chomp(':').to_sym] = RDF::URI(defn.last) if defn.is_a?(Array) && defn.length == 2
|
38
|
+
end
|
39
|
+
@elements << Block.new(:prefix, indent + 1)
|
40
|
+
@elements << Block.new(obj[1], indent + 1)
|
41
|
+
@elements << Block.new(obj.last, indent + 1, prefixes: prefixes, base_uri: base_uri)
|
42
|
+
else
|
43
|
+
obj.compact.each do |o|
|
44
|
+
@elements << Block.new(o, indent + 1, prefixes: prefixes, base_uri: base_uri)
|
45
|
+
end
|
46
|
+
end
|
24
47
|
else
|
25
48
|
@elements = obj
|
26
49
|
end
|
27
50
|
end
|
28
51
|
|
29
52
|
##
|
30
|
-
#
|
53
|
+
# Aggregate length over each element accounting for spaces
|
31
54
|
#
|
32
55
|
# @return [Integer]
|
33
56
|
# If indent is not not nil, returns zero
|
@@ -35,7 +58,7 @@ module SXP
|
|
35
58
|
if @elements.is_a?(Array)
|
36
59
|
@elements.map(&:length).inject(:+).to_i + @elements.length - 1
|
37
60
|
else
|
38
|
-
@elements.to_sxp.length
|
61
|
+
@elements.to_sxp(prefixes: @prefixes, base_uri: @base_uri).length
|
39
62
|
end
|
40
63
|
end
|
41
64
|
|
@@ -44,8 +67,8 @@ module SXP
|
|
44
67
|
# This should only be called on a block when
|
45
68
|
# no indentation is to be applied
|
46
69
|
# @return [String]
|
47
|
-
def to_sxp
|
48
|
-
@elements.to_sxp
|
70
|
+
def to_sxp(prefixes: nil, base_uri: nil)
|
71
|
+
@elements.to_sxp(prefixes: prefixes || @prefixes, base_uri: base_uri || @base_uri)
|
49
72
|
end
|
50
73
|
|
51
74
|
##
|
@@ -67,7 +90,7 @@ module SXP
|
|
67
90
|
first, *elems = @elements
|
68
91
|
unless first.sxp?
|
69
92
|
# It's atomic, write out after paren
|
70
|
-
buffer += first.to_sxp + "\n"
|
93
|
+
buffer += first.to_sxp(prefixes: @prefixes, base_uri: @base_uri) + "\n"
|
71
94
|
else
|
72
95
|
buffer += "\n"
|
73
96
|
elems.unshift(first)
|
@@ -77,7 +100,7 @@ module SXP
|
|
77
100
|
end
|
78
101
|
buffer += do_indent + ")\n"
|
79
102
|
else
|
80
|
-
buffer += do_indent + @elements.to_sxp + "\n"
|
103
|
+
buffer += do_indent + @elements.to_sxp(prefixes: @prefixes, base_uri: @base_uri) + "\n"
|
81
104
|
end
|
82
105
|
buffer
|
83
106
|
end
|
data/lib/sxp/pair.rb
CHANGED
@@ -27,7 +27,7 @@ module SXP
|
|
27
27
|
# Returns `true` if the tail of this pair is not `nil` or another pair.
|
28
28
|
#
|
29
29
|
# @return [Boolean]
|
30
|
-
# @see
|
30
|
+
# @see https:/srfi.schemers.org/srfi-1/srfi-1.html#ImproperLists
|
31
31
|
def dotted?
|
32
32
|
!proper?
|
33
33
|
end
|
@@ -36,7 +36,7 @@ module SXP
|
|
36
36
|
# Returns `true` if the tail of this pair is `nil` or another pair.
|
37
37
|
#
|
38
38
|
# @return [Boolean]
|
39
|
-
# @see
|
39
|
+
# @see https:/srfi.schemers.org/srfi-1/srfi-1.html#ImproperLists
|
40
40
|
def proper?
|
41
41
|
tail.nil? || tail.is_a?(Pair)
|
42
42
|
end
|
@@ -1,9 +1,11 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
|
+
require 'matrix'
|
3
|
+
|
2
4
|
module SXP; class Reader
|
3
5
|
##
|
4
6
|
# A Common Lisp S-expressions parser.
|
5
7
|
#
|
6
|
-
# @see
|
8
|
+
# @see https:/www.cs.cmu.edu/Groups/AI/html/cltl/clm/node14.html
|
7
9
|
class CommonLisp < Basic
|
8
10
|
OPTIONS = {nil: nil, t: true, quote: :quote, function: :function}
|
9
11
|
|
@@ -16,7 +18,7 @@ module SXP; class Reader
|
|
16
18
|
|
17
19
|
# Escape characters, used in the form `#\Backspace`. Case is treated
|
18
20
|
# insensitively
|
19
|
-
# @see
|
21
|
+
# @see https:/www.cs.cmu.edu/Groups/AI/html/cltl/clm/node22.html
|
20
22
|
CHARACTERS = {
|
21
23
|
'newline' => "\n",
|
22
24
|
'space' => " ",
|
@@ -37,8 +39,8 @@ module SXP; class Reader
|
|
37
39
|
# @option options [Object] :t (true)
|
38
40
|
# @option options [Object] :quote (:quote)
|
39
41
|
# @option options [Object] :function (:function)
|
40
|
-
def initialize(input, options
|
41
|
-
super(input, OPTIONS.merge(options), &block)
|
42
|
+
def initialize(input, **options, &block)
|
43
|
+
super(input, **OPTIONS.merge(options), &block)
|
42
44
|
end
|
43
45
|
|
44
46
|
##
|
@@ -88,7 +90,8 @@ module SXP; class Reader
|
|
88
90
|
#
|
89
91
|
# @return [Array]
|
90
92
|
def read_vector
|
91
|
-
|
93
|
+
list = read_list(')')
|
94
|
+
Vector.[](*list)
|
92
95
|
end
|
93
96
|
|
94
97
|
##
|
@@ -114,7 +117,7 @@ module SXP; class Reader
|
|
114
117
|
# eroneously read characters back in the input stream
|
115
118
|
#
|
116
119
|
# @return [String]
|
117
|
-
# @see
|
120
|
+
# @see https:/www.cs.cmu.edu/Groups/AI/html/cltl/clm/node22.html
|
118
121
|
def read_character
|
119
122
|
lit = read_literal
|
120
123
|
|
data/lib/sxp/reader/scheme.rb
CHANGED
@@ -3,7 +3,7 @@ module SXP; class Reader
|
|
3
3
|
##
|
4
4
|
# A Scheme R4RS S-expressions parser.
|
5
5
|
#
|
6
|
-
# @see
|
6
|
+
# @see https:/people.csail.mit.edu/jaffer/r4rs_9.html#SEC65
|
7
7
|
class Scheme < Extended
|
8
8
|
DECIMAL = /^[+-]?(\d*)?\.\d*$/
|
9
9
|
INTEGER_BASE_2 = /^[+-]?[01]+$/
|
@@ -14,7 +14,7 @@ module SXP; class Reader
|
|
14
14
|
|
15
15
|
# Escape characters, used in the form `#\newline`. Case is treated
|
16
16
|
# insensitively
|
17
|
-
# @see
|
17
|
+
# @see https:/people.csail.mit.edu/jaffer/r4rs_9.html#SEC65
|
18
18
|
CHARACTERS = {
|
19
19
|
'newline' => "\n",
|
20
20
|
'space' => " ",
|
@@ -26,8 +26,8 @@ module SXP; class Reader
|
|
26
26
|
# @param [IO, StringIO, String] input
|
27
27
|
# @param [Hash{Symbol => Object}] options
|
28
28
|
# @option options [Symbol] :version (:r4rs)
|
29
|
-
def initialize(input,
|
30
|
-
super(input,
|
29
|
+
def initialize(input, version: :r4rs, **options, &block)
|
30
|
+
super(input, version: version, **options, &block)
|
31
31
|
end
|
32
32
|
|
33
33
|
##
|
@@ -64,8 +64,8 @@ module SXP; class Reader
|
|
64
64
|
when ?d, ?D then read_integer(10)
|
65
65
|
when ?x, ?X then read_integer(16)
|
66
66
|
when ?\\ then read_character
|
67
|
-
when ?; then skip
|
68
|
-
when ?! then skip_line;
|
67
|
+
when ?; then skip # comment character
|
68
|
+
when ?! then skip_line; skip # shebang
|
69
69
|
else raise Error, "invalid sharp-sign read syntax: ##{char.chr}"
|
70
70
|
end
|
71
71
|
end
|
@@ -76,7 +76,7 @@ module SXP; class Reader
|
|
76
76
|
# eroneously read characters back in the input stream
|
77
77
|
#
|
78
78
|
# @return [String]
|
79
|
-
# @see
|
79
|
+
# @see https:/people.csail.mit.edu/jaffer/r4rs_9.html#SEC65
|
80
80
|
def read_character
|
81
81
|
lit = read_literal
|
82
82
|
|
data/lib/sxp/reader/sparql.rb
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
|
-
require 'rdf' # @see
|
2
|
+
require 'rdf' # @see https:/rubygems.org/gems/rdf
|
3
3
|
|
4
4
|
module SXP; class Reader
|
5
5
|
##
|
6
6
|
# A SPARQL Syntax Expressions (SSE) parser.
|
7
7
|
#
|
8
|
-
# Requires [RDF.rb](
|
8
|
+
# Requires [RDF.rb](https:/rubygems.org/gems/rdf/).
|
9
9
|
#
|
10
|
-
# @see
|
10
|
+
# @see https:/openjena.org/wiki/SSE
|
11
11
|
class SPARQL < Extended
|
12
12
|
# Alias for rdf:type
|
13
13
|
A = /^a$/
|
@@ -29,7 +29,11 @@ module SXP; class Reader
|
|
29
29
|
# Distinguished variable
|
30
30
|
VAR_ID = /^\?(.*)/
|
31
31
|
# Non-distinguished variable
|
32
|
-
ND_VAR = /^\?(
|
32
|
+
ND_VAR = /^\?(?:[\?\.])(.*)/
|
33
|
+
# Distinguished existential variable
|
34
|
+
EVAR_ID = /^\$(.*)/
|
35
|
+
# Non-distinguished existential variable
|
36
|
+
ND_EVAR = /^\$(?:[\$\.])(.*)/
|
33
37
|
# A QName, subject to expansion to URIs using {PREFIX}
|
34
38
|
PNAME = /([^:]*):(.*)/
|
35
39
|
|
@@ -67,7 +71,7 @@ module SXP; class Reader
|
|
67
71
|
#
|
68
72
|
# @param [IO, StringIO, String] input
|
69
73
|
# @param [Hash{Symbol => Object}] options
|
70
|
-
def initialize(input, options
|
74
|
+
def initialize(input, **options, &block)
|
71
75
|
super { @prefixes = {}; @bnodes = {}; @list_depth = 0 }
|
72
76
|
|
73
77
|
if block_given?
|
@@ -79,12 +83,12 @@ module SXP; class Reader
|
|
79
83
|
end
|
80
84
|
|
81
85
|
##
|
82
|
-
# Reads SSE Tokens, including
|
86
|
+
# Reads SSE Tokens, including `RDF::Literal`, `RDF::URI` and `RDF::Node`.
|
83
87
|
#
|
84
88
|
# Performs forward reference for prefix and base URI representations and saves in
|
85
89
|
# {#base_uri} and {#prefixes} accessors.
|
86
90
|
#
|
87
|
-
# Transforms tokens matching a {PNAME} pattern into
|
91
|
+
# Transforms tokens matching a {PNAME} pattern into `RDF::URI` instances if a match is
|
88
92
|
# found with a previously identified {PREFIX}.
|
89
93
|
# @return [Object]
|
90
94
|
def read_token
|
@@ -125,8 +129,6 @@ module SXP; class Reader
|
|
125
129
|
uri = RDF::URI(base.to_s + suffix)
|
126
130
|
#STDERR.puts "read_tok lexical uri: #{uri.inspect}"
|
127
131
|
|
128
|
-
# Cause URI to be serialized as a lexical
|
129
|
-
uri.lexical = value
|
130
132
|
[:atom, uri]
|
131
133
|
else
|
132
134
|
tok
|
@@ -158,7 +160,7 @@ module SXP; class Reader
|
|
158
160
|
{datatype: read_token.last}
|
159
161
|
else {}
|
160
162
|
end
|
161
|
-
RDF::Literal(value, options)
|
163
|
+
RDF::Literal(value, **options)
|
162
164
|
end
|
163
165
|
|
164
166
|
##
|
@@ -180,9 +182,7 @@ module SXP; class Reader
|
|
180
182
|
|
181
183
|
# If we have a base URI, use that when constructing a new URI
|
182
184
|
uri = if self.base_uri && RDF::URI(buffer).relative?
|
183
|
-
|
184
|
-
u.lexical = "<#{buffer}>" unless u.to_s == buffer # So that it can be re-serialized properly
|
185
|
-
u
|
185
|
+
self.base_uri.join(buffer)
|
186
186
|
else
|
187
187
|
RDF::URI(buffer)
|
188
188
|
end
|
@@ -207,7 +207,7 @@ module SXP; class Reader
|
|
207
207
|
#
|
208
208
|
# Atoms parsed including `base`, `prefix`, `true`, `false`, numeric, BNodes and variables.
|
209
209
|
#
|
210
|
-
# Creates
|
210
|
+
# Creates `RDF::Literal`, `RDF::Node`, or `RDF::Query::Variable` instances where appropriate.
|
211
211
|
#
|
212
212
|
# @return [Object]
|
213
213
|
def read_atom
|
@@ -223,8 +223,10 @@ module SXP; class Reader
|
|
223
223
|
when INTEGER then RDF::Literal::Integer.new(buffer)
|
224
224
|
when BNODE_ID then @bnodes[$1] ||= RDF::Node($1)
|
225
225
|
when BNODE_NEW then RDF::Node.new
|
226
|
-
when ND_VAR
|
227
|
-
when VAR_ID then variable($1, true)
|
226
|
+
when ND_VAR then variable($1, distinguished: false)
|
227
|
+
when VAR_ID then variable($1, distinguished: true)
|
228
|
+
when ND_EVAR then variable($1, existential: true, distinguished: false)
|
229
|
+
when EVAR_ID then variable($1, existential: true, distinguished: true)
|
228
230
|
else buffer.to_sym
|
229
231
|
end
|
230
232
|
end
|
@@ -251,20 +253,16 @@ module SXP; class Reader
|
|
251
253
|
# is a disinguished or non-distinguished variable. Non-distinguished
|
252
254
|
# variables are effectively the same as BNodes.
|
253
255
|
# @return [RDF::Query::Variable]
|
254
|
-
def variable(id, distinguished
|
256
|
+
def variable(id, distinguished: true, existential: false)
|
255
257
|
id = nil if id.to_s.empty?
|
256
258
|
|
257
259
|
if id
|
258
260
|
@vars ||= {}
|
259
261
|
@vars[id] ||= begin
|
260
|
-
|
261
|
-
v.distinguished = distinguished
|
262
|
-
v
|
262
|
+
RDF::Query::Variable.new(id, distinguished: distinguished, existential: existential)
|
263
263
|
end
|
264
264
|
else
|
265
|
-
|
266
|
-
v.distinguished = distinguished
|
267
|
-
v
|
265
|
+
RDF::Query::Variable.new(distinguished: distinguished, existential: existential)
|
268
266
|
end
|
269
267
|
end
|
270
268
|
end # SPARQL
|