sxp 0.0.4 → 0.0.5
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 +20 -3
- data/lib/sxp/reader.rb +52 -83
- data/lib/sxp/reader/basic.rb +76 -0
- data/lib/sxp/reader/extended.rb +30 -0
- data/lib/sxp/reader/scheme.rb +52 -0
- data/lib/sxp/version.rb +2 -3
- metadata +16 -13
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.5
|
data/lib/sxp.rb
CHANGED
@@ -1,3 +1,20 @@
|
|
1
|
+
require 'rational'
|
2
|
+
require 'stringio'
|
3
|
+
|
4
|
+
if RUBY_VERSION < '1.8.7'
|
5
|
+
# @see http://rubygems.org/gems/backports
|
6
|
+
begin
|
7
|
+
require 'backports/1.8.7'
|
8
|
+
rescue LoadError
|
9
|
+
begin
|
10
|
+
require 'rubygems'
|
11
|
+
require 'backports/1.8.7'
|
12
|
+
rescue LoadError
|
13
|
+
abort "SXP.rb requires Ruby 1.8.7 or the Backports gem (hint: `gem install backports')."
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
1
18
|
require 'sxp/version'
|
2
19
|
require 'sxp/extensions'
|
3
20
|
require 'sxp/writer'
|
@@ -16,7 +33,7 @@ module SXP
|
|
16
33
|
# @param [Hash{Symbol => Object}] options
|
17
34
|
# @return [Enumerable<Object>]
|
18
35
|
def self.read_url(url, options = {})
|
19
|
-
require '
|
36
|
+
require 'open-uri'
|
20
37
|
open(url.to_s, 'rb', nil, options) { |io| read_all(io, options) }
|
21
38
|
end
|
22
39
|
|
@@ -48,7 +65,7 @@ module SXP
|
|
48
65
|
# @param [Hash{Symbol => Object}] options
|
49
66
|
# @return [Enumerable<Object>]
|
50
67
|
def self.read_all(input, options = {})
|
51
|
-
Reader.new(input, options).read_all
|
68
|
+
Reader::Scheme.new(input, options).read_all
|
52
69
|
end
|
53
70
|
|
54
71
|
##
|
@@ -58,7 +75,7 @@ module SXP
|
|
58
75
|
# @param [Hash{Symbol => Object}] options
|
59
76
|
# @return [Object]
|
60
77
|
def self.read(input, options = {})
|
61
|
-
Reader.new(input, options).read
|
78
|
+
Reader::Scheme.new(input, options).read
|
62
79
|
end
|
63
80
|
|
64
81
|
class << self
|
data/lib/sxp/reader.rb
CHANGED
@@ -2,34 +2,42 @@ module SXP
|
|
2
2
|
##
|
3
3
|
# The base class for S-expression parsers.
|
4
4
|
class Reader
|
5
|
-
|
5
|
+
autoload :Basic, 'sxp/reader/basic'
|
6
|
+
autoload :Extended, 'sxp/reader/extended'
|
7
|
+
autoload :Scheme, 'sxp/reader/scheme'
|
6
8
|
|
7
9
|
class Error < StandardError; end
|
8
10
|
class EOF < Error; end
|
9
11
|
|
10
|
-
|
11
|
-
INTEGER_BASE_2 = /^[+-]?[01]+$/
|
12
|
-
INTEGER_BASE_8 = /^[+-]?[0-7]+$/
|
13
|
-
INTEGER_BASE_10 = /^[+-]?\d+$/
|
14
|
-
INTEGER_BASE_16 = /^[+-]?[\da-z]+$/i
|
15
|
-
RATIONAL = /^([+-]?\d+)\/(\d+)$/
|
16
|
-
ATOM = /^[^\s()\[\]]+/
|
12
|
+
include Enumerable
|
17
13
|
|
18
14
|
# @return [Object]
|
19
15
|
attr_reader :input
|
20
16
|
|
17
|
+
# @return [Hash{Symbol => Object}]
|
18
|
+
attr_reader :options
|
19
|
+
|
21
20
|
##
|
22
|
-
# @param [
|
21
|
+
# @param [IO, StringIO, String] input
|
23
22
|
# @param [Hash{Symbol => Object}] options
|
24
|
-
def initialize(input, options = {})
|
23
|
+
def initialize(input, options = {}, &block)
|
24
|
+
@options = options.dup
|
25
|
+
|
25
26
|
case
|
26
27
|
when [:getc, :ungetc, :eof?].all? { |x| input.respond_to?(x) }
|
27
28
|
@input = input
|
28
29
|
when input.respond_to?(:to_str)
|
29
|
-
require 'stringio'
|
30
|
+
require 'stringio' unless defined?(StringIO)
|
30
31
|
@input = StringIO.new(input.to_str)
|
31
32
|
else
|
32
|
-
raise ArgumentError, "expected an IO or String input stream
|
33
|
+
raise ArgumentError, "expected an IO or String input stream, but got #{input.inspect}"
|
34
|
+
end
|
35
|
+
|
36
|
+
if block_given?
|
37
|
+
case block.arity
|
38
|
+
when 1 then block.call(self)
|
39
|
+
else self.instance_eval(&block)
|
40
|
+
end
|
33
41
|
end
|
34
42
|
end
|
35
43
|
|
@@ -38,7 +46,11 @@ module SXP
|
|
38
46
|
# @yieldparam [Object] object
|
39
47
|
# @return [Enumerator]
|
40
48
|
def each(&block)
|
41
|
-
|
49
|
+
#block_given? ?
|
50
|
+
# to_enum
|
51
|
+
#else
|
52
|
+
# block.call(read)
|
53
|
+
#end
|
42
54
|
end
|
43
55
|
|
44
56
|
##
|
@@ -61,9 +73,9 @@ module SXP
|
|
61
73
|
case token
|
62
74
|
when :eof
|
63
75
|
throw :eof if options[:eof] == :throw
|
64
|
-
raise EOF,
|
76
|
+
raise EOF, "unexpected end of input"
|
65
77
|
when :list
|
66
|
-
if
|
78
|
+
if self.class.const_get(:LPARENS).include?(value)
|
67
79
|
read_list
|
68
80
|
else
|
69
81
|
throw :eol if options[:eol] == :throw # end of list
|
@@ -75,19 +87,6 @@ module SXP
|
|
75
87
|
|
76
88
|
alias_method :skip, :read
|
77
89
|
|
78
|
-
##
|
79
|
-
# @return [Object]
|
80
|
-
def read_token
|
81
|
-
case peek_char
|
82
|
-
when nil then :eof
|
83
|
-
when ?(, ?) then [:list, read_char]
|
84
|
-
when ?[, ?] then [:list, read_char]
|
85
|
-
when ?" then [:atom, read_string]
|
86
|
-
when ?# then [:atom, read_sharp]
|
87
|
-
else [:atom, read_atom]
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
90
|
##
|
92
91
|
# @param [Array]
|
93
92
|
def read_list
|
@@ -100,23 +99,19 @@ module SXP
|
|
100
99
|
|
101
100
|
##
|
102
101
|
# @return [Object]
|
103
|
-
def
|
104
|
-
|
105
|
-
|
106
|
-
|
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)
|
113
|
-
when ?\\ then read_character
|
114
|
-
when ?; then skip; read
|
115
|
-
when ?! then skip_line; read # shebang
|
116
|
-
else raise Error, "invalid sharp-sign read syntax: ##{char.chr}"
|
102
|
+
def read_token
|
103
|
+
case peek_char
|
104
|
+
when nil then :eof
|
105
|
+
else [:atom, read_atom]
|
117
106
|
end
|
118
107
|
end
|
119
108
|
|
109
|
+
##
|
110
|
+
# @return [Object]
|
111
|
+
def read_sharp
|
112
|
+
raise NotImplementedError.new("#{self.class}#read_sharp")
|
113
|
+
end
|
114
|
+
|
120
115
|
##
|
121
116
|
# @param [Integer] base
|
122
117
|
# @return [Integer]
|
@@ -131,66 +126,48 @@ module SXP
|
|
131
126
|
##
|
132
127
|
# @return [Object]
|
133
128
|
def read_atom
|
134
|
-
|
135
|
-
when '.' then buffer.to_sym
|
136
|
-
when FLOAT then buffer.to_f
|
137
|
-
when INTEGER_BASE_10 then buffer.to_i
|
138
|
-
when RATIONAL then Rational($1.to_i, $2.to_i)
|
139
|
-
else buffer.to_sym
|
140
|
-
end
|
129
|
+
raise NotImplementedError.new("#{self.class}#read_atom")
|
141
130
|
end
|
142
131
|
|
143
132
|
##
|
144
133
|
# @return [String]
|
145
134
|
def read_string
|
146
|
-
|
147
|
-
skip_char # '"'
|
148
|
-
until peek_char == ?"
|
149
|
-
buffer <<
|
150
|
-
case char = read_char
|
151
|
-
when ?\\ then read_character
|
152
|
-
else char
|
153
|
-
end
|
154
|
-
end
|
155
|
-
skip_char # '"'
|
156
|
-
buffer
|
135
|
+
raise NotImplementedError.new("#{self.class}#read_string")
|
157
136
|
end
|
158
137
|
|
159
138
|
##
|
160
139
|
# @return [String]
|
161
140
|
def read_character
|
162
|
-
|
163
|
-
when ?b then ?\b
|
164
|
-
when ?f then ?\f
|
165
|
-
when ?n then ?\n
|
166
|
-
when ?r then ?\r
|
167
|
-
when ?t then ?\t
|
168
|
-
when ?u then read_chars(4).to_i(16).chr
|
169
|
-
when ?U then read_chars(8).to_i(16).chr
|
170
|
-
else char
|
171
|
-
end
|
141
|
+
raise NotImplementedError.new("#{self.class}#read_character")
|
172
142
|
end
|
173
143
|
|
174
144
|
##
|
175
145
|
# @return [String]
|
176
146
|
def read_literal
|
177
|
-
|
178
|
-
buffer << read_char while !eof? && peek_char.chr =~ ATOM
|
179
|
-
buffer
|
147
|
+
raise NotImplementedError.new("#{self.class}#read_literal")
|
180
148
|
end
|
181
149
|
|
150
|
+
protected
|
151
|
+
|
182
152
|
##
|
183
153
|
# @return [void]
|
184
154
|
def skip_comments
|
185
155
|
until eof?
|
186
156
|
case (char = peek_char).chr
|
187
|
-
when /;/ then skip_line
|
188
157
|
when /\s+/ then skip_char
|
189
158
|
else break
|
190
159
|
end
|
191
160
|
end
|
192
161
|
end
|
193
162
|
|
163
|
+
##
|
164
|
+
# @return [void]
|
165
|
+
def skip_line
|
166
|
+
loop do
|
167
|
+
break if eof? || read_char.chr == $/
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
194
171
|
##
|
195
172
|
# @param [Integer] count
|
196
173
|
# @return [String]
|
@@ -208,21 +185,13 @@ module SXP
|
|
208
185
|
char
|
209
186
|
end
|
210
187
|
|
211
|
-
##
|
212
|
-
# @return [void]
|
213
|
-
def skip_line
|
214
|
-
loop do
|
215
|
-
break if eof? || read_char.chr == $/
|
216
|
-
end
|
217
|
-
end
|
218
|
-
|
219
188
|
alias_method :skip_char, :read_char
|
220
189
|
|
221
190
|
##
|
222
191
|
# @return [String]
|
223
192
|
def peek_char
|
224
193
|
char = @input.getc
|
225
|
-
@input.ungetc
|
194
|
+
@input.ungetc(char) unless char.nil?
|
226
195
|
char
|
227
196
|
end
|
228
197
|
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module SXP; class Reader
|
2
|
+
##
|
3
|
+
# A basic S-expression parser.
|
4
|
+
class Basic < Reader
|
5
|
+
LPARENS = [?(].freeze
|
6
|
+
RPARENS = [?)].freeze
|
7
|
+
ATOM = /^[^\s()]+/.freeze
|
8
|
+
RATIONAL = /^([+-]?\d+)\/(\d+)$/.freeze
|
9
|
+
DECIMAL = /^[+-]?(\d*)?\.\d*$/.freeze
|
10
|
+
INTEGER = /^[+-]?\d+$/.freeze
|
11
|
+
|
12
|
+
##
|
13
|
+
# @return [Object]
|
14
|
+
def read_token
|
15
|
+
case peek_char
|
16
|
+
when ?(, ?) then [:list, read_char]
|
17
|
+
when ?" then [:atom, read_string]
|
18
|
+
else super
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
##
|
23
|
+
# @return [Object]
|
24
|
+
def read_atom
|
25
|
+
case buffer = read_literal
|
26
|
+
when '.' then buffer.to_sym
|
27
|
+
when RATIONAL then Rational($1.to_i, $2.to_i)
|
28
|
+
when DECIMAL then Float(buffer) # FIXME?
|
29
|
+
when INTEGER then Integer(buffer)
|
30
|
+
else buffer.to_sym
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
##
|
35
|
+
# @return [String]
|
36
|
+
def read_string
|
37
|
+
buffer = String.new
|
38
|
+
skip_char # '"'
|
39
|
+
until peek_char == ?"
|
40
|
+
buffer <<
|
41
|
+
case char = read_char
|
42
|
+
when ?\\ then read_character
|
43
|
+
else char
|
44
|
+
end
|
45
|
+
end
|
46
|
+
skip_char # '"'
|
47
|
+
buffer
|
48
|
+
end
|
49
|
+
|
50
|
+
##
|
51
|
+
# @return [String]
|
52
|
+
def read_character
|
53
|
+
case char = read_char
|
54
|
+
when ?b then ?\b
|
55
|
+
when ?f then ?\f
|
56
|
+
when ?n then ?\n
|
57
|
+
when ?r then ?\r
|
58
|
+
when ?t then ?\t
|
59
|
+
when ?u then read_chars(4).to_i(16).chr
|
60
|
+
when ?U then read_chars(8).to_i(16).chr
|
61
|
+
when ?" then char
|
62
|
+
when ?\\ then char
|
63
|
+
else char
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
##
|
68
|
+
# @return [String]
|
69
|
+
def read_literal
|
70
|
+
grammar = self.class.const_get(:ATOM)
|
71
|
+
buffer = String.new
|
72
|
+
buffer << read_char while !eof? && peek_char.chr =~ grammar
|
73
|
+
buffer
|
74
|
+
end
|
75
|
+
end # class Reader
|
76
|
+
end; end # class SXP::Reader
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module SXP; class Reader
|
2
|
+
##
|
3
|
+
# An extended S-expression parser.
|
4
|
+
class Extended < Basic
|
5
|
+
LPARENS = [?(, ?[].freeze
|
6
|
+
RPARENS = [?), ?]].freeze
|
7
|
+
ATOM = /^[^\s()\[\]]+/.freeze
|
8
|
+
|
9
|
+
##
|
10
|
+
# @return [Object]
|
11
|
+
def read_token
|
12
|
+
case peek_char
|
13
|
+
when ?[, ?] then [:list, read_char]
|
14
|
+
else super
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
##
|
19
|
+
# @return [void]
|
20
|
+
def skip_comments
|
21
|
+
until eof?
|
22
|
+
case (char = peek_char).chr
|
23
|
+
when /\s+/ then skip_char
|
24
|
+
when /;/ then skip_line
|
25
|
+
else break
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end # class Reader
|
30
|
+
end; end # class SXP::Reader
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module SXP; class Reader
|
2
|
+
##
|
3
|
+
# A Scheme-like S-expression parser.
|
4
|
+
class Scheme < Extended
|
5
|
+
DECIMAL = /^[+-]?(\d*)?\.\d*$/.freeze
|
6
|
+
INTEGER_BASE_2 = /^[+-]?[01]+$/.freeze
|
7
|
+
INTEGER_BASE_8 = /^[+-]?[0-7]+$/.freeze
|
8
|
+
INTEGER_BASE_10 = /^[+-]?\d+$/.freeze
|
9
|
+
INTEGER_BASE_16 = /^[+-]?[\da-z]+$/i.freeze
|
10
|
+
RATIONAL = /^([+-]?\d+)\/(\d+)$/.freeze
|
11
|
+
|
12
|
+
##
|
13
|
+
# @return [Object]
|
14
|
+
def read_token
|
15
|
+
case peek_char
|
16
|
+
when ?# then [:atom, read_sharp]
|
17
|
+
else super
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
##
|
22
|
+
# @return [Object]
|
23
|
+
def read_atom
|
24
|
+
case buffer = read_literal
|
25
|
+
when '.' then buffer.to_sym
|
26
|
+
when RATIONAL then Rational($1.to_i, $2.to_i)
|
27
|
+
when DECIMAL then Float(buffer)
|
28
|
+
when INTEGER_BASE_10 then Integer(buffer)
|
29
|
+
else buffer.to_sym
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
##
|
34
|
+
# @return [Object]
|
35
|
+
def read_sharp
|
36
|
+
skip_char # '#'
|
37
|
+
case char = read_char
|
38
|
+
when ?n then nil # not in Scheme
|
39
|
+
when ?f then false
|
40
|
+
when ?t then true
|
41
|
+
when ?b then read_integer(2)
|
42
|
+
when ?o then read_integer(8)
|
43
|
+
when ?d then read_integer(10)
|
44
|
+
when ?x then read_integer(16)
|
45
|
+
when ?\\ then read_character
|
46
|
+
when ?; then skip; read
|
47
|
+
when ?! then skip_line; read # shebang
|
48
|
+
else raise Error, "invalid sharp-sign read syntax: ##{char.chr}"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end # class Reader
|
52
|
+
end; end # class SXP::Reader
|
data/lib/sxp/version.rb
CHANGED
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
|
+
- 5
|
9
|
+
version: 0.0.5
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Arto Bendiken
|
@@ -14,35 +14,35 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-
|
17
|
+
date: 2010-06-23 00:00:00 +02:00
|
18
18
|
default_executable: sxp2rdf
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
|
-
name:
|
21
|
+
name: yard
|
22
22
|
prerelease: false
|
23
23
|
requirement: &id001 !ruby/object:Gem::Requirement
|
24
24
|
requirements:
|
25
25
|
- - ">="
|
26
26
|
- !ruby/object:Gem::Version
|
27
27
|
segments:
|
28
|
-
- 1
|
29
|
-
- 3
|
30
28
|
- 0
|
31
|
-
|
29
|
+
- 5
|
30
|
+
- 8
|
31
|
+
version: 0.5.8
|
32
32
|
type: :development
|
33
33
|
version_requirements: *id001
|
34
34
|
- !ruby/object:Gem::Dependency
|
35
|
-
name:
|
35
|
+
name: rspec
|
36
36
|
prerelease: false
|
37
37
|
requirement: &id002 !ruby/object:Gem::Requirement
|
38
38
|
requirements:
|
39
39
|
- - ">="
|
40
40
|
- !ruby/object:Gem::Version
|
41
41
|
segments:
|
42
|
+
- 1
|
43
|
+
- 3
|
42
44
|
- 0
|
43
|
-
|
44
|
-
- 4
|
45
|
-
version: 0.5.4
|
45
|
+
version: 1.3.0
|
46
46
|
type: :development
|
47
47
|
version_requirements: *id002
|
48
48
|
description: " SXP is a data interchange format based on S-expressions, the simplest and\n most versatile known means of representing complex data structures such as\n lists, trees and graphs.\n"
|
@@ -65,6 +65,9 @@ files:
|
|
65
65
|
- lib/sxp/generator.rb
|
66
66
|
- lib/sxp/list.rb
|
67
67
|
- lib/sxp/pair.rb
|
68
|
+
- lib/sxp/reader/basic.rb
|
69
|
+
- lib/sxp/reader/extended.rb
|
70
|
+
- lib/sxp/reader/scheme.rb
|
68
71
|
- lib/sxp/reader.rb
|
69
72
|
- lib/sxp/version.rb
|
70
73
|
- lib/sxp/writer.rb
|
@@ -85,8 +88,8 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
85
88
|
segments:
|
86
89
|
- 1
|
87
90
|
- 8
|
88
|
-
-
|
89
|
-
version: 1.8.
|
91
|
+
- 1
|
92
|
+
version: 1.8.1
|
90
93
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
91
94
|
requirements:
|
92
95
|
- - ">="
|