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 CHANGED
@@ -1,2 +1 @@
1
1
  * Arto Bendiken <arto.bendiken@gmail.com>
2
- * Ben Lavender <blavender@gmail.com>
data/CREDITS CHANGED
@@ -1 +1,2 @@
1
1
  * Ben Lavender <blavender@gmail.com>
2
+ * Gregg Kellogg <gregg@kellogg-assoc.com>
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.13
1
+ 0.0.14
@@ -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
+
@@ -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*#{EXPONENT}?$/
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
- VAR_ID = /^\?([A-Za-z][A-Za-z0-9]*)/ # FIXME
19
- VAR_GEN = /^\?\?([0-9]+)/
20
- VAR_NEW = '??'
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
- when ?" then [:atom, read_rdf_literal] # "
28
- when ?< then [:atom, read_rdf_uri]
29
- else super
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 => read_rdf_uri} # TODO: support prefixed names
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
- RDF::URI(buffer)
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 DECIMAL then RDF::Literal(Float(buffer[-1].eql?(?.) ? buffer + '0' : buffer))
72
- when INTEGER then RDF::Literal(Integer(buffer))
73
- when BNODE_ID then RDF::Node($1)
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 VAR_ID then RDF::Query::Variable.new($1)
76
- when VAR_GEN then RDF::Query::Variable.new("?#{$1}") # FIXME?
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
@@ -2,7 +2,7 @@ module SXP
2
2
  module VERSION
3
3
  MAJOR = 0
4
4
  MINOR = 0
5
- TINY = 13
5
+ TINY = 14
6
6
  EXTRA = nil
7
7
 
8
8
  STRING = [MAJOR, MINOR, TINY, EXTRA].compact.join('.')
@@ -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
- - 13
9
- version: 0.0.13
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-01-13 00:00:00 +01:00
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: yard
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
- - 0
31
- version: 0.6.0
46
+ - 4
47
+ version: 0.6.4
32
48
  type: :development
33
- version_requirements: *id001
49
+ version_requirements: *id002
34
50
  - !ruby/object:Gem::Dependency
35
51
  name: rspec
36
52
  prerelease: false
37
- requirement: &id002 !ruby/object:Gem::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
- - 1
43
- - 3
59
+ - 2
60
+ - 5
44
61
  - 0
45
- version: 1.3.0
62
+ version: 2.5.0
46
63
  type: :development
47
- version_requirements: *id002
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.6
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.