sxp 0.0.13 → 0.0.14

Sign up to get free protection for your applications and to get access to all the features.
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.