sxp 0.0.4 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
- - ">="
|