sxp 0.0.13 → 0.0.14
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/AUTHORS +0 -1
- data/CREDITS +1 -0
- data/README +20 -0
- data/VERSION +1 -1
- data/lib/sxp/extensions.rb +11 -0
- data/lib/sxp/reader/sparql.rb +198 -16
- data/lib/sxp/version.rb +1 -1
- data/lib/sxp/writer.rb +74 -0
- metadata +36 -13
data/AUTHORS
CHANGED
data/CREDITS
CHANGED
data/README
CHANGED
@@ -45,6 +45,8 @@ Examples
|
|
45
45
|
|
46
46
|
### Parsing SPARQL S-expressions
|
47
47
|
|
48
|
+
require 'rdf'
|
49
|
+
|
48
50
|
SXP::Reader::SPARQL.read %q((base <http://ar.to/>)) #=> [:base, RDF::URI('http://ar.to/')]
|
49
51
|
|
50
52
|
Documentation
|
@@ -52,6 +54,23 @@ Documentation
|
|
52
54
|
|
53
55
|
* <http://sxp.rubyforge.org/>
|
54
56
|
|
57
|
+
* {SXP}
|
58
|
+
|
59
|
+
### Parsing SXP
|
60
|
+
* {SXP::Reader}
|
61
|
+
* {SXP::Reader::Basic}
|
62
|
+
* {SXP::Reader::CommonLisp}
|
63
|
+
* {SXP::Reader::Extended}
|
64
|
+
* {SXP::Reader::Scheme}
|
65
|
+
* {SXP::Reader::SPARQL}
|
66
|
+
|
67
|
+
### Manipulating SXP
|
68
|
+
* {SXP::Pair}
|
69
|
+
* {SXP::List}
|
70
|
+
|
71
|
+
### Generating SXP
|
72
|
+
* {SXP::Generator}
|
73
|
+
|
55
74
|
Dependencies
|
56
75
|
------------
|
57
76
|
|
@@ -98,6 +117,7 @@ Contributors
|
|
98
117
|
------------
|
99
118
|
|
100
119
|
* [Ben Lavender](https://github.com/bhuga) - <http://bhuga.net/>
|
120
|
+
* [Gregg Kellogg](http://github.com/gkellogg) - <http://kellogg-assoc.com/>
|
101
121
|
|
102
122
|
License
|
103
123
|
-------
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.14
|
data/lib/sxp/extensions.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'rdf'
|
2
|
+
|
1
3
|
##
|
2
4
|
# Extensions for Ruby's `Symbol` class.
|
3
5
|
class Symbol
|
@@ -9,3 +11,12 @@ class Symbol
|
|
9
11
|
to_s[-1] == ?:
|
10
12
|
end
|
11
13
|
end
|
14
|
+
|
15
|
+
##
|
16
|
+
# Extensions for RDF::URI
|
17
|
+
class RDF::URI
|
18
|
+
# Original lexical value of this URI to allow for round-trip serialization.
|
19
|
+
def lexical=(value); @lexical = value; end
|
20
|
+
def lexical; @lexical; end
|
21
|
+
end
|
22
|
+
|
data/lib/sxp/reader/sparql.rb
CHANGED
@@ -8,45 +8,171 @@ module SXP; class Reader
|
|
8
8
|
#
|
9
9
|
# @see http://openjena.org/wiki/SSE
|
10
10
|
class SPARQL < Extended
|
11
|
+
# Alias for rdf:type
|
12
|
+
A = /^a$/
|
13
|
+
# Base token, causes next URI to be treated as the `base_uri` for further URI expansion
|
14
|
+
BASE = /^base$/i
|
15
|
+
# Prefix token, causes following prefix and URI pairs to be used for transforming
|
16
|
+
# {PNAME} tokens into URIs.
|
17
|
+
PREFIX = /^prefix$/i
|
11
18
|
NIL = /^nil$/i
|
12
19
|
FALSE = /^false$/i
|
13
20
|
TRUE = /^true$/i
|
14
21
|
EXPONENT = /[eE][+-]?[0-9]+/
|
15
|
-
DECIMAL = /^[+-]?(\d*)?\.\d
|
22
|
+
DECIMAL = /^[+-]?(\d*)?\.\d*$/
|
23
|
+
DOUBLE = /^[+-]?(\d*)?\.\d*#{EXPONENT}$/
|
24
|
+
# BNode with identifier
|
16
25
|
BNODE_ID = /^_:([A-Za-z][A-Za-z0-9]*)/ # FIXME
|
26
|
+
# Anonymous BNode
|
17
27
|
BNODE_NEW = /^_:$/
|
18
|
-
|
19
|
-
|
20
|
-
|
28
|
+
# Distinguished variable with an optional name
|
29
|
+
VAR_ID = /^\?([A-Za-z][A-Za-z0-9]*)?/ # FIXME
|
30
|
+
# Non-distinguished variable with an optional identifier
|
31
|
+
ND_VAR = /^\?\?([0-9]*)/
|
32
|
+
# A URI reference, subject to expansion using `base_uri`
|
21
33
|
URIREF = /^<([^>]+)>/
|
34
|
+
# A QName, subject to expansion to URIs using {PREFIX}
|
35
|
+
PNAME = /([^:]*):([^:]*)/
|
36
|
+
|
37
|
+
RDF_TYPE = (a = RDF.type.dup; a.lexical = 'a'; a).freeze
|
22
38
|
|
23
39
|
##
|
40
|
+
# Base URI as specified or when parsing parsing a BASE token using the immediately following
|
41
|
+
# token, which must be a URI.
|
42
|
+
attr_accessor :base_uri
|
43
|
+
|
44
|
+
##
|
45
|
+
# Prefixes defined while parsing
|
46
|
+
# @return [Hash{Object => RDF::URI}]
|
47
|
+
attr_accessor :prefixes
|
48
|
+
|
49
|
+
##
|
50
|
+
# Defines the given named URI prefix for this parser.
|
51
|
+
#
|
52
|
+
# @example Defining a URI prefix
|
53
|
+
# parser.prefix :dc, RDF::URI('http://purl.org/dc/terms/')
|
54
|
+
#
|
55
|
+
# @example Returning a URI prefix
|
56
|
+
# parser.prefix(:dc) #=> RDF::URI('http://purl.org/dc/terms/')
|
57
|
+
#
|
58
|
+
# @overload prefix(name, uri)
|
59
|
+
# @param [Symbol, #to_s] name
|
60
|
+
# @param [RDF::URI, #to_s] uri
|
61
|
+
#
|
62
|
+
# @overload prefix(name)
|
63
|
+
# @param [Symbol, #to_s] name
|
64
|
+
#
|
65
|
+
# @return [RDF::URI]
|
66
|
+
def prefix(name, uri = nil)
|
67
|
+
name = name.to_s.empty? ? nil : (name.respond_to?(:to_sym) ? name.to_sym : name.to_s.to_sym)
|
68
|
+
uri.nil? ? @prefixes[name] : @prefixes[name] = uri
|
69
|
+
end
|
70
|
+
|
71
|
+
##
|
72
|
+
# Initializes the reader.
|
73
|
+
#
|
74
|
+
# @param [IO, StringIO, String] input
|
75
|
+
# @param [Hash{Symbol => Object}] options
|
76
|
+
def initialize(input, options = {}, &block)
|
77
|
+
super { @prefixes = {}; @bnodes = {}; @list_depth = 0 }
|
78
|
+
|
79
|
+
if block_given?
|
80
|
+
case block.arity
|
81
|
+
when 1 then block.call(self)
|
82
|
+
else self.instance_eval(&block)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
##
|
88
|
+
# Reads SSE Tokens, including {RDF::Literal}, {RDF::URI} and RDF::Node.
|
89
|
+
#
|
90
|
+
# Performs forward reference for prefix and base URI representations and saves in
|
91
|
+
# {#base_uri} and {#prefixes} accessors.
|
92
|
+
#
|
93
|
+
# Transforms tokens matching a {PNAME} pattern into {RDF::URI} instances if a match is
|
94
|
+
# found with a previously identified {PREFIX}.
|
24
95
|
# @return [Object]
|
25
96
|
def read_token
|
26
97
|
case peek_char
|
27
|
-
|
28
|
-
|
29
|
-
|
98
|
+
when ?" then [:atom, read_rdf_literal] # "
|
99
|
+
when ?< then [:atom, read_rdf_uri]
|
100
|
+
else
|
101
|
+
tok = super
|
102
|
+
|
103
|
+
# If we just parsed "PREFIX", and this is an opening list, then
|
104
|
+
# record list depth and process following as token, URI pairs
|
105
|
+
#
|
106
|
+
# Once we've closed the list, go out of prefix mode
|
107
|
+
if tok.is_a?(Array) && tok[0] == :list
|
108
|
+
if '(['.include?(tok[1])
|
109
|
+
@list_depth += 1
|
110
|
+
else
|
111
|
+
@list_depth -= 1
|
112
|
+
@prefix_depth = nil if @prefix_depth && @list_depth < @prefix_depth
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
if tok.is_a?(Array) && tok[0] == :atom && tok[1].is_a?(Symbol)
|
117
|
+
value = tok[1].to_s
|
118
|
+
|
119
|
+
# We previously parsed a PREFIX, this will be the map value
|
120
|
+
@parsed_prefix = value.chop if @prefix_depth && @prefix_depth > 0
|
121
|
+
|
122
|
+
# If we just saw PREFIX, then this starts the parsing mode
|
123
|
+
@prefix_depth = @list_depth + 1 if value =~ PREFIX
|
124
|
+
|
125
|
+
# If the token is of the form 'prefix:suffix', create a URI and give it the
|
126
|
+
# token as a QName
|
127
|
+
if value.to_s =~ PNAME && base = prefix($1)
|
128
|
+
suffix = $2
|
129
|
+
#STDERR.puts "read_tok lexical: pfx: #{$1.inspect} => #{prefix($1).inspect}, sfx: #{suffix.inspect}"
|
130
|
+
suffix = suffix.sub(/^\#/, "") if base.to_s.index("#")
|
131
|
+
uri = RDF::URI(base.to_s + suffix)
|
132
|
+
#STDERR.puts "read_tok lexical uri: #{uri.inspect}"
|
133
|
+
|
134
|
+
# Cause URI to be serialized as a lexical
|
135
|
+
uri.lexical = value
|
136
|
+
[:atom, uri]
|
137
|
+
else
|
138
|
+
tok
|
139
|
+
end
|
140
|
+
else
|
141
|
+
tok
|
142
|
+
end
|
30
143
|
end
|
31
144
|
end
|
32
145
|
|
33
146
|
##
|
147
|
+
# Reads literals corresponding to SPARQL/Turtle/Notation-3 syntax
|
148
|
+
#
|
149
|
+
# @example
|
150
|
+
# "a plain literal"
|
151
|
+
# "a literal with a language"@en
|
152
|
+
# "a typed literal"^^<http://example/>
|
153
|
+
# "a typed literal with a PNAME"^^xsd:string
|
154
|
+
#
|
34
155
|
# @return [RDF::Literal]
|
35
156
|
def read_rdf_literal
|
36
157
|
value = read_string
|
37
158
|
options = case peek_char
|
38
159
|
when ?@
|
39
160
|
skip_char # '@'
|
40
|
-
{:language => read_atom}
|
161
|
+
{:language => read_atom.downcase}
|
41
162
|
when ?^
|
42
163
|
2.times { skip_char } # '^^'
|
43
|
-
{:datatype =>
|
164
|
+
{:datatype => read_token.last}
|
44
165
|
else {}
|
45
166
|
end
|
46
167
|
RDF::Literal(value, options)
|
47
168
|
end
|
48
169
|
|
49
170
|
##
|
171
|
+
# Reads a URI in SPARQL/Turtle/Notation-3 syntax
|
172
|
+
#
|
173
|
+
# @example
|
174
|
+
# <http://example/>
|
175
|
+
#
|
50
176
|
# @return [RDF::URI]
|
51
177
|
def read_rdf_uri
|
52
178
|
buffer = String.new
|
@@ -57,24 +183,54 @@ module SXP; class Reader
|
|
57
183
|
buffer << read_char # TODO: unescaping
|
58
184
|
end
|
59
185
|
skip_char # '>'
|
60
|
-
|
186
|
+
|
187
|
+
# If we have a base URI, use that when constructing a new URI
|
188
|
+
uri = if self.base_uri
|
189
|
+
u = self.base_uri.join(buffer)
|
190
|
+
u.lexical = "<#{buffer}>" unless u.to_s == buffer # So that it can be re-serialized properly
|
191
|
+
u
|
192
|
+
else
|
193
|
+
RDF::URI(buffer)
|
194
|
+
end
|
195
|
+
|
196
|
+
# If we previously parsed a "BASE" element, then this URI is used to set that value
|
197
|
+
if @parsed_base
|
198
|
+
self.base_uri = uri
|
199
|
+
@parsed_base = nil
|
200
|
+
end
|
201
|
+
|
202
|
+
# If we previously parsed a "PREFIX" element, associate this URI with the prefix
|
203
|
+
if @parsed_prefix
|
204
|
+
prefix(@parsed_prefix, uri)
|
205
|
+
@parsed_prefix = nil
|
206
|
+
end
|
207
|
+
|
208
|
+
uri
|
61
209
|
end
|
62
210
|
|
63
211
|
##
|
212
|
+
# Reads an SSE Atom
|
213
|
+
#
|
214
|
+
# Atoms parsed including `base`, `prefix`, `true`, `false`, numeric, BNodes and variables.
|
215
|
+
#
|
216
|
+
# Creates {RDF::Literal}, RDF::Node, or {RDF::Query::Variable} instances where appropriate.
|
217
|
+
#
|
64
218
|
# @return [Object]
|
65
219
|
def read_atom
|
66
220
|
case buffer = read_literal
|
67
221
|
when '.' then buffer.to_sym
|
222
|
+
when A then RDF_TYPE
|
223
|
+
when BASE then @parsed_base = true; buffer.to_sym
|
68
224
|
when NIL then nil
|
69
225
|
when FALSE then RDF::Literal(false)
|
70
226
|
when TRUE then RDF::Literal(true)
|
71
|
-
when
|
72
|
-
when
|
73
|
-
when
|
227
|
+
when DOUBLE then RDF::Literal::Double.new(buffer)
|
228
|
+
when DECIMAL then RDF::Literal::Decimal.new(buffer)
|
229
|
+
when INTEGER then RDF::Literal::Integer.new(buffer)
|
230
|
+
when BNODE_ID then @bnodes[$1] ||= RDF::Node($1)
|
74
231
|
when BNODE_NEW then RDF::Node.new
|
75
|
-
when
|
76
|
-
when
|
77
|
-
when VAR_NEW then RDF::Query::Variable.new
|
232
|
+
when ND_VAR then variable($1, false)
|
233
|
+
when VAR_ID then variable($1, true)
|
78
234
|
else buffer.to_sym
|
79
235
|
end
|
80
236
|
end
|
@@ -91,5 +247,31 @@ module SXP; class Reader
|
|
91
247
|
end
|
92
248
|
end
|
93
249
|
end
|
250
|
+
|
251
|
+
##
|
252
|
+
# Return variable allocated to an ID.
|
253
|
+
# If no ID is provided, a new variable
|
254
|
+
# is allocated. Otherwise, any previous assignment will be used.
|
255
|
+
#
|
256
|
+
# The variable has a #distinguished? method applied depending on if this
|
257
|
+
# is a disinguished or non-distinguished variable. Non-distinguished
|
258
|
+
# variables are effectively the same as BNodes.
|
259
|
+
# @return [RDF::Query::Variable]
|
260
|
+
def variable(id, distinguished = true)
|
261
|
+
id = nil if id.to_s.empty?
|
262
|
+
|
263
|
+
if id
|
264
|
+
@vars ||= {}
|
265
|
+
@vars[id] ||= begin
|
266
|
+
v = RDF::Query::Variable.new(id)
|
267
|
+
v.distinguished = distinguished
|
268
|
+
v
|
269
|
+
end
|
270
|
+
else
|
271
|
+
v = RDF::Query::Variable.new
|
272
|
+
v.distinguished = distinguished
|
273
|
+
v
|
274
|
+
end
|
275
|
+
end
|
94
276
|
end # SPARQL
|
95
277
|
end; end # SXP::Reader
|
data/lib/sxp/version.rb
CHANGED
data/lib/sxp/writer.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
1
3
|
##
|
2
4
|
# Extensions for Ruby's `Object` class.
|
3
5
|
class Object
|
@@ -82,6 +84,18 @@ class Integer
|
|
82
84
|
end
|
83
85
|
end
|
84
86
|
|
87
|
+
##
|
88
|
+
# Extensions for Ruby's `BigDecimal` class.
|
89
|
+
class BigDecimal
|
90
|
+
##
|
91
|
+
# Returns the SXP representation of this object.
|
92
|
+
#
|
93
|
+
# @return [String]
|
94
|
+
def to_sxp
|
95
|
+
to_f.to_s
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
85
99
|
##
|
86
100
|
# Extensions for Ruby's `Float` class.
|
87
101
|
class Float
|
@@ -133,3 +147,63 @@ class Regexp
|
|
133
147
|
'#' << inspect
|
134
148
|
end
|
135
149
|
end
|
150
|
+
|
151
|
+
require 'rdf' # For SPARQL
|
152
|
+
|
153
|
+
class RDF::URI
|
154
|
+
##
|
155
|
+
# Returns the SXP representation of this object.
|
156
|
+
#
|
157
|
+
# @return [String]
|
158
|
+
def to_sxp; lexical || "<#{self}>"; end
|
159
|
+
end
|
160
|
+
|
161
|
+
class RDF::Node
|
162
|
+
##
|
163
|
+
# Returns the SXP representation of this object.
|
164
|
+
#
|
165
|
+
# @return [String]
|
166
|
+
def to_sxp; to_s; end
|
167
|
+
end
|
168
|
+
|
169
|
+
class RDF::Literal
|
170
|
+
##
|
171
|
+
# Returns the SXP representation of a Literal.
|
172
|
+
#
|
173
|
+
# @return [String]
|
174
|
+
def to_sxp
|
175
|
+
case datatype
|
176
|
+
when RDF::XSD.boolean, RDF::XSD.integer, RDF::XSD.double, RDF::XSD.decimal, RDF::XSD.time
|
177
|
+
object.to_sxp
|
178
|
+
else
|
179
|
+
text = value.dump
|
180
|
+
text << "@#{language}" if self.has_language?
|
181
|
+
text << "^^#{datatype.to_sxp}" if self.has_datatype?
|
182
|
+
text
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
class RDF::Query
|
188
|
+
# Transform Query into an Array form of an SXP
|
189
|
+
#
|
190
|
+
# If Query is named, it's treated as a GroupGraphPattern, otherwise, a BGP
|
191
|
+
#
|
192
|
+
# @return [Array]
|
193
|
+
def to_sxp
|
194
|
+
res = [:bgp] + patterns
|
195
|
+
(respond_to?(:named?) && named? ? [:graph, context, res] : res).to_sxp
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
class RDF::Query::Pattern
|
200
|
+
# Transform Query Pattern into an SXP
|
201
|
+
# @return [String]
|
202
|
+
def to_sxp
|
203
|
+
[:triple, subject, predicate, object].to_sxp
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
class RDF::Query::Variable
|
208
|
+
def to_sxp; to_s; end
|
209
|
+
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
|
+
- 14
|
9
|
+
version: 0.0.14
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Arto Bendiken
|
@@ -14,37 +14,54 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2011-
|
17
|
+
date: 2011-04-04 00:00:00 +02:00
|
18
18
|
default_executable: sxp2rdf
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
|
-
name:
|
21
|
+
name: json
|
22
22
|
prerelease: false
|
23
23
|
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
segments:
|
29
|
+
- 1
|
30
|
+
- 5
|
31
|
+
- 1
|
32
|
+
version: 1.5.1
|
33
|
+
type: :runtime
|
34
|
+
version_requirements: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: yard
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
24
40
|
requirements:
|
25
41
|
- - ">="
|
26
42
|
- !ruby/object:Gem::Version
|
27
43
|
segments:
|
28
44
|
- 0
|
29
45
|
- 6
|
30
|
-
-
|
31
|
-
version: 0.6.
|
46
|
+
- 4
|
47
|
+
version: 0.6.4
|
32
48
|
type: :development
|
33
|
-
version_requirements: *
|
49
|
+
version_requirements: *id002
|
34
50
|
- !ruby/object:Gem::Dependency
|
35
51
|
name: rspec
|
36
52
|
prerelease: false
|
37
|
-
requirement: &
|
53
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
54
|
+
none: false
|
38
55
|
requirements:
|
39
56
|
- - ">="
|
40
57
|
- !ruby/object:Gem::Version
|
41
58
|
segments:
|
42
|
-
-
|
43
|
-
-
|
59
|
+
- 2
|
60
|
+
- 5
|
44
61
|
- 0
|
45
|
-
version:
|
62
|
+
version: 2.5.0
|
46
63
|
type: :development
|
47
|
-
version_requirements: *
|
64
|
+
version_requirements: *id003
|
48
65
|
description: A pure-Ruby implementation of a universal S-expression parser.
|
49
66
|
email: arto.bendiken@gmail.com
|
50
67
|
executables:
|
@@ -75,6 +92,10 @@ files:
|
|
75
92
|
- lib/sxp/version.rb
|
76
93
|
- lib/sxp/writer.rb
|
77
94
|
- lib/sxp.rb
|
95
|
+
- bin/sxp2rdf
|
96
|
+
- bin/sxp2json
|
97
|
+
- bin/sxp2xml
|
98
|
+
- bin/sxp2yaml
|
78
99
|
has_rdoc: false
|
79
100
|
homepage: http://sxp.rubyforge.org/
|
80
101
|
licenses:
|
@@ -85,6 +106,7 @@ rdoc_options: []
|
|
85
106
|
require_paths:
|
86
107
|
- lib
|
87
108
|
required_ruby_version: !ruby/object:Gem::Requirement
|
109
|
+
none: false
|
88
110
|
requirements:
|
89
111
|
- - ">="
|
90
112
|
- !ruby/object:Gem::Version
|
@@ -94,6 +116,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
94
116
|
- 1
|
95
117
|
version: 1.8.1
|
96
118
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
119
|
+
none: false
|
97
120
|
requirements:
|
98
121
|
- - ">="
|
99
122
|
- !ruby/object:Gem::Version
|
@@ -103,7 +126,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
103
126
|
requirements: []
|
104
127
|
|
105
128
|
rubyforge_project: sxp
|
106
|
-
rubygems_version: 1.3.
|
129
|
+
rubygems_version: 1.3.7
|
107
130
|
signing_key:
|
108
131
|
specification_version: 3
|
109
132
|
summary: A pure-Ruby implementation of a universal S-expression parser.
|