rdf-n3 3.1.1 → 3.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (124) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +148 -69
  3. data/UNLICENSE +1 -1
  4. data/VERSION +1 -1
  5. data/lib/rdf/n3.rb +8 -8
  6. data/lib/rdf/n3/algebra.rb +147 -68
  7. data/lib/rdf/n3/algebra/builtin.rb +79 -0
  8. data/lib/rdf/n3/algebra/formula.rb +355 -94
  9. data/lib/rdf/n3/algebra/list/append.rb +33 -4
  10. data/lib/rdf/n3/algebra/list/first.rb +24 -0
  11. data/lib/rdf/n3/algebra/list/in.rb +42 -3
  12. data/lib/rdf/n3/algebra/list/last.rb +17 -4
  13. data/lib/rdf/n3/algebra/list/length.rb +24 -0
  14. data/lib/rdf/n3/algebra/list/member.rb +39 -2
  15. data/lib/rdf/n3/algebra/list_operator.rb +83 -0
  16. data/lib/rdf/n3/algebra/log/conclusion.rb +57 -1
  17. data/lib/rdf/n3/algebra/log/conjunction.rb +28 -1
  18. data/lib/rdf/n3/algebra/log/content.rb +34 -0
  19. data/lib/rdf/n3/algebra/log/equal_to.rb +34 -0
  20. data/lib/rdf/n3/algebra/log/implies.rb +55 -30
  21. data/lib/rdf/n3/algebra/log/includes.rb +58 -1
  22. data/lib/rdf/n3/algebra/log/n3_string.rb +34 -0
  23. data/lib/rdf/n3/algebra/log/not_equal_to.rb +23 -0
  24. data/lib/rdf/n3/algebra/log/not_includes.rb +27 -0
  25. data/lib/rdf/n3/algebra/log/output_string.rb +40 -0
  26. data/lib/rdf/n3/algebra/log/parsed_as_n3.rb +36 -0
  27. data/lib/rdf/n3/algebra/log/semantics.rb +40 -0
  28. data/lib/rdf/n3/algebra/math/absolute_value.rb +36 -0
  29. data/lib/rdf/n3/algebra/math/acos.rb +26 -0
  30. data/lib/rdf/n3/algebra/math/acosh.rb +26 -0
  31. data/lib/rdf/n3/algebra/math/asin.rb +26 -0
  32. data/lib/rdf/n3/algebra/math/asinh.rb +26 -0
  33. data/lib/rdf/n3/algebra/math/atan.rb +26 -0
  34. data/lib/rdf/n3/algebra/math/atanh.rb +26 -0
  35. data/lib/rdf/n3/algebra/math/ceiling.rb +28 -0
  36. data/lib/rdf/n3/algebra/math/cos.rb +40 -0
  37. data/lib/rdf/n3/algebra/math/cosh.rb +38 -0
  38. data/lib/rdf/n3/algebra/math/difference.rb +34 -3
  39. data/lib/rdf/n3/algebra/math/equal_to.rb +54 -0
  40. data/lib/rdf/n3/algebra/math/exponentiation.rb +29 -3
  41. data/lib/rdf/n3/algebra/math/floor.rb +28 -0
  42. data/lib/rdf/n3/algebra/math/greater_than.rb +41 -0
  43. data/lib/rdf/n3/algebra/math/less_than.rb +41 -0
  44. data/lib/rdf/n3/algebra/math/negation.rb +31 -2
  45. data/lib/rdf/n3/algebra/math/not_equal_to.rb +25 -0
  46. data/lib/rdf/n3/algebra/math/not_greater_than.rb +25 -0
  47. data/lib/rdf/n3/algebra/math/not_less_than.rb +25 -0
  48. data/lib/rdf/n3/algebra/math/product.rb +14 -3
  49. data/lib/rdf/n3/algebra/math/quotient.rb +30 -3
  50. data/lib/rdf/n3/algebra/math/remainder.rb +29 -3
  51. data/lib/rdf/n3/algebra/math/rounded.rb +20 -3
  52. data/lib/rdf/n3/algebra/math/sin.rb +40 -0
  53. data/lib/rdf/n3/algebra/math/sinh.rb +38 -0
  54. data/lib/rdf/n3/algebra/math/sum.rb +35 -4
  55. data/lib/rdf/n3/algebra/math/tan.rb +40 -0
  56. data/lib/rdf/n3/algebra/math/tanh.rb +38 -0
  57. data/lib/rdf/n3/algebra/not_implemented.rb +13 -0
  58. data/lib/rdf/n3/algebra/resource_operator.rb +123 -0
  59. data/lib/rdf/n3/algebra/str/concatenation.rb +21 -3
  60. data/lib/rdf/n3/algebra/str/contains.rb +28 -4
  61. data/lib/rdf/n3/algebra/str/contains_ignoring_case.rb +33 -0
  62. data/lib/rdf/n3/algebra/str/ends_with.rb +33 -0
  63. data/lib/rdf/n3/algebra/str/equal_ignoring_case.rb +34 -0
  64. data/lib/rdf/n3/algebra/str/format.rb +12 -4
  65. data/lib/rdf/n3/algebra/str/greater_than.rb +38 -0
  66. data/lib/rdf/n3/algebra/str/less_than.rb +33 -0
  67. data/lib/rdf/n3/algebra/str/matches.rb +33 -5
  68. data/lib/rdf/n3/algebra/str/not_equal_ignoring_case.rb +17 -0
  69. data/lib/rdf/n3/algebra/str/not_greater_than.rb +17 -0
  70. data/lib/rdf/n3/algebra/str/not_less_than.rb +17 -0
  71. data/lib/rdf/n3/algebra/str/not_matches.rb +18 -0
  72. data/lib/rdf/n3/algebra/str/replace.rb +28 -5
  73. data/lib/rdf/n3/algebra/str/scrape.rb +31 -5
  74. data/lib/rdf/n3/algebra/str/starts_with.rb +33 -0
  75. data/lib/rdf/n3/algebra/time/day.rb +35 -0
  76. data/lib/rdf/n3/algebra/time/day_of_week.rb +27 -0
  77. data/lib/rdf/n3/algebra/time/gm_time.rb +29 -0
  78. data/lib/rdf/n3/algebra/time/hour.rb +35 -0
  79. data/lib/rdf/n3/algebra/time/in_seconds.rb +59 -0
  80. data/lib/rdf/n3/algebra/time/local_time.rb +29 -0
  81. data/lib/rdf/n3/algebra/time/minute.rb +35 -0
  82. data/lib/rdf/n3/algebra/time/month.rb +35 -0
  83. data/lib/rdf/n3/algebra/time/second.rb +35 -0
  84. data/lib/rdf/n3/algebra/time/timezone.rb +36 -0
  85. data/lib/rdf/n3/algebra/time/year.rb +29 -0
  86. data/lib/rdf/n3/extensions.rb +180 -21
  87. data/lib/rdf/n3/format.rb +65 -0
  88. data/lib/rdf/n3/list.rb +630 -0
  89. data/lib/rdf/n3/reader.rb +762 -485
  90. data/lib/rdf/n3/reasoner.rb +57 -68
  91. data/lib/rdf/n3/refinements.rb +178 -0
  92. data/lib/rdf/n3/repository.rb +332 -0
  93. data/lib/rdf/n3/terminals.rb +80 -0
  94. data/lib/rdf/n3/vocab.rb +35 -7
  95. data/lib/rdf/n3/writer.rb +208 -148
  96. metadata +110 -52
  97. data/AUTHORS +0 -1
  98. data/History.markdown +0 -99
  99. data/lib/rdf/n3/algebra/log/equalTo.rb +0 -7
  100. data/lib/rdf/n3/algebra/log/notEqualTo.rb +0 -7
  101. data/lib/rdf/n3/algebra/log/notIncludes.rb +0 -12
  102. data/lib/rdf/n3/algebra/log/outputString.rb +0 -7
  103. data/lib/rdf/n3/algebra/math/absoluteValue.rb +0 -9
  104. data/lib/rdf/n3/algebra/math/equalTo.rb +0 -9
  105. data/lib/rdf/n3/algebra/math/greaterThan.rb +0 -9
  106. data/lib/rdf/n3/algebra/math/integerQuotient.rb +0 -9
  107. data/lib/rdf/n3/algebra/math/lessThan.rb +0 -9
  108. data/lib/rdf/n3/algebra/math/memberCount.rb +0 -9
  109. data/lib/rdf/n3/algebra/math/notEqualTo.rb +0 -9
  110. data/lib/rdf/n3/algebra/math/notGreaterThan.rb +0 -9
  111. data/lib/rdf/n3/algebra/math/notLessThan.rb +0 -9
  112. data/lib/rdf/n3/algebra/str/containsIgnoringCase.rb +0 -9
  113. data/lib/rdf/n3/algebra/str/endsWith.rb +0 -9
  114. data/lib/rdf/n3/algebra/str/equalIgnoringCase.rb +0 -9
  115. data/lib/rdf/n3/algebra/str/greaterThan.rb +0 -9
  116. data/lib/rdf/n3/algebra/str/lessThan.rb +0 -9
  117. data/lib/rdf/n3/algebra/str/notEqualIgnoringCase.rb +0 -9
  118. data/lib/rdf/n3/algebra/str/notGreaterThan.rb +0 -9
  119. data/lib/rdf/n3/algebra/str/notLessThan.rb +0 -9
  120. data/lib/rdf/n3/algebra/str/notMatches.rb +0 -9
  121. data/lib/rdf/n3/algebra/str/startsWith.rb +0 -56
  122. data/lib/rdf/n3/patches/array_hacks.rb +0 -53
  123. data/lib/rdf/n3/reader/meta.rb +0 -641
  124. data/lib/rdf/n3/reader/parser.rb +0 -239
@@ -0,0 +1,80 @@
1
+ # encoding: utf-8
2
+ module RDF::N3
3
+ module Terminals
4
+ # Definitions of token regular expressions used for lexical analysis
5
+ ##
6
+ # Unicode regular expressions for Ruby 1.9+ with the Oniguruma engine.
7
+ U_CHARS1 = Regexp.compile(<<-EOS.gsub(/\s+/, ''))
8
+ [\\u00C0-\\u00D6]|[\\u00D8-\\u00F6]|[\\u00F8-\\u02FF]|
9
+ [\\u0370-\\u037D]|[\\u037F-\\u1FFF]|[\\u200C-\\u200D]|
10
+ [\\u2070-\\u218F]|[\\u2C00-\\u2FEF]|[\\u3001-\\uD7FF]|
11
+ [\\uF900-\\uFDCF]|[\\uFDF0-\\uFFFD]|[\\u{10000}-\\u{EFFFF}]
12
+ EOS
13
+ U_CHARS2 = Regexp.compile("\\u00B7|[\\u0300-\\u036F]|[\\u203F-\\u2040]", Regexp::FIXEDENCODING).freeze
14
+ IRI_RANGE = Regexp.compile("[[^<>\"{}|^`\\\\]&&[^\\x00-\\x20]]", Regexp::FIXEDENCODING).freeze
15
+
16
+ ESCAPE_CHAR4 = /\\u(?:[0-9A-Fa-f]{4,4})/u.freeze # \uXXXX
17
+ ESCAPE_CHAR8 = /\\U(?:[0-9A-Fa-f]{8,8})/u.freeze # \UXXXXXXXX
18
+ UCHAR = /#{ESCAPE_CHAR4}|#{ESCAPE_CHAR8}/n.freeze
19
+ # 170s
20
+ PERCENT = /%[0-9A-Fa-f]{2}/u.freeze
21
+ # 172s
22
+ PN_LOCAL_ESC = /\\[_~\.\-\!$\&'\(\)\*\+,;=\/\?\#@%]/u.freeze
23
+ # 169s
24
+ PLX = /#{PERCENT}|#{PN_LOCAL_ESC}/u.freeze
25
+ # 163s
26
+ PN_CHARS_BASE = /[A-Z]|[a-z]|#{U_CHARS1}/u.freeze
27
+ # 164s
28
+ PN_CHARS_U = /_|#{PN_CHARS_BASE}/u.freeze
29
+ # 166s
30
+ PN_CHARS = /-|[0-9]|#{PN_CHARS_U}|#{U_CHARS2}/u.freeze
31
+ PN_LOCAL_BODY = /(?:(?:\.|:|#{PN_CHARS}|#{PLX})*(?:#{PN_CHARS}|:|#{PLX}))?/u.freeze
32
+ PN_CHARS_BODY = /(?:(?:\.|#{PN_CHARS})*#{PN_CHARS})?/u.freeze
33
+ # 167s
34
+ PN_PREFIX = /#{PN_CHARS_BASE}#{PN_CHARS_BODY}/u.freeze
35
+ # 168s
36
+ PN_LOCAL = /(?:[0-9]|:|#{PN_CHARS_U}|#{PLX})#{PN_LOCAL_BODY}/u.freeze
37
+ # 154s
38
+ EXPONENT = /[eE][+-]?[0-9]+/u.freeze
39
+ # 159s
40
+ ECHAR = /\\[tbnrf\\"']/u.freeze
41
+ # 18
42
+ IRIREF = /<(?:#{IRI_RANGE}|#{UCHAR})*>/mu.freeze
43
+ # 139s
44
+ PNAME_NS = /#{PN_PREFIX}?:/u.freeze
45
+ # 140s
46
+ PNAME_LN = /#{PNAME_NS}#{PN_LOCAL}/u.freeze
47
+ # 141s
48
+ BLANK_NODE_LABEL = /_:(?:[0-9]|#{PN_CHARS_U})(?:(?:#{PN_CHARS}|\.)*#{PN_CHARS})?/u.freeze
49
+ # 144s
50
+ # XXX: negative-lookahed for @is and @has
51
+ LANGTAG = /@(?!(?:is|has))(?:[a-zA-Z]+(?:-[a-zA-Z0-9]+)*)/u.freeze
52
+ # 19
53
+ INTEGER = /[+-]?[0-9]+/u.freeze
54
+ # 20
55
+ DECIMAL = /[+-]?(?:[0-9]*\.[0-9]+)/u.freeze
56
+ # 21
57
+ DOUBLE = /[+-]?(?:[0-9]+\.[0-9]*#{EXPONENT}|\.?[0-9]+#{EXPONENT})/u.freeze
58
+ # 22
59
+ STRING_LITERAL_SINGLE_QUOTE = /'(?:[^\'\\\n\r]|#{ECHAR}|#{UCHAR})*'/u.freeze
60
+ # 23
61
+ STRING_LITERAL_QUOTE = /"(?:[^\"\\\n\r]|#{ECHAR}|#{UCHAR})*"/u.freeze
62
+ # 24
63
+ STRING_LITERAL_LONG_SINGLE_QUOTE = /'''(?:(?:'|'')?(?:[^'\\]|#{ECHAR}|#{UCHAR}))*'''/um.freeze
64
+ # 25
65
+ STRING_LITERAL_LONG_QUOTE = /"""(?:(?:"|"")?(?:[^"\\]|#{ECHAR}|#{UCHAR}))*"""/um.freeze
66
+
67
+ # 28t
68
+ PREFIX = /@?prefix/ui.freeze
69
+ # 29t
70
+ BASE = /@?base/ui.freeze
71
+ QUICK_VAR_NAME = /\?#{PN_LOCAL}/.freeze
72
+
73
+ # 161s
74
+ WS = /(?:\s|(?:#[^\n\r]*))+/um.freeze
75
+ # 162s
76
+ ANON = /\[\s*\]/u.freeze
77
+
78
+ FORALL = /@forAll/u.freeze
79
+ end
80
+ end
@@ -1,9 +1,37 @@
1
1
  module RDF::N3
2
- class Crypto < RDF::Vocabulary("http://www.w3.org/2000/10/swap/crypto#"); end
3
- class List < RDF::Vocabulary("http://www.w3.org/2000/10/swap/list#"); end
4
- class Log < RDF::Vocabulary("http://www.w3.org/2000/10/swap/log#"); end
5
- class Math < RDF::Vocabulary("http://www.w3.org/2000/10/swap/math#"); end
6
- class Rei < RDF::Vocabulary("http://www.w3.org/2000/10/swap/reify#"); end
7
- class Str < RDF::Vocabulary("http://www.w3.org/2000/10/swap/string#"); end
8
- class Time < RDF::Vocabulary("http://www.w3.org/2000/10/swap/time#"); end
2
+ # @!parse
3
+ # # Crypto namespace
4
+ # class Crypto < RDF::Vocabulary; end
5
+ const_set("Crypto", Class.new(RDF::Vocabulary("http://www.w3.org/2000/10/swap/crypto#")))
6
+ RDF::Vocabulary.register(:crypto, Crypto)
7
+
8
+ # @!parse
9
+ # # Log namespace
10
+ # class Log < RDF::Vocabulary; end
11
+ const_set("Log", Class.new(RDF::Vocabulary("http://www.w3.org/2000/10/swap/log#")))
12
+ RDF::Vocabulary.register(:log, Log)
13
+
14
+ # @!parse
15
+ # # Math namespace
16
+ # class Math < RDF::Vocabulary; end
17
+ const_set("Math", Class.new(RDF::Vocabulary("http://www.w3.org/2000/10/swap/math#")))
18
+ RDF::Vocabulary.register(:math, Math)
19
+
20
+ # @!parse
21
+ # # Rei namespace
22
+ # class Rei < RDF::Vocabulary; end
23
+ const_set("Rei", Class.new(RDF::Vocabulary("http://www.w3.org/2000/10/swap/reify#")))
24
+ RDF::Vocabulary.register(:rei, Rei)
25
+
26
+ # @!parse
27
+ # # Str namespace
28
+ # class Str < RDF::Vocabulary; end
29
+ const_set("Str", Class.new(RDF::Vocabulary("http://www.w3.org/2000/10/swap/string#")))
30
+ RDF::Vocabulary.register(:string, Str)
31
+
32
+ # @!parse
33
+ # # Time namespace
34
+ # class Time < RDF::Vocabulary; end
35
+ const_set("Time", Class.new(RDF::Vocabulary("http://www.w3.org/2000/10/swap/time#")))
36
+ RDF::Vocabulary.register(:time, Time)
9
37
  end
@@ -49,7 +49,8 @@ module RDF::N3
49
49
  class Writer < RDF::Writer
50
50
  format RDF::N3::Format
51
51
  include RDF::Util::Logger
52
- QNAME = Meta::REGEXPS[:"http://www.w3.org/2000/10/swap/grammar/n3#qname"]
52
+ include Terminals
53
+ using Refinements
53
54
 
54
55
  # @return [RDF::Repository] Repository of statements serialized
55
56
  attr_accessor :repo
@@ -57,6 +58,9 @@ module RDF::N3
57
58
  # @return [RDF::Graph] Graph being serialized
58
59
  attr_accessor :graph
59
60
 
61
+ # @return [Array<RDF::Node>] formulae names
62
+ attr_accessor :formula_names
63
+
60
64
  ##
61
65
  # N3 Writer options
62
66
  # @see http://www.rubydoc.info/github/ruby-rdf/rdf/RDF/Writer#options-class_method
@@ -104,10 +108,13 @@ module RDF::N3
104
108
  # @yield [writer]
105
109
  # @yieldparam [RDF::Writer] writer
106
110
  def initialize(output = $stdout, **options, &block)
107
- @repo = RDF::Repository.new
111
+ @repo = RDF::N3::Repository.new
108
112
  @uri_to_pname = {}
109
113
  @uri_to_prefix = {}
110
114
  super do
115
+ if base_uri
116
+ @uri_to_prefix[base_uri.to_s.end_with?('#', '/') ? base_uri : RDF::URI("#{base_uri}#")] = nil
117
+ end
111
118
  reset
112
119
  if block_given?
113
120
  case block.arity
@@ -152,12 +159,14 @@ module RDF::N3
152
159
 
153
160
  self.reset
154
161
 
155
- log_debug {"\nserialize: repo: #{repo.size}"}
162
+ log_debug("\nserialize: repo:") {repo.size}
156
163
 
157
164
  preprocess
158
165
 
159
166
  start_document
160
167
 
168
+ @formula_names = repo.graph_names(unique: true)
169
+
161
170
  with_graph(nil) do
162
171
  count = 0
163
172
  order_subjects.each do |subject|
@@ -168,10 +177,13 @@ module RDF::N3
168
177
  end
169
178
 
170
179
  # Output any formulae not already serialized using owl:sameAs
171
- repo.graph_names.each do |graph_name|
180
+ formula_names.each do |graph_name|
172
181
  next if graph_done?(graph_name)
173
182
 
174
- log_debug {"named graph(#{graph_name})"}
183
+ # Add graph_name to @formulae
184
+ @formulae[graph_name] = true
185
+
186
+ log_debug {"formula(#{graph_name})"}
175
187
  @output.write("\n#{indent}")
176
188
  p_term(graph_name, :subject)
177
189
  @output.write(" ")
@@ -200,7 +212,7 @@ module RDF::N3
200
212
 
201
213
  #log_debug {"get_pname(#{resource}), std?}"}
202
214
  pname = case
203
- when @uri_to_pname.has_key?(uri)
215
+ when @uri_to_pname.key?(uri)
204
216
  return @uri_to_pname[uri]
205
217
  when u = @uri_to_prefix.keys.detect {|u| uri.index(u.to_s) == 0}
206
218
  # Use a defined prefix
@@ -222,34 +234,31 @@ module RDF::N3
222
234
 
223
235
  # Make sure pname is a valid pname
224
236
  if pname
225
- md = QNAME.match(pname)
237
+ md = PNAME_LN.match(pname) || PNAME_NS.match(pname)
226
238
  pname = nil unless md.to_s.length == pname.length
227
239
  end
228
240
 
229
241
  @uri_to_pname[uri] = pname
230
- rescue Addressable::URI::InvalidURIError => e
231
- raise RDF::WriterError, "Invalid URI #{resource.inspect}: #{e.message}"
232
242
  end
233
243
 
234
244
  # Take a hash from predicate uris to lists of values.
235
245
  # Sort the lists of values. Return a sorted list of properties.
236
- # @param [Hash{String => Array<Resource>}] properties A hash of Property to Resource mappings
237
- # @return [Array<String>}] Ordered list of properties. Uses predicate_order.
246
+ # @param [Hash{RDF::Term => Array<RDF::Term>}] properties A hash of Property to Resource mappings
247
+ # @return [Array<RDF::Term>}] Ordered list of properties. Uses predicate_order.
238
248
  def sort_properties(properties)
239
249
  # Make sorted list of properties
240
250
  prop_list = []
241
251
 
242
252
  predicate_order.each do |prop|
243
- next unless properties[prop.to_s]
244
- prop_list << prop.to_s
253
+ next unless properties.key?(prop)
254
+ prop_list << prop
245
255
  end
246
256
 
247
257
  properties.keys.sort.each do |prop|
248
- next if prop_list.include?(prop.to_s)
249
- prop_list << prop.to_s
258
+ next if prop_list.include?(prop)
259
+ prop_list << prop
250
260
  end
251
261
 
252
- log_debug {"sort_properties: #{prop_list.join(', ')}"}
253
262
  prop_list
254
263
  end
255
264
 
@@ -267,7 +276,11 @@ module RDF::N3
267
276
  when RDF::XSD.boolean, RDF::XSD.integer, RDF::XSD.decimal
268
277
  literal.canonicalize.to_s
269
278
  when RDF::XSD.double
270
- literal.canonicalize.to_s.sub('E', 'e') # Favor lower case exponent
279
+ if literal.nan? || literal.infinite?
280
+ quoted(literal.value) + "^^#{format_uri(literal.datatype)}"
281
+ else
282
+ literal.canonicalize.to_s
283
+ end
271
284
  else
272
285
  text = quoted(literal.value)
273
286
  text << "@#{literal.language}" if literal.has_language?
@@ -286,8 +299,8 @@ module RDF::N3
286
299
  # @param [Hash{Symbol => Object}] options
287
300
  # @return [String]
288
301
  def format_uri(uri, **options)
289
- md = uri.relativize(base_uri)
290
- log_debug("relativize") {"#{uri.to_sxp} => #{md.inspect}"} if md != uri.to_s
302
+ md = uri == base_uri ? '' : uri.relativize(base_uri)
303
+ log_debug("relativize") {"#{uri.to_sxp} => <#{md.inspect}>"} if md != uri.to_s
291
304
  md != uri.to_s ? "<#{md}>" : (get_pname(uri) || "<#{uri}>")
292
305
  end
293
306
 
@@ -298,7 +311,15 @@ module RDF::N3
298
311
  # @param [Hash{Symbol => Object}] options
299
312
  # @return [String]
300
313
  def format_node(node, **options)
301
- options[:unique_bnodes] ? node.to_unique_base : node.to_base
314
+ if node.id.match(/^([^_]+)_[^_]+_([^_]+)$/)
315
+ sn, seq = $1, $2.to_i
316
+ seq = nil if seq == 0
317
+ "_:#{sn}#{seq}"
318
+ elsif options[:unique_bnodes]
319
+ node.to_unique_base
320
+ else
321
+ node.to_base
322
+ end
302
323
  end
303
324
 
304
325
  protected
@@ -306,20 +327,21 @@ module RDF::N3
306
327
  def start_document
307
328
  @output.write("@base <#{base_uri}> .\n") unless base_uri.to_s.empty?
308
329
 
309
- log_debug {"start_document: prefixes #{prefixes.inspect}"}
330
+ log_debug("start_document: prefixes") { prefixes.inspect}
310
331
  prefixes.keys.sort_by(&:to_s).each do |prefix|
311
332
  @output.write("@prefix #{prefix}: <#{prefixes[prefix]}> .\n")
312
333
  end
313
334
 
335
+ # Universals and extentials at top-level
314
336
  unless @universals.empty?
315
- log_debug {"start_document: universals #{@universals.inspect}"}
337
+ log_debug("start_document: universals") { @universals.inspect}
316
338
  terms = @universals.map {|v| format_uri(RDF::URI(v.name.to_s))}
317
339
  @output.write("@forAll #{terms.join(', ')} .\n")
318
340
  end
319
341
 
320
342
  unless @existentials.empty?
321
- log_debug {"start_document: universals #{@existentials.inspect}"}
322
- terms = @existentials.map {|v| format_uri(RDF::URI(v.name.to_s))}
343
+ log_debug("start_document: existentials") { @existentials.inspect}
344
+ terms = @existentials.map {|v| format_uri(RDF::URI(v.name.to_s.sub(/_ext$/, '')))}
323
345
  @output.write("@forSome #{terms.join(', ')} .\n")
324
346
  end
325
347
  end
@@ -331,7 +353,17 @@ module RDF::N3
331
353
  # Defines order of predicates to to emit at begninning of a resource description. Defaults to
332
354
  # [rdf:type, rdfs:label, dc:title]
333
355
  # @return [Array<URI>]
334
- def predicate_order; [RDF.type, RDF::RDFS.label, RDF::URI("http://purl.org/dc/terms/title")]; end
356
+ def predicate_order
357
+ [
358
+ RDF.type,
359
+ RDF::RDFS.label,
360
+ RDF::RDFS.comment,
361
+ RDF::URI("http://purl.org/dc/terms/title"),
362
+ RDF::URI("http://purl.org/dc/terms/description"),
363
+ RDF::OWL.sameAs,
364
+ RDF::N3::Log.implies
365
+ ]
366
+ end
335
367
 
336
368
  # Order subjects for output. Override this to output subjects in another order.
337
369
  #
@@ -342,7 +374,7 @@ module RDF::N3
342
374
  subjects = []
343
375
 
344
376
  # Start with base_uri
345
- if base_uri && @subjects.keys.include?(base_uri)
377
+ if base_uri && @subjects.keys.select(&:uri?).include?(base_uri)
346
378
  subjects << base_uri
347
379
  seen[base_uri] = true
348
380
  end
@@ -350,24 +382,26 @@ module RDF::N3
350
382
  # Add distinguished classes
351
383
  top_classes.each do |class_uri|
352
384
  graph.query({predicate: RDF.type, object: class_uri}).
353
- map {|st| st.subject}.
354
- sort.
355
- uniq.
356
- each do |subject|
357
- log_debug("order_subjects") {subject.to_sxp}
358
- subjects << subject
359
- seen[subject] = true
360
- end
385
+ map {|st| st.subject}.sort.uniq.each do |subject|
386
+ log_debug("order_subjects") {subject.to_sxp}
387
+ subjects << subject
388
+ seen[subject] = true
389
+ end
390
+ end
391
+
392
+ # Add formulae which are subjects in this graph
393
+ @formulae.each_key do |bn|
394
+ next unless @subjects.key?(bn)
395
+ subjects << bn
396
+ seen[bn] = true
361
397
  end
362
398
 
363
399
  # Mark as seen lists that are part of another list
364
- @lists.values.map(&:statements).
365
- flatten.each do |st|
366
- seen[st.object] = true if @lists.has_key?(st.object)
367
- end
400
+ @lists.values.flatten.each do |v|
401
+ seen[v] = true if @lists.key?(v)
402
+ end
368
403
 
369
- # List elements which are bnodes should not be targets for top-level serialization
370
- list_elements = @lists.values.map(&:to_a).flatten.select(&:node?).compact
404
+ list_elements = [] # Lists may be top-level elements
371
405
 
372
406
  # Sort subjects by resources over bnodes, ref_counts and the subject URI itself
373
407
  recursable = (@subjects.keys - list_elements).
@@ -391,7 +425,7 @@ module RDF::N3
391
425
  @options[:prefixes] = {} # Will define actual used when matched
392
426
  repo.each {|statement| preprocess_statement(statement)}
393
427
 
394
- vars = repo.enum_term.to_a.uniq.select {|r| r.is_a?(RDF::Query::Variable)}
428
+ vars = repo.enum_term.to_a.uniq.select {|r| r.is_a?(RDF::Query::Variable) && !r.to_s.end_with?('_quick')}
395
429
  @universals = vars.reject(&:existential?)
396
430
  @existentials = vars - @universals
397
431
  end
@@ -400,7 +434,7 @@ module RDF::N3
400
434
  # prefixes.
401
435
  # @param [Statement] statement
402
436
  def preprocess_statement(statement)
403
- #log_debug {"preprocess: #{statement.inspect}"}
437
+ #log_debug("preprocess") {statement.inspect}
404
438
 
405
439
  # Pre-fetch pnames, to fill prefixes
406
440
  get_pname(statement.subject)
@@ -410,24 +444,13 @@ module RDF::N3
410
444
  end
411
445
 
412
446
  # Perform graph-specific preprocessing
413
- # @param [Statement]
447
+ # @param [Statement] statement
414
448
  def preprocess_graph_statement(statement)
415
449
  bump_reference(statement.object)
416
450
  # Count properties of this subject
417
451
  @subjects[statement.subject] ||= {}
418
452
  @subjects[statement.subject][statement.predicate] ||= 0
419
453
  @subjects[statement.subject][statement.predicate] += 1
420
-
421
- # Collect lists
422
- if statement.predicate == RDF.first
423
- l = RDF::List.new(subject: statement.subject, graph: graph)
424
- @lists[statement.subject] = l if l.valid?
425
- end
426
-
427
- if statement.object == RDF.nil || statement.subject == RDF.nil
428
- # Add an entry for the list tail
429
- @lists[RDF.nil] ||= RDF::List[]
430
- end
431
454
  end
432
455
 
433
456
  # Returns indent string multiplied by the depth
@@ -464,35 +487,33 @@ module RDF::N3
464
487
  private
465
488
 
466
489
  # Checks if l is a valid RDF list, i.e. no nodes have other properties.
467
- def is_valid_list?(l)
468
- #log_debug("is_valid_list?") {l.inspect}
469
- return @lists[l] && @lists[l].valid?
470
- end
471
-
472
- def do_list(l, position)
473
- list = @lists[l]
474
- log_debug("do_list") {list.inspect}
475
- subject_done(RDF.nil)
476
- index = 0
477
- list.each_statement do |st|
478
- next unless st.predicate == RDF.first
479
- log_debug {" list this: #{st.subject} first: #{st.object}[#{position}]"}
480
- @output.write(" ") if index > 0
481
- path(st.object, position)
482
- subject_done(st.subject)
483
- position = :object
484
- index += 1
485
- end
490
+ def collection?(l)
491
+ return @lists.key?(l) || l.list?
486
492
  end
487
493
 
488
494
  def collection(node, position)
489
- return false if !is_valid_list?(node)
490
- return false if position == :subject && ref_count(node) > 0
491
- return false if position == :object && prop_count(node) > 0
492
- #log_debug("collection") {"#{node.to_sxp}, #{position}"}
495
+ return false if !collection?(node)
496
+ log_debug("collection") do
497
+ "#{node.to_sxp}, " +
498
+ "pos: #{position}, " +
499
+ "rc: #{ref_count(node)}"
500
+ end
493
501
 
494
502
  @output.write("(")
495
- log_depth {do_list(node, position)}
503
+ log_depth do
504
+ list = node.list? ? node : @lists[node]
505
+ log_debug("collection") {list.inspect}
506
+ subject_done(RDF.nil)
507
+ subject_done(node)
508
+ index = 0
509
+ list.each do |li|
510
+ log_debug("(list first)") {"#{li}[#{position}]"}
511
+ @output.write(" ") if index > 0
512
+ path(li, :object)
513
+ subject_done(li)
514
+ index += 1
515
+ end
516
+ end
496
517
  @output.write(')')
497
518
  end
498
519
 
@@ -500,7 +521,11 @@ module RDF::N3
500
521
  def p_term(resource, position)
501
522
  #log_debug("p_term") {"#{resource.to_sxp}, #{position}"}
502
523
  l = if resource.is_a?(RDF::Query::Variable)
503
- format_term(RDF::URI(resource.name.to_s.sub(/^\$/, '')))
524
+ if resource.to_s.end_with?('_quick')
525
+ '?' + RDF::URI(resource.name).fragment.sub(/_quick$/, '')
526
+ else
527
+ format_term(RDF::URI(resource.name.to_s.sub(/_ext$/, '')))
528
+ end
504
529
  elsif resource == RDF.nil
505
530
  "()"
506
531
  else
@@ -515,9 +540,9 @@ module RDF::N3
515
540
  log_debug("path") do
516
541
  "#{resource.to_sxp}, " +
517
542
  "pos: #{position}, " +
518
- "{}?: #{formula?(resource, position)}, " +
519
- "()?: #{is_valid_list?(resource)}, " +
520
- "[]?: #{blankNodePropertyList?(resource, position)}, " +
543
+ "{}?: #{formula?(resource, position).inspect}, " +
544
+ "()?: #{collection?(resource).inspect}, " +
545
+ "[]?: #{blankNodePropertyList?(resource, position).inspect}, " +
521
546
  "rc: #{ref_count(resource)}"
522
547
  end
523
548
  raise RDF::WriterError, "Cannot serialize resource '#{resource}'" unless
@@ -537,7 +562,7 @@ module RDF::N3
537
562
  when RDF::N3::Log.implies
538
563
  @output.write("=>")
539
564
  else
540
- path(resource, :predicate)
565
+ log_depth {path(resource, :predicate)}
541
566
  end
542
567
  end
543
568
 
@@ -546,13 +571,15 @@ module RDF::N3
546
571
  log_debug("objectList") {objects.inspect}
547
572
  return if objects.empty?
548
573
 
549
- objects.each_with_index do |obj, i|
550
- if i > 0 && (formula?(obj, :object) || blankNodePropertyList?(obj, :object))
551
- @output.write ", "
552
- elsif i > 0
553
- @output.write ",\n#{indent(4)}"
574
+ log_depth do
575
+ objects.each_with_index do |obj, i|
576
+ if i > 0 && (formula?(obj, :object) || blankNodePropertyList?(obj, :object))
577
+ @output.write ", "
578
+ elsif i > 0
579
+ @output.write ",\n#{indent(4)}"
580
+ end
581
+ path(obj, :object)
554
582
  end
555
- path(obj, :object)
556
583
  end
557
584
  end
558
585
 
@@ -560,29 +587,24 @@ module RDF::N3
560
587
  # @return [Integer] the number of properties serialized
561
588
  def predicateObjectList(subject, from_bpl = false)
562
589
  properties = {}
563
- if subject.variable?
564
- # Can't query on variable
565
- @graph.enum_statement.select {|s| s.subject.equal?(subject)}.each do |st|
566
- (properties[st.predicate.to_s] ||= []) << st.object
567
- end
568
- else
569
- @graph.query({subject: subject}) do |st|
570
- (properties[st.predicate.to_s] ||= []) << st.object
571
- end
590
+ @graph.enum_statement.select {|s| s.subject.sameTerm?(subject)}.each do |st|
591
+ (properties[st.predicate] ||= []) << st.object
572
592
  end
573
593
 
574
594
  prop_list = sort_properties(properties)
575
- prop_list -= [RDF.first.to_s, RDF.rest.to_s] if @lists.include?(subject)
576
- log_debug("predicateObjectList") {prop_list.inspect}
595
+ prop_list -= [RDF.first, RDF.rest] if @lists.key?(subject)
596
+ log_debug("predicateObjectList") { "subject: #{subject.to_sxp}, properties: #{prop_list.join(', ')}" }
577
597
  return 0 if prop_list.empty?
578
598
 
579
599
  @output.write("\n#{indent(2)}") if properties.keys.length > 1 && from_bpl
580
- prop_list.each_with_index do |prop, i|
581
- begin
582
- @output.write(";\n#{indent(2)}") if i > 0
583
- predicate(RDF::URI.intern(prop))
584
- @output.write(" ")
585
- objectList(properties[prop])
600
+ log_depth do
601
+ prop_list.each_with_index do |prop, i|
602
+ begin
603
+ @output.write(";\n#{indent(2)}") if i > 0
604
+ predicate(prop)
605
+ @output.write(" ")
606
+ objectList(properties[prop])
607
+ end
586
608
  end
587
609
  end
588
610
  properties.keys.length
@@ -592,10 +614,9 @@ module RDF::N3
592
614
  def blankNodePropertyList?(resource, position)
593
615
  resource.node? &&
594
616
  !formula?(resource, position) &&
595
- !is_valid_list?(resource) &&
617
+ !collection?(resource) &&
596
618
  (!is_done?(resource) || position == :subject) &&
597
619
  ref_count(resource) == (position == :object ? 1 : 0) &&
598
- resource_in_single_graph?(resource) &&
599
620
  !repo.has_graph?(resource)
600
621
  end
601
622
 
@@ -604,20 +625,15 @@ module RDF::N3
604
625
 
605
626
  log_debug("blankNodePropertyList") {resource.to_sxp}
606
627
  subject_done(resource)
607
- @output.write(position == :subject ? "\n#{indent}[" : '[')
628
+ @output.write((position == :subject ? "\n#{indent}[" : '['))
608
629
  num_props = log_depth {predicateObjectList(resource, true)}
609
- @output.write((num_props > 1 ? "\n#{indent(2)}" : "") + (position == :object ? ']' : '] .'))
630
+ @output.write((num_props > 1 ? "\n#{indent(2)}" : "") + (position == :subject ? '] .' : ']'))
610
631
  true
611
632
  end
612
633
 
613
634
  # Can subject be represented as a formula?
614
635
  def formula?(resource, position)
615
- (resource.node? || position == :graph_name) &&
616
- repo.has_graph?(resource) &&
617
- !is_valid_list?(resource) &&
618
- (!is_done?(resource) || position == :subject) &&
619
- ref_count(resource) == (position == :object ? 1 : 0) &&
620
- resource_in_single_graph?(resource)
636
+ !!@formulae[resource]
621
637
  end
622
638
 
623
639
  def formula(resource, position)
@@ -626,9 +642,9 @@ module RDF::N3
626
642
  log_debug("formula") {resource.to_sxp}
627
643
  subject_done(resource)
628
644
  @output.write('{')
645
+ count = 0
629
646
  log_depth do
630
647
  with_graph(resource) do
631
- count = 0
632
648
  order_subjects.each do |subject|
633
649
  unless is_done?(subject)
634
650
  statement(subject, count)
@@ -637,7 +653,7 @@ module RDF::N3
637
653
  end
638
654
  end
639
655
  end
640
- @output.write((graph.count > 1 ? "\n#{indent}" : "") + '}')
656
+ @output.write((count > 0 ? "#{indent}" : "") + '}')
641
657
  true
642
658
  end
643
659
 
@@ -647,25 +663,22 @@ module RDF::N3
647
663
  path(subject, :subject)
648
664
  @output.write(" ")
649
665
  num_props = predicateObjectList(subject)
650
- @output.write("#{num_props > 0 ? ' ' : ''}.")
666
+ @output.puts("#{num_props > 0 ? ' ' : ''}.")
651
667
  true
652
668
  end
653
669
 
654
670
  def statement(subject, count)
655
- log_debug("statement") {"#{subject.to_sxp}, bnodePL?: #{blankNodePropertyList?(subject, :subject)}"}
671
+ log_debug("statement") do
672
+ "#{subject.to_sxp}, " +
673
+ "{}?: #{formula?(subject, :subject).inspect}, " +
674
+ "()?: #{collection?(subject).inspect}, " +
675
+ "[]?: #{blankNodePropertyList?(subject, :subject).inspect}, "
676
+ end
656
677
  subject_done(subject)
657
678
  blankNodePropertyList(subject, :subject) || triples(subject)
658
679
  @output.puts if count > 0 || graph.graph_name
659
680
  end
660
681
 
661
- # Return the number of statements having this resource as a subject other than for list properties
662
- # @return [Integer]
663
- def prop_count(subject)
664
- @subjects.fetch(subject, {}).
665
- reject {|k, v| [RDF.type, RDF.first, RDF.rest].include?(k)}.
666
- values.reduce(:+) || 0
667
- end
668
-
669
682
  # Return the number of times this node has been referenced in the object position
670
683
  # @return [Integer]
671
684
  def ref_count(node)
@@ -697,19 +710,6 @@ module RDF::N3
697
710
  @graphs[graph_name] = true
698
711
  end
699
712
 
700
- def resource_in_single_graph?(resource)
701
- if resource.variable?
702
- graph_names = @repo.
703
- enum_statement.
704
- select {|st| st.subject.equal?(resource) || st.object.equal?(resource)}.
705
- map(&:graph_name)
706
- else
707
- graph_names = @repo.query({subject: resource}).map(&:graph_name)
708
- graph_names += @repo.query({object: resource}).map(&:graph_name)
709
- end
710
- graph_names.uniq.length <= 1
711
- end
712
-
713
713
  # Process a graph projection
714
714
  def with_graph(graph_name)
715
715
  old_lists, @lists = @lists, {}
@@ -717,21 +717,81 @@ module RDF::N3
717
717
  old_serialized, @serialized = @serialized, {}
718
718
  old_subjects, @subjects = @subjects, {}
719
719
  old_graph, @graph = @graph, repo.project_graph(graph_name)
720
+ old_formulae, @formulae = @formulae, {}
720
721
 
721
722
  graph_done(graph_name)
722
723
 
723
- graph.each {|statement| preprocess_graph_statement(statement)}
724
+ lists = {}
725
+ graph.each do |statement|
726
+ preprocess_graph_statement(statement)
727
+ [statement.subject, statement.object].each do |resource|
728
+ @formulae[resource] = true if
729
+ resource.node? &&
730
+ (formula_names.include?(resource) || resource.id.start_with?('_form_'))
731
+
732
+ # First-class list may have members which are formulae, and need reference counts
733
+ if resource.list?
734
+ resource.each_descendant do |term|
735
+ bump_reference(term)
736
+ @formulae[term] = true if
737
+ term.node? &&
738
+ (formula_names.include?(term) || term.id.start_with?('_form_'))
739
+ end
740
+ end
741
+ end
742
+
743
+ # Collect list elements
744
+ if [RDF.first, RDF.rest].include?(statement.predicate) && statement.subject.node?
745
+ lists[statement.subject] ||= {}
746
+ lists[statement.subject][statement.predicate] = statement.object
747
+ end
748
+ end
749
+
750
+ # Remove list entries after head with more than two properties (other than rdf:type)
751
+ rests = lists.values.map {|props| props[RDF.rest]}
752
+
753
+ # Remove non-head lists that have too many properties
754
+ rests.select do |bn|
755
+ pc = 0
756
+ @subjects.fetch(bn, {}).each do |pred, count|
757
+ next if pred == RDF.type
758
+ pc += count
759
+ end
760
+ lists.delete(bn) if pc > 2
761
+ end
762
+
763
+ # Values for this list element, recursive
764
+ def list_values(bn, lists)
765
+ raise "no list" unless lists.has_key?(bn)
766
+ first, rest = lists[bn][RDF.first], lists[bn][RDF.rest]
767
+ (rest == RDF.nil ? [] : list_values(rest, lists)).unshift(first)
768
+ rescue
769
+ lists.delete(bn)
770
+ raise $!
771
+ end
772
+
773
+ # Create value arrays for each entry
774
+ lists.each do |bn, props|
775
+ begin
776
+ @lists[bn] = list_values(bn, lists)
777
+ rescue
778
+ # Skip this list element, if it raises an exception
779
+ lists.delete(bn)
780
+ end
781
+ end
782
+
783
+ # Mark all remaining rests done
784
+ rests.each {|bn| subject_done(bn) if lists.include?(bn)}
724
785
 
725
- # Remove lists that are referenced and have non-list properties;
726
- # these are legal, but can't be serialized as lists
727
- @lists.reject! do |node, list|
728
- ref_count(node) > 0 && prop_count(node) > 0 ||
729
- list.subjects.any? {|elt| !resource_in_single_graph?(elt)}
786
+ # Remove entries that are referenced as rdf:rest of some entry
787
+ lists.each do |bn, props|
788
+ @lists.delete(props[RDF.rest])
730
789
  end
731
790
 
791
+ # Record nodes in subject or object
732
792
  yield
733
793
  ensure
734
- @graph, @lists, @references, @serialized, @subjects = old_graph, old_lists, old_references, old_serialized, old_subjects
794
+ @graph, @lists, @references, @serialized, @subjects, @formulae = old_graph, old_lists, old_references, old_serialized, old_subjects, old_formulae
735
795
  end
736
796
  end
737
797
  end