sxp 0.0.3 → 0.0.4
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.
- data/VERSION +1 -1
- data/lib/sxp.rb +70 -4
- data/lib/sxp/extensions.rb +5 -1
- data/lib/sxp/generator.rb +57 -29
- data/lib/sxp/list.rb +182 -7
- data/lib/sxp/pair.rb +38 -13
- data/lib/sxp/reader.rb +60 -76
- data/lib/sxp/version.rb +1 -1
- data/lib/sxp/writer.rb +84 -6
- metadata +3 -3
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.4
|
data/lib/sxp.rb
CHANGED
@@ -1,7 +1,73 @@
|
|
1
1
|
require 'sxp/version'
|
2
2
|
require 'sxp/extensions'
|
3
|
-
require 'sxp/pair'
|
4
|
-
require 'sxp/list'
|
5
|
-
require 'sxp/reader'
|
6
3
|
require 'sxp/writer'
|
7
|
-
|
4
|
+
|
5
|
+
module SXP
|
6
|
+
autoload :Pair, 'sxp/pair'
|
7
|
+
autoload :List, 'sxp/list'
|
8
|
+
autoload :Generator, 'sxp/generator'
|
9
|
+
autoload :Reader, 'sxp/reader'
|
10
|
+
|
11
|
+
##
|
12
|
+
# Reads all S-expressions from a given input URI using the HTTP or FTP
|
13
|
+
# protocols.
|
14
|
+
#
|
15
|
+
# @param [String, #to_s] url
|
16
|
+
# @param [Hash{Symbol => Object}] options
|
17
|
+
# @return [Enumerable<Object>]
|
18
|
+
def self.read_url(url, options = {})
|
19
|
+
require 'openuri'
|
20
|
+
open(url.to_s, 'rb', nil, options) { |io| read_all(io, options) }
|
21
|
+
end
|
22
|
+
|
23
|
+
##
|
24
|
+
# Reads all S-expressions from the given input files.
|
25
|
+
#
|
26
|
+
# @param [Enumerable<String>] filenames
|
27
|
+
# @param [Hash{Symbol => Object}] options
|
28
|
+
# @return [Enumerable<Object>]
|
29
|
+
def self.read_files(*filenames)
|
30
|
+
options = filenames.last.is_a?(Hash) ? filenames.pop : {}
|
31
|
+
filenames.map { |filename| read_file(filename, options) }.inject { |sxps, sxp| sxps + sxp }
|
32
|
+
end
|
33
|
+
|
34
|
+
##
|
35
|
+
# Reads all S-expressions from a given input file.
|
36
|
+
#
|
37
|
+
# @param [String, #to_s] filename
|
38
|
+
# @param [Hash{Symbol => Object}] options
|
39
|
+
# @return [Enumerable<Object>]
|
40
|
+
def self.read_file(filename, options = {})
|
41
|
+
File.open(filename.to_s, 'rb') { |io| read_all(io, options) }
|
42
|
+
end
|
43
|
+
|
44
|
+
##
|
45
|
+
# Reads all S-expressions from the given input stream.
|
46
|
+
#
|
47
|
+
# @param [IO, StringIO, String] input
|
48
|
+
# @param [Hash{Symbol => Object}] options
|
49
|
+
# @return [Enumerable<Object>]
|
50
|
+
def self.read_all(input, options = {})
|
51
|
+
Reader.new(input, options).read_all
|
52
|
+
end
|
53
|
+
|
54
|
+
##
|
55
|
+
# Reads one S-expression from the given input stream.
|
56
|
+
#
|
57
|
+
# @param [IO, StringIO, String] input
|
58
|
+
# @param [Hash{Symbol => Object}] options
|
59
|
+
# @return [Object]
|
60
|
+
def self.read(input, options = {})
|
61
|
+
Reader.new(input, options).read
|
62
|
+
end
|
63
|
+
|
64
|
+
class << self
|
65
|
+
alias_method :parse, :read
|
66
|
+
alias_method :parse_all, :read_all
|
67
|
+
alias_method :parse_files, :read_files
|
68
|
+
alias_method :parse_file, :read_file
|
69
|
+
alias_method :parse_url, :read_url
|
70
|
+
alias_method :parse_uri, :read_url # @deprecated
|
71
|
+
alias_method :read_uri, :read_url # @deprecated
|
72
|
+
end
|
73
|
+
end # module SXP
|
data/lib/sxp/extensions.rb
CHANGED
data/lib/sxp/generator.rb
CHANGED
@@ -1,14 +1,26 @@
|
|
1
1
|
module SXP
|
2
|
+
##
|
3
|
+
# An S-expression generator.
|
2
4
|
class Generator
|
5
|
+
##
|
6
|
+
# @param [Array] sxps
|
7
|
+
# @return [Object]
|
3
8
|
def self.string(*sxps)
|
4
9
|
require 'stringio' unless defined?(StringIO)
|
5
10
|
write(StringIO.new, *sxps).instance_variable_get('@buffer').string
|
6
11
|
end
|
7
12
|
|
13
|
+
##
|
14
|
+
# @param [Array] sxps
|
15
|
+
# @return [Object]
|
8
16
|
def self.print(*sxps)
|
9
17
|
write($stdout, *sxps)
|
10
18
|
end
|
11
19
|
|
20
|
+
##
|
21
|
+
# @param [Object] out
|
22
|
+
# @param [Array] sxps
|
23
|
+
# @return [Object]
|
12
24
|
def self.write(out, *sxps)
|
13
25
|
generator = self.new(out)
|
14
26
|
sxps.each do |sxp|
|
@@ -17,6 +29,8 @@ module SXP
|
|
17
29
|
generator
|
18
30
|
end
|
19
31
|
|
32
|
+
##
|
33
|
+
# @param [Object] buffer
|
20
34
|
def initialize(buffer)
|
21
35
|
@output = [@buffer = buffer]
|
22
36
|
@indent = 0
|
@@ -24,39 +38,53 @@ module SXP
|
|
24
38
|
|
25
39
|
protected
|
26
40
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
41
|
+
##
|
42
|
+
# @param [String] text
|
43
|
+
# @param [Hash{Symbol => Object}] options
|
44
|
+
# @return [void]
|
45
|
+
def emit(text, options = {})
|
46
|
+
if out = @output.last
|
47
|
+
out.print(' ' * (indent * 2)) if options[:indent]
|
48
|
+
out.print(text)
|
32
49
|
end
|
50
|
+
end
|
33
51
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
52
|
+
##
|
53
|
+
# @yield
|
54
|
+
# @return [String]
|
55
|
+
def captured(&block)
|
56
|
+
require 'stringio' unless defined?(StringIO)
|
57
|
+
begin
|
58
|
+
@output.push(buffer = StringIO.new)
|
59
|
+
block.call
|
60
|
+
ensure
|
61
|
+
@output.pop
|
43
62
|
end
|
63
|
+
buffer.string
|
64
|
+
end
|
44
65
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
66
|
+
##
|
67
|
+
# @yield
|
68
|
+
# @return [Object]
|
69
|
+
def indented(&block)
|
70
|
+
begin
|
71
|
+
increase_indent!
|
72
|
+
block.call
|
73
|
+
ensure
|
74
|
+
decrease_indent!
|
52
75
|
end
|
76
|
+
end
|
53
77
|
|
54
|
-
|
55
|
-
|
56
|
-
|
78
|
+
##
|
79
|
+
# @return [void]
|
80
|
+
def increase_indent!
|
81
|
+
@indent += 1
|
82
|
+
end
|
57
83
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
end
|
84
|
+
##
|
85
|
+
# @return [void]
|
86
|
+
def decrease_indent!
|
87
|
+
@indent -= 1
|
88
|
+
end
|
89
|
+
end # class Generator
|
90
|
+
end # module SXP
|
data/lib/sxp/list.rb
CHANGED
@@ -1,62 +1,106 @@
|
|
1
1
|
require 'sxp/pair'
|
2
2
|
|
3
3
|
module SXP
|
4
|
+
##
|
5
|
+
# A singly-linked list.
|
4
6
|
class List < Pair
|
5
7
|
include Enumerable
|
6
8
|
|
9
|
+
##
|
10
|
+
# @param [Array] elements
|
11
|
+
# @return [Object]
|
7
12
|
def self.[](*elements)
|
8
13
|
self.new(elements)
|
9
14
|
end
|
10
15
|
|
16
|
+
##
|
17
|
+
# @param [Array] elements
|
18
|
+
# @yield [list]
|
19
|
+
# @yieldparam [List] list
|
11
20
|
def initialize(elements = [], &block)
|
12
21
|
@pair = nil
|
13
22
|
unshift(*elements) unless elements.empty?
|
14
23
|
block.call(self) if block_given?
|
15
24
|
end
|
16
25
|
|
26
|
+
##
|
27
|
+
# @return [String]
|
17
28
|
def inspect
|
18
29
|
"(" << map { |value| value.inspect }.join(' ') << ")"
|
19
30
|
end
|
20
31
|
|
21
|
-
|
22
|
-
|
32
|
+
##
|
33
|
+
# @return [Object]
|
34
|
+
def head
|
35
|
+
first
|
36
|
+
end
|
37
|
+
|
38
|
+
##
|
39
|
+
# @return [Object]
|
40
|
+
def tail
|
41
|
+
rest
|
42
|
+
end
|
23
43
|
|
44
|
+
##
|
45
|
+
# @return [Object]
|
24
46
|
def rest
|
25
47
|
empty? ? false : @pair.tail
|
26
48
|
end
|
27
49
|
|
28
|
-
|
29
|
-
|
50
|
+
##
|
51
|
+
# @param [Object] other
|
52
|
+
# @return [Object]
|
30
53
|
def &(other)
|
31
54
|
self.class.new(self.to_a & other.to_a)
|
32
55
|
end
|
33
56
|
|
57
|
+
##
|
58
|
+
# @param [Object] other
|
59
|
+
# @return [Object]
|
34
60
|
def |(other)
|
35
61
|
self.class.new(self.to_a | other.to_a)
|
36
62
|
end
|
37
63
|
|
64
|
+
##
|
65
|
+
# @param [Object] times
|
66
|
+
# @return [Object]
|
38
67
|
def *(times)
|
39
68
|
result = (self.to_a * times)
|
40
69
|
result.is_a?(Array) ? self.class.new(result) : result
|
41
70
|
end
|
42
71
|
|
72
|
+
##
|
73
|
+
# @param [Object] other
|
74
|
+
# @return [Object]
|
43
75
|
def +(other)
|
44
76
|
self.class.new(self.to_a + other.to_a)
|
45
77
|
end
|
46
78
|
|
79
|
+
##
|
80
|
+
# @param [Object] other
|
81
|
+
# @return [Object]
|
47
82
|
def -(other)
|
48
83
|
self.class.new(self.to_a - other.to_a)
|
49
84
|
end
|
50
85
|
|
86
|
+
##
|
87
|
+
# @param [Object] object
|
88
|
+
# @return [Object]
|
51
89
|
def <<(object)
|
52
90
|
push(object)
|
53
91
|
self
|
54
92
|
end
|
55
93
|
|
94
|
+
##
|
95
|
+
# @param [Object] other
|
96
|
+
# @return [Object]
|
56
97
|
def <=>(other)
|
57
98
|
to_a <=> other.to_a
|
58
99
|
end
|
59
100
|
|
101
|
+
##
|
102
|
+
# @param [Object] other
|
103
|
+
# @return [Object]
|
60
104
|
def ==(other)
|
61
105
|
case other
|
62
106
|
when List
|
@@ -68,56 +112,89 @@ module SXP
|
|
68
112
|
end
|
69
113
|
end
|
70
114
|
|
115
|
+
##
|
116
|
+
# @param [Array] args
|
117
|
+
# @return [Object]
|
71
118
|
def [](*args)
|
72
119
|
result = to_a[*args]
|
73
120
|
result.is_a?(Array) ? self.class.new(result) : result # FIXME
|
74
121
|
end
|
75
122
|
|
123
|
+
##
|
124
|
+
# @param [Array] args
|
125
|
+
# @return [Object]
|
76
126
|
def []=(*args)
|
77
127
|
raise NotImplementedError # TODO
|
78
128
|
end
|
79
129
|
|
130
|
+
##
|
131
|
+
# @param [Object] object
|
132
|
+
# @return [Object]
|
80
133
|
def assoc(object)
|
81
134
|
raise NotImplementedError # TODO
|
82
135
|
end
|
83
136
|
|
137
|
+
##
|
138
|
+
# @param [Integer] index
|
139
|
+
# @return [Object]
|
84
140
|
def at(index)
|
85
141
|
to_a.at(index)
|
86
142
|
end
|
87
143
|
|
144
|
+
##
|
145
|
+
# @return [Object]
|
88
146
|
def clear
|
89
147
|
@pair = nil
|
90
148
|
self
|
91
149
|
end
|
92
150
|
|
151
|
+
##
|
152
|
+
# @return [Object]
|
93
153
|
def collect!(&block)
|
94
154
|
raise NotImplementedError # TODO
|
95
155
|
end
|
96
156
|
|
157
|
+
##
|
158
|
+
# @return [Object]
|
97
159
|
def compact
|
98
160
|
self.class.new(to_a.compact)
|
99
161
|
end
|
100
162
|
|
163
|
+
##
|
164
|
+
# @return [Object]
|
101
165
|
def compact!
|
102
166
|
raise NotImplementedError # TODO
|
103
167
|
end
|
104
168
|
|
169
|
+
##
|
170
|
+
# @param [Object] other
|
171
|
+
# @return [Object]
|
105
172
|
def concat(other)
|
106
173
|
raise NotImplementedError # TODO
|
107
174
|
end
|
108
175
|
|
176
|
+
##
|
177
|
+
# @param [Object] object
|
178
|
+
# @return [Object]
|
109
179
|
def delete(object, &block)
|
110
180
|
raise NotImplementedError # TODO
|
111
181
|
end
|
112
182
|
|
183
|
+
##
|
184
|
+
# @param [Integer] index
|
185
|
+
# @return [Object]
|
113
186
|
def delete_at(index)
|
114
187
|
raise NotImplementedError # TODO
|
115
188
|
end
|
116
189
|
|
190
|
+
##
|
191
|
+
# @return [Object]
|
117
192
|
def delete_if(&block)
|
118
193
|
raise NotImplementedError # TODO
|
119
194
|
end
|
120
195
|
|
196
|
+
##
|
197
|
+
# @return [Enumerator]
|
121
198
|
def each(&block)
|
122
199
|
pair = @pair
|
123
200
|
while pair != nil
|
@@ -127,19 +204,25 @@ module SXP
|
|
127
204
|
self
|
128
205
|
end
|
129
206
|
|
207
|
+
##
|
208
|
+
# @return [Enumerator]
|
130
209
|
def each_index(&block)
|
131
210
|
index = 0
|
132
211
|
each do
|
133
212
|
block.call(index)
|
134
213
|
index += 1
|
135
214
|
end
|
136
|
-
self
|
137
215
|
end
|
138
216
|
|
217
|
+
##
|
218
|
+
# @return [Boolean]
|
139
219
|
def empty?
|
140
220
|
@pair.nil?
|
141
221
|
end
|
142
222
|
|
223
|
+
##
|
224
|
+
# @param [Object] other
|
225
|
+
# @return [Boolean]
|
143
226
|
def eql?(other)
|
144
227
|
case other
|
145
228
|
when self then true
|
@@ -148,14 +231,23 @@ module SXP
|
|
148
231
|
end
|
149
232
|
end
|
150
233
|
|
234
|
+
##
|
235
|
+
# @param [Array] args
|
236
|
+
# @return [Object]
|
151
237
|
def fetch(*args, &block)
|
152
238
|
to_a.fetch(*args, &block)
|
153
239
|
end
|
154
240
|
|
241
|
+
##
|
242
|
+
# @param [Array] args
|
243
|
+
# @return [Object]
|
155
244
|
def fill(*args, &block)
|
156
245
|
raise NotImplementedError # TODO
|
157
246
|
end
|
158
247
|
|
248
|
+
##
|
249
|
+
# @param [Integer] count
|
250
|
+
# @return [Object]
|
159
251
|
def first(count = nil)
|
160
252
|
case
|
161
253
|
when count.nil?
|
@@ -167,30 +259,50 @@ module SXP
|
|
167
259
|
end
|
168
260
|
end
|
169
261
|
|
262
|
+
##
|
263
|
+
# @return [Object]
|
170
264
|
def flatten
|
171
265
|
raise NotImplementedError # TODO
|
172
266
|
end
|
173
267
|
|
268
|
+
##
|
269
|
+
# @return [Object]
|
174
270
|
def flatten!
|
175
271
|
raise NotImplementedError # TODO
|
176
272
|
end
|
177
273
|
|
274
|
+
##
|
275
|
+
# @param [Object] object
|
276
|
+
# @return [Object]
|
178
277
|
def include?(object)
|
179
278
|
to_a.include?(object)
|
180
279
|
end
|
181
280
|
|
281
|
+
##
|
282
|
+
# @param [Object] object
|
283
|
+
# @return [Object]
|
182
284
|
def index(object)
|
183
285
|
to_a.index(object)
|
184
286
|
end
|
185
287
|
|
288
|
+
##
|
289
|
+
# @param [Integer] index
|
290
|
+
# @param [Array] objects
|
291
|
+
# @return [Object]
|
186
292
|
def insert(index, *objects)
|
187
293
|
raise NotImplementedError # TODO
|
188
294
|
end
|
189
295
|
|
296
|
+
##
|
297
|
+
# @param [String] separator
|
298
|
+
# @return [Object]
|
190
299
|
def join(separator = $,)
|
191
300
|
to_a.join(separator)
|
192
301
|
end
|
193
302
|
|
303
|
+
##
|
304
|
+
# @param [Integer] count
|
305
|
+
# @return [Object]
|
194
306
|
def last(count = nil)
|
195
307
|
case
|
196
308
|
when count.nil?
|
@@ -200,38 +312,60 @@ module SXP
|
|
200
312
|
end
|
201
313
|
end
|
202
314
|
|
315
|
+
##
|
316
|
+
# @return [Integer]
|
203
317
|
def length
|
204
318
|
@length ||= to_a.length
|
205
319
|
end
|
206
320
|
|
321
|
+
##
|
322
|
+
# @return [Object]
|
207
323
|
def map!(&block)
|
208
324
|
collect!(&block)
|
209
325
|
end
|
210
326
|
|
327
|
+
##
|
328
|
+
# @return [Integer]
|
211
329
|
def nitems
|
212
330
|
to_a.nitems
|
213
331
|
end
|
214
332
|
|
333
|
+
##
|
334
|
+
# @param [Object] template
|
335
|
+
# @return [Object]
|
215
336
|
def pack(template)
|
216
337
|
to_a.pack(template)
|
217
338
|
end
|
218
339
|
|
340
|
+
##
|
341
|
+
# @return [Object]
|
219
342
|
def pop
|
220
343
|
raise NotImplementedError # TODO
|
221
344
|
end
|
222
345
|
|
346
|
+
##
|
347
|
+
# @param [Array] objects
|
348
|
+
# @return [Object]
|
223
349
|
def push(*objects)
|
224
350
|
raise NotImplementedError # TODO
|
225
351
|
end
|
226
352
|
|
353
|
+
##
|
354
|
+
# @param [Object] key
|
355
|
+
# @return [Object]
|
227
356
|
def rassoc(key)
|
228
357
|
raise NotImplementedError # TODO
|
229
358
|
end
|
230
359
|
|
360
|
+
##
|
361
|
+
# @return [Object]
|
231
362
|
def reject!(&block)
|
232
363
|
raise NotImplementedError # TODO
|
233
364
|
end
|
234
365
|
|
366
|
+
##
|
367
|
+
# @param [Object] other_list
|
368
|
+
# @return [Object]
|
235
369
|
def replace(other_list)
|
236
370
|
case other_list
|
237
371
|
when Pair
|
@@ -247,72 +381,110 @@ module SXP
|
|
247
381
|
self
|
248
382
|
end
|
249
383
|
|
384
|
+
##
|
385
|
+
# @return [Object]
|
250
386
|
def reverse
|
251
387
|
self.class.new(to_a.reverse)
|
252
388
|
end
|
253
389
|
|
390
|
+
##
|
391
|
+
# @return [Object]
|
254
392
|
def reverse!
|
255
393
|
raise NotImplementedError # TODO
|
256
394
|
end
|
257
395
|
|
396
|
+
##
|
397
|
+
# @return [Object]
|
258
398
|
def reverse_each(&block)
|
259
399
|
to_a.reverse_each(&block)
|
260
400
|
self
|
261
401
|
end
|
262
402
|
|
403
|
+
##
|
404
|
+
# @param [Object] object
|
405
|
+
# @return [Object]
|
263
406
|
def rindex(object)
|
264
407
|
to_a.rindex(object)
|
265
408
|
end
|
266
409
|
|
410
|
+
##
|
411
|
+
# @return [Object]
|
267
412
|
def shift
|
268
413
|
raise NotImplementedError # TODO
|
269
414
|
end
|
270
415
|
|
416
|
+
##
|
417
|
+
# @return [Integer]
|
271
418
|
def size
|
272
419
|
length
|
273
420
|
end
|
274
421
|
|
422
|
+
##
|
423
|
+
# @param [Array] args
|
424
|
+
# @return [Object]
|
275
425
|
def slice(*args)
|
276
426
|
self[*args]
|
277
427
|
end
|
278
428
|
|
429
|
+
##
|
430
|
+
# @param [Array] args
|
431
|
+
# @return [Object]
|
279
432
|
def slice!(*args)
|
280
433
|
raise NotImplementedError # TODO
|
281
434
|
end
|
282
435
|
|
436
|
+
##
|
437
|
+
# @return [Object]
|
283
438
|
def sort(&block)
|
284
439
|
(array = to_a).sort!(&block)
|
285
440
|
self.class.new(array)
|
286
441
|
end
|
287
442
|
|
443
|
+
##
|
444
|
+
# @return [Object]
|
288
445
|
def sort!
|
289
446
|
raise NotImplementedError # TODO
|
290
447
|
end
|
291
448
|
|
449
|
+
##
|
450
|
+
# @return [List]
|
292
451
|
def to_list
|
293
452
|
self
|
294
453
|
end
|
295
454
|
|
455
|
+
##
|
456
|
+
# @return [Pair]
|
296
457
|
def to_pair
|
297
458
|
@pair
|
298
459
|
end
|
299
460
|
|
461
|
+
##
|
462
|
+
# @return [String]
|
300
463
|
def to_s
|
301
464
|
join
|
302
465
|
end
|
303
466
|
|
467
|
+
##
|
468
|
+
# @return [Object]
|
304
469
|
def transpose
|
305
470
|
self.class.new(to_a.transpose)
|
306
471
|
end
|
307
472
|
|
473
|
+
##
|
474
|
+
# @return [Object]
|
308
475
|
def uniq
|
309
476
|
self.class.new(to_a.uniq)
|
310
477
|
end
|
311
478
|
|
479
|
+
##
|
480
|
+
# @return [Object]
|
312
481
|
def uniq!
|
313
482
|
raise NotImplementedError # TODO
|
314
483
|
end
|
315
484
|
|
485
|
+
##
|
486
|
+
# @param [Array] objects
|
487
|
+
# @return [Object]
|
316
488
|
def unshift(*objects)
|
317
489
|
objects.reverse_each do |object|
|
318
490
|
@pair = Pair.new(object, @pair)
|
@@ -320,8 +492,11 @@ module SXP
|
|
320
492
|
self
|
321
493
|
end
|
322
494
|
|
495
|
+
##
|
496
|
+
# @param [Array] selector
|
497
|
+
# @return [Object]
|
323
498
|
def values_at(*selector)
|
324
499
|
self.class.new(to_a.values_at(*selector))
|
325
500
|
end
|
326
|
-
end
|
327
|
-
end
|
501
|
+
end # class List
|
502
|
+
end # module SXP
|
data/lib/sxp/pair.rb
CHANGED
@@ -1,39 +1,64 @@
|
|
1
1
|
module SXP
|
2
|
+
##
|
2
3
|
class Pair
|
4
|
+
# @return [Object]
|
3
5
|
attr_accessor :head
|
6
|
+
|
7
|
+
# @return [Object]
|
4
8
|
attr_accessor :tail
|
5
9
|
|
10
|
+
##
|
11
|
+
# @param [Object] head
|
12
|
+
# @param [Object] tail
|
6
13
|
def initialize(head = nil, tail = nil)
|
7
14
|
@head, @tail = head, tail
|
8
15
|
end
|
9
16
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
else
|
15
|
-
"(#{head.inspect} . #{tail.inspect})"
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
17
|
+
##
|
18
|
+
# Returns `true` if the head and tail of this pair are both `nil`.
|
19
|
+
#
|
20
|
+
# @return [Boolean]
|
19
21
|
def empty?
|
20
22
|
head.nil? && tail.nil?
|
21
23
|
end
|
22
24
|
|
23
25
|
##
|
24
|
-
#
|
26
|
+
# Returns `true` if the tail of this pair is not `nil` or another pair.
|
27
|
+
#
|
28
|
+
# @return [Boolean]
|
29
|
+
# @see http://srfi.schemers.org/srfi-1/srfi-1.html#ImproperLists
|
25
30
|
def dotted?
|
26
31
|
!proper?
|
27
32
|
end
|
28
33
|
|
29
34
|
##
|
30
|
-
#
|
35
|
+
# Returns `true` if the tail of this pair is `nil` or another pair.
|
36
|
+
#
|
37
|
+
# @return [Boolean]
|
38
|
+
# @see http://srfi.schemers.org/srfi-1/srfi-1.html#ImproperLists
|
31
39
|
def proper?
|
32
40
|
tail.nil? || tail.is_a?(Pair)
|
33
41
|
end
|
34
42
|
|
43
|
+
##
|
44
|
+
# Returns an array representation of this pair.
|
45
|
+
#
|
46
|
+
# @return [Array]
|
35
47
|
def to_a
|
36
48
|
[head, tail]
|
37
49
|
end
|
38
|
-
|
39
|
-
|
50
|
+
|
51
|
+
##
|
52
|
+
# Returns a developer-friendly representation of this pair.
|
53
|
+
#
|
54
|
+
# @return [String]
|
55
|
+
def inspect
|
56
|
+
case
|
57
|
+
when tail.nil?
|
58
|
+
"(#{head.inspect})"
|
59
|
+
else
|
60
|
+
"(#{head.inspect} . #{tail.inspect})"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end # class Pair
|
64
|
+
end # module SXP
|
data/lib/sxp/reader.rb
CHANGED
@@ -1,67 +1,6 @@
|
|
1
1
|
module SXP
|
2
2
|
##
|
3
|
-
#
|
4
|
-
# protocols.
|
5
|
-
#
|
6
|
-
# @param [String, #to_s] url
|
7
|
-
# @param [Hash{Symbol => Object}] options
|
8
|
-
# @return [Enumerable<Object>]
|
9
|
-
def self.read_url(url, options = {})
|
10
|
-
require 'openuri'
|
11
|
-
open(url.to_s, 'rb', nil, options) { |io| read_all(io, options) }
|
12
|
-
end
|
13
|
-
|
14
|
-
##
|
15
|
-
# Reads all S-expressions from the given input files.
|
16
|
-
#
|
17
|
-
# @param [Enumerable<String>] filenames
|
18
|
-
# @param [Hash{Symbol => Object}] options
|
19
|
-
# @return [Enumerable<Object>]
|
20
|
-
def self.read_files(*filenames)
|
21
|
-
options = filenames.last.is_a?(Hash) ? filenames.pop : {}
|
22
|
-
filenames.map { |filename| read_file(filename, options) }.inject { |sxps, sxp| sxps + sxp }
|
23
|
-
end
|
24
|
-
|
25
|
-
##
|
26
|
-
# Reads all S-expressions from a given input file.
|
27
|
-
#
|
28
|
-
# @param [String, #to_s] filename
|
29
|
-
# @param [Hash{Symbol => Object}] options
|
30
|
-
# @return [Enumerable<Object>]
|
31
|
-
def self.read_file(filename, options = {})
|
32
|
-
File.open(filename.to_s, 'rb') { |io| read_all(io, options) }
|
33
|
-
end
|
34
|
-
|
35
|
-
##
|
36
|
-
# Reads all S-expressions from the given input stream.
|
37
|
-
#
|
38
|
-
# @param [IO, StringIO, String] input
|
39
|
-
# @param [Hash{Symbol => Object}] options
|
40
|
-
# @return [Enumerable<Object>]
|
41
|
-
def self.read_all(input, options = {})
|
42
|
-
Reader.new(input, options).read_all
|
43
|
-
end
|
44
|
-
|
45
|
-
##
|
46
|
-
# Reads one S-expression from the given input stream.
|
47
|
-
#
|
48
|
-
# @param [IO, StringIO, String] input
|
49
|
-
# @param [Hash{Symbol => Object}] options
|
50
|
-
# @return [Object]
|
51
|
-
def self.read(input, options = {})
|
52
|
-
Reader.new(input, options).read
|
53
|
-
end
|
54
|
-
|
55
|
-
class << self
|
56
|
-
alias_method :parse, :read
|
57
|
-
alias_method :parse_all, :read_all
|
58
|
-
alias_method :parse_files, :read_files
|
59
|
-
alias_method :parse_file, :read_file
|
60
|
-
alias_method :parse_url, :read_url
|
61
|
-
alias_method :parse_uri, :read_url # @deprecated
|
62
|
-
alias_method :read_uri, :read_url # @deprecated
|
63
|
-
end
|
64
|
-
|
3
|
+
# The base class for S-expression parsers.
|
65
4
|
class Reader
|
66
5
|
include Enumerable
|
67
6
|
|
@@ -76,8 +15,12 @@ module SXP
|
|
76
15
|
RATIONAL = /^([+-]?\d+)\/(\d+)$/
|
77
16
|
ATOM = /^[^\s()\[\]]+/
|
78
17
|
|
18
|
+
# @return [Object]
|
79
19
|
attr_reader :input
|
80
20
|
|
21
|
+
##
|
22
|
+
# @param [Object] input
|
23
|
+
# @param [Hash{Symbol => Object}] options
|
81
24
|
def initialize(input, options = {})
|
82
25
|
case
|
83
26
|
when [:getc, :ungetc, :eof?].all? { |x| input.respond_to?(x) }
|
@@ -90,10 +33,17 @@ module SXP
|
|
90
33
|
end
|
91
34
|
end
|
92
35
|
|
36
|
+
##
|
37
|
+
# @yield [object]
|
38
|
+
# @yieldparam [Object] object
|
39
|
+
# @return [Enumerator]
|
93
40
|
def each(&block)
|
94
|
-
block.call(read)
|
41
|
+
block.call(read) if block_given? # FIXME
|
95
42
|
end
|
96
43
|
|
44
|
+
##
|
45
|
+
# @param [Hash{Symbol => Object}] options
|
46
|
+
# @return [Array]
|
97
47
|
def read_all(options = {})
|
98
48
|
list = []
|
99
49
|
catch (:eof) do
|
@@ -102,6 +52,9 @@ module SXP
|
|
102
52
|
list
|
103
53
|
end
|
104
54
|
|
55
|
+
##
|
56
|
+
# @param [Hash{Symbol => Object}] options
|
57
|
+
# @return [Object]
|
105
58
|
def read(options = {})
|
106
59
|
skip_comments
|
107
60
|
token, value = read_token
|
@@ -122,6 +75,8 @@ module SXP
|
|
122
75
|
|
123
76
|
alias_method :skip, :read
|
124
77
|
|
78
|
+
##
|
79
|
+
# @return [Object]
|
125
80
|
def read_token
|
126
81
|
case peek_char
|
127
82
|
when nil then :eof
|
@@ -133,6 +88,8 @@ module SXP
|
|
133
88
|
end
|
134
89
|
end
|
135
90
|
|
91
|
+
##
|
92
|
+
# @param [Array]
|
136
93
|
def read_list
|
137
94
|
list = []
|
138
95
|
catch (:eol) do
|
@@ -141,23 +98,28 @@ module SXP
|
|
141
98
|
list
|
142
99
|
end
|
143
100
|
|
101
|
+
##
|
102
|
+
# @return [Object]
|
144
103
|
def read_sharp
|
145
104
|
skip_char # '#'
|
146
105
|
case char = read_char
|
147
|
-
when ?n
|
148
|
-
when ?f
|
149
|
-
when ?t
|
150
|
-
when ?b
|
151
|
-
when ?o
|
152
|
-
when ?d
|
153
|
-
when ?x
|
106
|
+
when ?n then nil
|
107
|
+
when ?f then false
|
108
|
+
when ?t then true
|
109
|
+
when ?b then read_integer(2)
|
110
|
+
when ?o then read_integer(8)
|
111
|
+
when ?d then read_integer(10)
|
112
|
+
when ?x then read_integer(16)
|
154
113
|
when ?\\ then read_character
|
155
|
-
when ?;
|
156
|
-
when ?!
|
114
|
+
when ?; then skip; read
|
115
|
+
when ?! then skip_line; read # shebang
|
157
116
|
else raise Error, "invalid sharp-sign read syntax: ##{char.chr}"
|
158
117
|
end
|
159
118
|
end
|
160
119
|
|
120
|
+
##
|
121
|
+
# @param [Integer] base
|
122
|
+
# @return [Integer]
|
161
123
|
def read_integer(base = 10)
|
162
124
|
case buffer = read_literal
|
163
125
|
when self.class.const_get(:"INTEGER_BASE_#{base}")
|
@@ -166,6 +128,8 @@ module SXP
|
|
166
128
|
end
|
167
129
|
end
|
168
130
|
|
131
|
+
##
|
132
|
+
# @return [Object]
|
169
133
|
def read_atom
|
170
134
|
case buffer = read_literal
|
171
135
|
when '.' then buffer.to_sym
|
@@ -176,6 +140,8 @@ module SXP
|
|
176
140
|
end
|
177
141
|
end
|
178
142
|
|
143
|
+
##
|
144
|
+
# @return [String]
|
179
145
|
def read_string
|
180
146
|
buffer = String.new
|
181
147
|
skip_char # '"'
|
@@ -190,6 +156,8 @@ module SXP
|
|
190
156
|
buffer
|
191
157
|
end
|
192
158
|
|
159
|
+
##
|
160
|
+
# @return [String]
|
193
161
|
def read_character
|
194
162
|
case char = read_char
|
195
163
|
when ?b then ?\b
|
@@ -203,12 +171,16 @@ module SXP
|
|
203
171
|
end
|
204
172
|
end
|
205
173
|
|
174
|
+
##
|
175
|
+
# @return [String]
|
206
176
|
def read_literal
|
207
177
|
buffer = String.new
|
208
178
|
buffer << read_char while !eof? && peek_char.chr =~ ATOM
|
209
179
|
buffer
|
210
180
|
end
|
211
181
|
|
182
|
+
##
|
183
|
+
# @return [void]
|
212
184
|
def skip_comments
|
213
185
|
until eof?
|
214
186
|
case (char = peek_char).chr
|
@@ -219,18 +191,25 @@ module SXP
|
|
219
191
|
end
|
220
192
|
end
|
221
193
|
|
194
|
+
##
|
195
|
+
# @param [Integer] count
|
196
|
+
# @return [String]
|
222
197
|
def read_chars(count = 1)
|
223
198
|
buffer = ''
|
224
199
|
count.times { buffer << read_char.chr }
|
225
200
|
buffer
|
226
201
|
end
|
227
202
|
|
203
|
+
##
|
204
|
+
# @return [String]
|
228
205
|
def read_char
|
229
206
|
char = @input.getc
|
230
207
|
raise EOF, 'unexpected end of input' if char.nil?
|
231
208
|
char
|
232
209
|
end
|
233
210
|
|
211
|
+
##
|
212
|
+
# @return [void]
|
234
213
|
def skip_line
|
235
214
|
loop do
|
236
215
|
break if eof? || read_char.chr == $/
|
@@ -239,13 +218,18 @@ module SXP
|
|
239
218
|
|
240
219
|
alias_method :skip_char, :read_char
|
241
220
|
|
221
|
+
##
|
222
|
+
# @return [String]
|
242
223
|
def peek_char
|
243
224
|
char = @input.getc
|
244
225
|
@input.ungetc char unless char.nil?
|
245
226
|
char
|
246
227
|
end
|
247
228
|
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
229
|
+
##
|
230
|
+
# @return [Boolean]
|
231
|
+
def eof?
|
232
|
+
@input.eof?
|
233
|
+
end
|
234
|
+
end # class Reader
|
235
|
+
end # module SXP
|
data/lib/sxp/version.rb
CHANGED
data/lib/sxp/writer.rb
CHANGED
@@ -1,34 +1,94 @@
|
|
1
|
+
##
|
2
|
+
# Extensions for Ruby's `Object` class.
|
1
3
|
class Object
|
4
|
+
##
|
5
|
+
# Returns the SXP representation of this object.
|
6
|
+
#
|
7
|
+
# @return [String]
|
2
8
|
def to_sxp
|
3
9
|
to_s.to_json
|
4
10
|
end
|
5
11
|
end
|
6
12
|
|
13
|
+
##
|
14
|
+
# Extensions for Ruby's `NilClass` class.
|
7
15
|
class NilClass
|
8
|
-
|
16
|
+
##
|
17
|
+
# Returns the SXP representation of this object.
|
18
|
+
#
|
19
|
+
# @return [String]
|
20
|
+
def to_sxp
|
21
|
+
'#n'
|
22
|
+
end
|
9
23
|
end
|
10
24
|
|
25
|
+
##
|
26
|
+
# Extensions for Ruby's `FalseClass` class.
|
11
27
|
class FalseClass
|
12
|
-
|
28
|
+
##
|
29
|
+
# Returns the SXP representation of this object.
|
30
|
+
#
|
31
|
+
# @return [String]
|
32
|
+
def to_sxp
|
33
|
+
'#f'
|
34
|
+
end
|
13
35
|
end
|
14
36
|
|
37
|
+
##
|
38
|
+
# Extensions for Ruby's `TrueClass` class.
|
15
39
|
class TrueClass
|
16
|
-
|
40
|
+
##
|
41
|
+
# Returns the SXP representation of this object.
|
42
|
+
#
|
43
|
+
# @return [String]
|
44
|
+
def to_sxp
|
45
|
+
'#t'
|
46
|
+
end
|
17
47
|
end
|
18
48
|
|
49
|
+
##
|
50
|
+
# Extensions for Ruby's `String` class.
|
19
51
|
class String
|
20
|
-
|
52
|
+
##
|
53
|
+
# Returns the SXP representation of this object.
|
54
|
+
#
|
55
|
+
# @return [String]
|
56
|
+
def to_sxp
|
57
|
+
inspect
|
58
|
+
end
|
21
59
|
end
|
22
60
|
|
61
|
+
##
|
62
|
+
# Extensions for Ruby's `Symbol` class.
|
23
63
|
class Symbol
|
24
|
-
|
64
|
+
##
|
65
|
+
# Returns the SXP representation of this object.
|
66
|
+
#
|
67
|
+
# @return [String]
|
68
|
+
def to_sxp
|
69
|
+
to_s
|
70
|
+
end
|
25
71
|
end
|
26
72
|
|
73
|
+
##
|
74
|
+
# Extensions for Ruby's `Integer` class.
|
27
75
|
class Integer
|
28
|
-
|
76
|
+
##
|
77
|
+
# Returns the SXP representation of this object.
|
78
|
+
#
|
79
|
+
# @return [String]
|
80
|
+
def to_sxp
|
81
|
+
to_s
|
82
|
+
end
|
29
83
|
end
|
30
84
|
|
85
|
+
##
|
86
|
+
# Extensions for Ruby's `Float` class.
|
31
87
|
class Float
|
88
|
+
##
|
89
|
+
# Returns the SXP representation of this object.
|
90
|
+
#
|
91
|
+
# @return [String]
|
32
92
|
def to_sxp
|
33
93
|
case
|
34
94
|
when nan? then 'nan.'
|
@@ -38,19 +98,37 @@ class Float
|
|
38
98
|
end
|
39
99
|
end
|
40
100
|
|
101
|
+
##
|
102
|
+
# Extensions for Ruby's `Array` class.
|
41
103
|
class Array
|
104
|
+
##
|
105
|
+
# Returns the SXP representation of this object.
|
106
|
+
#
|
107
|
+
# @return [String]
|
42
108
|
def to_sxp
|
43
109
|
'(' << map { |x| x.to_sxp }.join(' ') << ')'
|
44
110
|
end
|
45
111
|
end
|
46
112
|
|
113
|
+
##
|
114
|
+
# Extensions for Ruby's `Time` class.
|
47
115
|
class Time
|
116
|
+
##
|
117
|
+
# Returns the SXP representation of this object.
|
118
|
+
#
|
119
|
+
# @return [String]
|
48
120
|
def to_sxp
|
49
121
|
'#@' << (respond_to?(:xmlschema) ? xmlschema : to_i).to_s
|
50
122
|
end
|
51
123
|
end
|
52
124
|
|
125
|
+
##
|
126
|
+
# Extensions for Ruby's `Regexp` class.
|
53
127
|
class Regexp
|
128
|
+
##
|
129
|
+
# Returns the SXP representation of this object.
|
130
|
+
#
|
131
|
+
# @return [String]
|
54
132
|
def to_sxp
|
55
133
|
'#' << inspect
|
56
134
|
end
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
version: 0.0.
|
8
|
+
- 4
|
9
|
+
version: 0.0.4
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Arto Bendiken
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-05-
|
17
|
+
date: 2010-05-25 00:00:00 +02:00
|
18
18
|
default_executable: sxp2rdf
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|