rdf-turtle 3.1.0 → 3.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 23a25ff951065479aaa42feb25d8646ed7e87270556e759ea10d219e24b4ac72
4
- data.tar.gz: ee4be6acf9c89792f5df29e018a3ab22c667477de4e95178517a4002da68707b
3
+ metadata.gz: 6a527c2357a49551b152847f83943ac2e215ac10b2c4c76d0727fd42a85026cd
4
+ data.tar.gz: 0d8fa400deaf2bbc4c8ea996b0a37d5ead4fbb00519cc95ac84dad58052125e9
5
5
  SHA512:
6
- metadata.gz: 34c956ae30fbbe235344fc2ada8ed41539e912022b6ec0699afde1108d98d6b83a8c3008bc131535e0f96ef9b9f481079aa58dc5f8b384b250b20b4b33b72d90
7
- data.tar.gz: 405cfd25f8899631bac219096670d36969602329c7f3ea4905f0a36a47835ee6ac8aac65c163c6b8d4cc4ce284d2e162cb00ff1e7db193c8bf072c00c84c77dd
6
+ metadata.gz: 2493ae5d441f97c947249a58a7fd8a49ce8aca6f675b08fc5b223a8f3bab2037d2004416ddca6f0ea78ad4dca9199b5c31c92f14b7068085222a7b191de44deb
7
+ data.tar.gz: c4db12c8fe5e8401f82ae472ee343474704bb19a1bf202b1e551f29be6b2a874697e046a0c6bd4a91fe0a29c3428a02fe2e7fd2dff249c44a783ee17bdc9481c
data/README.md CHANGED
@@ -19,6 +19,7 @@ Install with `gem install rdf-turtle`
19
19
  * Implements a complete parser for [Turtle][].
20
20
  * Compatible with Ruby >= 2.2.2.
21
21
  * Optional streaming writer, to serialize large graphs
22
+ * Provisional support for [Turtle*][RDF*].
22
23
 
23
24
  ## Usage
24
25
  Instantiate a reader from a local file:
@@ -35,6 +36,50 @@ Write a graph to a file:
35
36
  writer << graph
36
37
  end
37
38
 
39
+ ## Turtle* (RDFStar)
40
+
41
+ Both reader and writer include provisional support for [Turtle*][RDF*].
42
+
43
+ Internally, an `RDF::Statement` is treated as another resource, along with `RDF::URI` and `RDF::Node`, which allows an `RDF::Statement` to have a `#subject` or `#object` which is also an `RDF::Statement`.
44
+
45
+ **Note: This feature is subject to change or elimination as the standards process progresses.**
46
+
47
+ ### Serializing a Graph containing embedded statements
48
+
49
+ require 'rdf/turtle'
50
+ statement = RDF::Statement(RDF::URI('bob'), RDF::Vocab::FOAF.age, RDF::Literal(23))
51
+ graph = RDF::Graph.new << [statement, RDF::URI("ex:certainty"), RDF::Literal(0.9)]
52
+ graph.dump(:ttl, validate: false, standard_prefixes: true)
53
+ # => '<<<bob> foaf:age 23>> <ex:certainty> 9.0e-1 .'
54
+
55
+ ### Reading a Graph containing embedded statements
56
+
57
+ By default, the Turtle reader will reject a document containing a subject resource.
58
+
59
+ ttl = %(
60
+ @prefix foaf: <http://xmlns.com/foaf/0.1/> .
61
+ @prefix ex: <http://example.com/> .
62
+ <<<bob> foaf:age 23>> ex:certainty 9.0e-1 .
63
+ )
64
+ graph = RDF::Graph.new do |graph|
65
+ RDF::Turtle::Reader.new(ttl) {|reader| graph << reader}
66
+ end
67
+ # => RDF::ReaderError
68
+
69
+ Readers support a `rdfstar` option with either `:PG` (Property Graph) or `:SA` (Separate Assertions) modes. In `:PG` mode, statements that are used in the subject or object positions are also implicitly added to the graph:
70
+
71
+ graph = RDF::Graph.new do |graph|
72
+ RDF::Turtle::Reader.new(ttl, rdfstar: :PG) {|reader| graph << reader}
73
+ end
74
+ graph.count #=> 2
75
+
76
+ When using the `:SA` mode, only one statement is asserted, although the reified statement is contained within the graph.
77
+
78
+ graph = RDF::Graph.new do |graph|
79
+ RDF::Turtle::Reader.new(ttl, rdfstar: :SA) {|reader| graph << reader}
80
+ end
81
+ graph.count #=> 1
82
+
38
83
  ## Documentation
39
84
  Full documentation available on [Rubydoc.info][Turtle doc]
40
85
 
@@ -133,6 +178,7 @@ A copy of the [Turtle EBNF][] and derived parser files are included in the repos
133
178
  [Backports]: https://rubygems.org/gems/backports
134
179
  [N-Triples]: https://www.w3.org/TR/rdf-testcases/#ntriples
135
180
  [Turtle]: https://www.w3.org/TR/2012/WD-turtle-20120710/
181
+ [RDF*]: https://lists.w3.org/Archives/Public/public-rdf-star/
136
182
  [Turtle doc]: https://rubydoc.info/github/ruby-rdf/rdf-turtle/master/file/README.md
137
183
  [Turtle EBNF]: https://dvcs.w3.org/hg/rdf/file/default/rdf-turtle/turtle.bnf
138
184
  [Freebase Dumps]: https://developers.google.com/freebase/data
data/VERSION CHANGED
@@ -1 +1 @@
1
- 3.1.0
1
+ 3.1.1
@@ -25,7 +25,7 @@ module RDF::Turtle
25
25
  terminal(:STRING_LITERAL_SINGLE_QUOTE, STRING_LITERAL_SINGLE_QUOTE, unescape: true)
26
26
 
27
27
  # String terminals
28
- terminal(nil, %r([\(\),.;\[\]Aa]|\^\^|true|false))
28
+ terminal(nil, %r([\(\),.;\[\]Aa]|\^\^|true|false|<<|>>))
29
29
 
30
30
  terminal(:PREFIX, PREFIX)
31
31
  terminal(:BASE, BASE)
@@ -87,7 +87,7 @@ module RDF::Turtle
87
87
  @options = {
88
88
  anon_base: "b0",
89
89
  whitespace: WS,
90
- log_depth: 0,
90
+ depth: 0,
91
91
  }.merge(@options)
92
92
  @prod_stack = []
93
93
 
@@ -185,7 +185,7 @@ module RDF::Turtle
185
185
 
186
186
  # Create a literal
187
187
  def literal(value, **options)
188
- log_debug("literal") do
188
+ log_debug("literal", depth: @options[:depth]) do
189
189
  "value: #{value.inspect}, " +
190
190
  "options: #{options.inspect}, " +
191
191
  "validate: #{validate?.inspect}, " +
@@ -221,7 +221,7 @@ module RDF::Turtle
221
221
  ''
222
222
  end
223
223
  suffix = suffix.to_s.sub(/^\#/, "") if base.index("#")
224
- log_debug("pname") {"base: '#{base}', suffix: '#{suffix}'"}
224
+ log_debug("pname", depth: options[:depth]) {"base: '#{base}', suffix: '#{suffix}'"}
225
225
  process_iri(base + suffix.to_s)
226
226
  end
227
227
 
@@ -283,7 +283,7 @@ module RDF::Turtle
283
283
  terminated = token.value == '@prefix'
284
284
  error("Expected PNAME_NS", production: :prefix, token: pfx) unless pfx === :PNAME_NS
285
285
  error("Expected IRIREF", production: :prefix, token: iri) unless iri === :IRIREF
286
- log_debug("prefixID") {"Defined prefix #{pfx.inspect} mapping to #{iri.inspect}"}
286
+ log_debug("prefixID", depth: options[:depth]) {"Defined prefix #{pfx.inspect} mapping to #{iri.inspect}"}
287
287
  prefix(pfx.value[0..-2], process_iri(iri))
288
288
  error("prefixId", "#{token} should be downcased") if token.value.start_with?('@') && token.value != '@prefix'
289
289
 
@@ -362,6 +362,7 @@ module RDF::Turtle
362
362
  read_iri ||
363
363
  read_BlankNode ||
364
364
  read_collection ||
365
+ read_rdfstar ||
365
366
  error( "Expected subject", production: :subject, token: @lexer.first)
366
367
  end
367
368
  end
@@ -373,7 +374,8 @@ module RDF::Turtle
373
374
  read_BlankNode ||
374
375
  read_collection ||
375
376
  read_blankNodePropertyList ||
376
- read_literal
377
+ read_literal ||
378
+ read_rdfstar
377
379
 
378
380
  add_statement(:object, RDF::Statement(subject, predicate, object)) if subject && predicate
379
381
  object
@@ -381,6 +383,28 @@ module RDF::Turtle
381
383
  end
382
384
  end
383
385
 
386
+ # Read an RDF* reified statement
387
+ # @return [RDF::Statement]
388
+ def read_rdfstar
389
+ return unless @options[:rdfstar]
390
+ if @lexer.first.value == '<<'
391
+ prod(:rdfstar) do
392
+ @lexer.shift # eat <<
393
+ subject = read_subject || error("Failed to parse subject", production: :rdfstar, token: @lexer.first)
394
+ predicate = read_verb || error("Failed to parse predicate", production: :rdfstar, token: @lexer.first)
395
+ object = read_object || error("Failed to parse object", production: :rdfstar, token: @lexer.first)
396
+ unless @lexer.first.value == '>>'
397
+ error("Failed to end of embedded triple", production: :rdfstar, token: @lexer.first)
398
+ end
399
+ @lexer.shift
400
+ statement = RDF::Statement(subject, predicate, object)
401
+ # Emit the statement if in Property Graph mode
402
+ add_statement(:rdfstar, statement) if @options[:rdfstar] == :PG
403
+ statement
404
+ end
405
+ end
406
+ end
407
+
384
408
  # @return [RDF::Literal]
385
409
  def read_literal
386
410
  error("Unexpected end of file", production: :literal) unless token = @lexer.first
@@ -431,7 +455,7 @@ module RDF::Turtle
431
455
  if token === '['
432
456
  prod(:blankNodePropertyList, %{]}) do
433
457
  @lexer.shift
434
- log_info("blankNodePropertyList") {"token: #{token.inspect}"}
458
+ log_info("blankNodePropertyList", depth: options[:depth]) {"token: #{token.inspect}"}
435
459
  node = bnode
436
460
  read_predicateObjectList(node)
437
461
  error("blankNodePropertyList", "Expected closing ']'") unless @lexer.first === ']'
@@ -447,7 +471,7 @@ module RDF::Turtle
447
471
  prod(:collection, %{)}) do
448
472
  @lexer.shift
449
473
  token = @lexer.first
450
- log_info("collection") {"token: #{token.inspect}"}
474
+ log_info("collection", depth: options[:depth]) {"token: #{token.inspect}"}
451
475
  objects = []
452
476
  while object = read_object
453
477
  objects << object
@@ -483,8 +507,8 @@ module RDF::Turtle
483
507
 
484
508
  def prod(production, recover_to = [])
485
509
  @prod_stack << {prod: production, recover_to: recover_to}
486
- @options[:log_depth] += 1
487
- log_recover("#{production}(start)") {"token: #{@lexer.first.inspect}"}
510
+ @options[:depth] += 1
511
+ log_recover("#{production}(start)", depth: options[:depth]) {"token: #{@lexer.first.inspect}"}
488
512
  yield
489
513
  rescue EBNF::LL1::Lexer::Error, SyntaxError, Recovery => e
490
514
  # Lexer encountered an illegal token or the parser encountered
@@ -504,13 +528,13 @@ module RDF::Turtle
504
528
  end
505
529
  end
506
530
  raise EOFError, "End of input found when recovering" if @lexer.first.nil?
507
- log_debug("recovery", "current token: #{@lexer.first.inspect}")
531
+ log_debug("recovery", "current token: #{@lexer.first.inspect}", depth: options[:depth])
508
532
 
509
533
  unless e.is_a?(Recovery)
510
534
  # Get the list of follows for this sequence, this production and the stacked productions.
511
- log_debug("recovery", "stack follows:")
535
+ log_debug("recovery", "stack follows:", depth: options[:depth])
512
536
  @prod_stack.reverse.each do |prod|
513
- log_debug("recovery", level: 4) {" #{prod[:prod]}: #{prod[:recover_to].inspect}"}
537
+ log_debug("recovery", level: 4, depth: options[:depth]) {" #{prod[:prod]}: #{prod[:recover_to].inspect}"}
514
538
  end
515
539
  end
516
540
 
@@ -520,9 +544,9 @@ module RDF::Turtle
520
544
  # Skip tokens until one is found in follows
521
545
  while (token = (@lexer.first rescue @lexer.recover)) && follows.none? {|t| token === t}
522
546
  skipped = @lexer.shift
523
- log_debug("recovery") {"skip #{skipped.inspect}"}
547
+ log_debug("recovery", depth: options[:depth]) {"skip #{skipped.inspect}"}
524
548
  end
525
- log_debug("recovery") {"found #{token.inspect} in follows"}
549
+ log_debug("recovery", depth: options[:depth]) {"found #{token.inspect} in follows"}
526
550
 
527
551
  # Re-raise the error unless token is a follows of this production
528
552
  raise Recovery unless Array(recover_to).any? {|t| token === t}
@@ -530,8 +554,8 @@ module RDF::Turtle
530
554
  # Skip that token to get something reasonable to start the next production with
531
555
  @lexer.shift
532
556
  ensure
533
- log_info("#{production}(finish)")
534
- @options[:log_depth] -= 1
557
+ log_info("#{production}(finish)", depth: options[:depth])
558
+ @options[:depth] -= 1
535
559
  @prod_stack.pop
536
560
  end
537
561
 
@@ -554,7 +578,8 @@ module RDF::Turtle
554
578
  lineno: lineno,
555
579
  token: options[:token],
556
580
  production: options[:production],
557
- exception: SyntaxError)
581
+ depth: options[:depth],
582
+ exception: SyntaxError,)
558
583
  end
559
584
 
560
585
  # Used for internal error recovery
@@ -313,6 +313,17 @@ module RDF::Turtle
313
313
  options[:unique_bnodes] ? node.to_unique_base : node.to_base
314
314
  end
315
315
 
316
+ ##
317
+ # Returns an embedded triples
318
+ #
319
+ # @param [RDF::Statement] statement
320
+ # @param [Hash{Symbol => Object}] options
321
+ # @return [String]
322
+ def format_rdfstar(statement, **options)
323
+ log_debug("rdfstar") {"#{statement.to_ntriples}"}
324
+ "<<%s %s %s>>" % statement.to_a.map { |value| format_term(value, **options) }
325
+ end
326
+
316
327
  protected
317
328
  # Output @base and @prefix definitions
318
329
  def start_document
@@ -457,7 +468,7 @@ module RDF::Turtle
457
468
 
458
469
  # Can subject be represented as a blankNodePropertyList?
459
470
  def blankNodePropertyList?(resource, position)
460
- resource.node? &&
471
+ !resource.statement? && resource.node? &&
461
472
  !is_valid_list?(resource) &&
462
473
  (!is_done?(resource) || position == :subject) &&
463
474
  ref_count(resource) == (position == :object ? 1 : 0)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rdf-turtle
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.0
4
+ version: 3.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gregg Kellogg
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-12-10 00:00:00.000000000 Z
11
+ date: 2020-05-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rdf
@@ -17,6 +17,9 @@ dependencies:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
19
  version: '3.1'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 3.1.2
20
23
  type: :runtime
21
24
  prerelease: false
22
25
  version_requirements: !ruby/object:Gem::Requirement
@@ -24,6 +27,9 @@ dependencies:
24
27
  - - "~>"
25
28
  - !ruby/object:Gem::Version
26
29
  version: '3.1'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 3.1.2
27
33
  - !ruby/object:Gem::Dependency
28
34
  name: ebnf
29
35
  requirement: !ruby/object:Gem::Requirement
@@ -169,7 +175,7 @@ files:
169
175
  - lib/rdf/turtle/terminals.rb
170
176
  - lib/rdf/turtle/version.rb
171
177
  - lib/rdf/turtle/writer.rb
172
- homepage: https://ruby-rdf.github.com/rdf-turtle
178
+ homepage: https://github.com/ruby-rdf/rdf-turtle
173
179
  licenses:
174
180
  - Unlicense
175
181
  metadata: {}
@@ -188,7 +194,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
188
194
  - !ruby/object:Gem::Version
189
195
  version: '0'
190
196
  requirements: []
191
- rubygems_version: 3.0.6
197
+ rubygems_version: 3.1.3
192
198
  signing_key:
193
199
  specification_version: 4
194
200
  summary: Turtle reader/writer for Ruby.