rdf-turtle 3.1.2 → 3.2.1
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.
- checksums.yaml +4 -4
- data/README.md +33 -20
- data/VERSION +1 -1
- data/lib/rdf/turtle/format.rb +1 -0
- data/lib/rdf/turtle/reader.rb +102 -31
- data/lib/rdf/turtle/writer.rb +63 -41
- data/lib/rdf/turtle.rb +1 -1
- metadata +41 -42
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 57d92c642076069a911db896415fa9dbd3203b6c82a9074e0fc8a53dafb2389d
|
|
4
|
+
data.tar.gz: 238620d914ce1ff78d8710e12083d03eacb5f9be7106d0d12ef15c5105445b72
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0db24a8c173d7b522ba06e1af9e957c513a551a211d22b6a2d19b7218a79e236f6db1f66b738a1a76e5b96ef6161f432f7ece17dea4af8d3de46520960a89977
|
|
7
|
+
data.tar.gz: a2826f9e8fbd34364acc54a117e283067982491e9c8e736ac1c8fcb86aac1f6a3acc43811040242fea19e79019d809fd14dae6b46ecc0568b67775dddd84369d
|
data/README.md
CHANGED
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
[Turtle][] reader/writer for [RDF.rb][RDF.rb] .
|
|
4
4
|
|
|
5
5
|
[](https://badge.fury.io/rb/rdf-turtle)
|
|
6
|
-
[](https://coveralls.io/
|
|
6
|
+
[](https://github.com/ruby-rdf/rdf-turtle/actions?query=workflow%3ACI)
|
|
7
|
+
[](https://coveralls.io/github/ruby-rdf/rdf-turtle?branch=develop)
|
|
8
|
+
[](https://gitter.im/ruby-rdf/rdf)
|
|
8
9
|
|
|
9
10
|
## Description
|
|
10
11
|
This is a [Ruby][] implementation of a [Turtle][] parser for [RDF.rb][].
|
|
@@ -16,9 +17,9 @@ Install with `gem install rdf-turtle`
|
|
|
16
17
|
|
|
17
18
|
* 100% free and unencumbered [public domain](https://unlicense.org/) software.
|
|
18
19
|
* Implements a complete parser for [Turtle][].
|
|
19
|
-
* Compatible with Ruby >= 2.
|
|
20
|
+
* Compatible with Ruby >= 2.6.
|
|
20
21
|
* Optional streaming writer, to serialize large graphs
|
|
21
|
-
* Provisional support for [Turtle
|
|
22
|
+
* Provisional support for [Turtle-star][RDF-star].
|
|
22
23
|
|
|
23
24
|
## Usage
|
|
24
25
|
Instantiate a reader from a local file:
|
|
@@ -35,9 +36,9 @@ Write a graph to a file:
|
|
|
35
36
|
writer << graph
|
|
36
37
|
end
|
|
37
38
|
|
|
38
|
-
## Turtle
|
|
39
|
+
## Turtle-star (RDF-star)
|
|
39
40
|
|
|
40
|
-
Both reader and writer include provisional support for [Turtle
|
|
41
|
+
Both reader and writer include provisional support for [Turtle-star][RDF-star].
|
|
41
42
|
|
|
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`.
|
|
43
44
|
|
|
@@ -65,19 +66,29 @@ By default, the Turtle reader will reject a document containing a subject resour
|
|
|
65
66
|
end
|
|
66
67
|
# => RDF::ReaderError
|
|
67
68
|
|
|
68
|
-
Readers support a `rdfstar` option
|
|
69
|
+
Readers support a boolean valued `rdfstar` option; only one statement is asserted, although the reified statement is contained within the graph.
|
|
69
70
|
|
|
70
71
|
graph = RDF::Graph.new do |graph|
|
|
71
|
-
RDF::Turtle::Reader.new(ttl, rdfstar:
|
|
72
|
+
RDF::Turtle::Reader.new(ttl, rdfstar: true) {|reader| graph << reader}
|
|
72
73
|
end
|
|
73
|
-
graph.count #=>
|
|
74
|
+
graph.count #=> 1
|
|
75
|
+
|
|
76
|
+
### Reading a Graph containing statement annotations
|
|
74
77
|
|
|
75
|
-
|
|
78
|
+
Annotations are introduced using the `{| ... |}` syntax, which is treated like a `blankNodePropertyList`,
|
|
79
|
+
where the subject is the the triple ending with that annotation.
|
|
76
80
|
|
|
81
|
+
ttl = %(
|
|
82
|
+
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
|
|
83
|
+
@prefix ex: <http://example.com/> .
|
|
84
|
+
<bob> foaf:age 23 {| ex:certainty 9.0e-1 |} .
|
|
85
|
+
)
|
|
77
86
|
graph = RDF::Graph.new do |graph|
|
|
78
|
-
RDF::Turtle::Reader.new(ttl
|
|
87
|
+
RDF::Turtle::Reader.new(ttl) {|reader| graph << reader}
|
|
79
88
|
end
|
|
80
|
-
|
|
89
|
+
# => RDF::ReaderError
|
|
90
|
+
|
|
91
|
+
Note that this requires the `rdfstar` option to be set.
|
|
81
92
|
|
|
82
93
|
## Documentation
|
|
83
94
|
Full documentation available on [Rubydoc.info][Turtle doc]
|
|
@@ -129,9 +140,9 @@ This version uses a hand-written parser using the Lexer from the [EBNF][] gem in
|
|
|
129
140
|
|
|
130
141
|
## Dependencies
|
|
131
142
|
|
|
132
|
-
* [Ruby](https://ruby-lang.org/) (>= 2.
|
|
133
|
-
* [RDF.rb](https://rubygems.org/gems/rdf) (~> 3.
|
|
134
|
-
* [EBNF][] (~> 1.
|
|
143
|
+
* [Ruby](https://ruby-lang.org/) (>= 2.6)
|
|
144
|
+
* [RDF.rb](https://rubygems.org/gems/rdf) (~> 3.2)
|
|
145
|
+
* [EBNF][] (~> 1.2)
|
|
135
146
|
|
|
136
147
|
## Installation
|
|
137
148
|
|
|
@@ -159,7 +170,9 @@ This repository uses [Git Flow](https://github.com/nvie/gitflow) to mange develo
|
|
|
159
170
|
list in the the `README`. Alphabetical order applies.
|
|
160
171
|
* Do note that in order for us to merge any non-trivial changes (as a rule
|
|
161
172
|
of thumb, additions larger than about 15 lines of code), we need an
|
|
162
|
-
explicit [public domain dedication][PDD] on record from you
|
|
173
|
+
explicit [public domain dedication][PDD] on record from you,
|
|
174
|
+
which you will be asked to agree to on the first commit to a repo within the organization.
|
|
175
|
+
Note that the agreement applies to all repos in the [Ruby RDF](https://github.com/ruby-rdf/) organization.
|
|
163
176
|
|
|
164
177
|
## License
|
|
165
178
|
This is free and unencumbered public domain software. For more information,
|
|
@@ -171,13 +184,13 @@ A copy of the [Turtle EBNF][] and derived parser files are included in the repos
|
|
|
171
184
|
[RDF]: https://www.w3.org/RDF/
|
|
172
185
|
[YARD]: https://yardoc.org/
|
|
173
186
|
[YARD-GS]: https://rubydoc.info/docs/yard/file/docs/GettingStarted.md
|
|
174
|
-
[PDD]:
|
|
175
|
-
[RDF.rb]: https://
|
|
187
|
+
[PDD]: https://unlicense.org/#unlicensing-contributions
|
|
188
|
+
[RDF.rb]: https://ruby-rdf.github.io/rdf
|
|
176
189
|
[EBNF]: https://rubygems.org/gems/ebnf
|
|
177
190
|
[Backports]: https://rubygems.org/gems/backports
|
|
178
191
|
[N-Triples]: https://www.w3.org/TR/rdf-testcases/#ntriples
|
|
179
192
|
[Turtle]: https://www.w3.org/TR/2012/WD-turtle-20120710/
|
|
180
|
-
[RDF
|
|
181
|
-
[Turtle doc]: https://
|
|
193
|
+
[RDF-star]: https://w3c.github.io/rdf-star/rdf-star-cg-spec.html
|
|
194
|
+
[Turtle doc]: https://ruby-rdf.github.io/rdf-turtle/master/file/README.md
|
|
182
195
|
[Turtle EBNF]: https://dvcs.w3.org/hg/rdf/file/default/rdf-turtle/turtle.bnf
|
|
183
196
|
[Freebase Dumps]: https://developers.google.com/freebase/data
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
3.1
|
|
1
|
+
3.2.1
|
data/lib/rdf/turtle/format.rb
CHANGED
data/lib/rdf/turtle/reader.rb
CHANGED
|
@@ -25,7 +25,14 @@ 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(
|
|
28
|
+
terminal(nil, %r(
|
|
29
|
+
[\(\),.;\[\]Aa]
|
|
30
|
+
| \^\^
|
|
31
|
+
| \{\|
|
|
32
|
+
| \|\}
|
|
33
|
+
| true|false
|
|
34
|
+
| <<|>>
|
|
35
|
+
)x)
|
|
29
36
|
|
|
30
37
|
terminal(:PREFIX, PREFIX)
|
|
31
38
|
terminal(:BASE, BASE)
|
|
@@ -33,7 +40,7 @@ module RDF::Turtle
|
|
|
33
40
|
|
|
34
41
|
##
|
|
35
42
|
# Reader options
|
|
36
|
-
# @see https://
|
|
43
|
+
# @see https://ruby-rdf.github.io/rdf/RDF/Reader#options-class_method
|
|
37
44
|
def self.options
|
|
38
45
|
super + [
|
|
39
46
|
RDF::CLI::Option.new(
|
|
@@ -92,11 +99,11 @@ module RDF::Turtle
|
|
|
92
99
|
@prod_stack = []
|
|
93
100
|
|
|
94
101
|
@options[:base_uri] = RDF::URI(base_uri || "")
|
|
95
|
-
|
|
102
|
+
debug("base IRI") {base_uri.inspect}
|
|
96
103
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
104
|
+
debug("validate") {validate?.inspect}
|
|
105
|
+
debug("canonicalize") {canonicalize?.inspect}
|
|
106
|
+
debug("intern") {intern?.inspect}
|
|
100
107
|
|
|
101
108
|
@lexer = EBNF::LL1::Lexer.new(input, self.class.patterns, **@options)
|
|
102
109
|
|
|
@@ -185,7 +192,7 @@ module RDF::Turtle
|
|
|
185
192
|
|
|
186
193
|
# Create a literal
|
|
187
194
|
def literal(value, **options)
|
|
188
|
-
|
|
195
|
+
debug("literal", depth: @options[:depth]) do
|
|
189
196
|
"value: #{value.inspect}, " +
|
|
190
197
|
"options: #{options.inspect}, " +
|
|
191
198
|
"validate: #{validate?.inspect}, " +
|
|
@@ -221,7 +228,7 @@ module RDF::Turtle
|
|
|
221
228
|
''
|
|
222
229
|
end
|
|
223
230
|
suffix = suffix.to_s.sub(/^\#/, "") if base.index("#")
|
|
224
|
-
|
|
231
|
+
debug("pname", depth: options[:depth]) {"base: '#{base}', suffix: '#{suffix}'"}
|
|
225
232
|
process_iri(base + suffix.to_s)
|
|
226
233
|
end
|
|
227
234
|
|
|
@@ -283,7 +290,7 @@ module RDF::Turtle
|
|
|
283
290
|
terminated = token.value == '@prefix'
|
|
284
291
|
error("Expected PNAME_NS", production: :prefix, token: pfx) unless pfx === :PNAME_NS
|
|
285
292
|
error("Expected IRIREF", production: :prefix, token: iri) unless iri === :IRIREF
|
|
286
|
-
|
|
293
|
+
debug("prefixID", depth: options[:depth]) {"Defined prefix #{pfx.inspect} mapping to #{iri.inspect}"}
|
|
287
294
|
prefix(pfx.value[0..-2], process_iri(iri))
|
|
288
295
|
error("prefixId", "#{token} should be downcased") if token.value.start_with?('@') && token.value != '@prefix'
|
|
289
296
|
|
|
@@ -340,6 +347,10 @@ module RDF::Turtle
|
|
|
340
347
|
last_object = nil
|
|
341
348
|
while object = prod(:_objectList_2) {read_object(subject, predicate)}
|
|
342
349
|
last_object = object
|
|
350
|
+
|
|
351
|
+
# If object is followed by an annotation, read that and also emit an embedded triple.
|
|
352
|
+
read_annotation(subject, predicate, object)
|
|
353
|
+
|
|
343
354
|
break unless @lexer.first === ','
|
|
344
355
|
@lexer.shift while @lexer.first === ','
|
|
345
356
|
end
|
|
@@ -362,7 +373,7 @@ module RDF::Turtle
|
|
|
362
373
|
read_iri ||
|
|
363
374
|
read_BlankNode ||
|
|
364
375
|
read_collection ||
|
|
365
|
-
|
|
376
|
+
read_quotedTriple ||
|
|
366
377
|
error( "Expected subject", production: :subject, token: @lexer.first)
|
|
367
378
|
end
|
|
368
379
|
end
|
|
@@ -375,7 +386,7 @@ module RDF::Turtle
|
|
|
375
386
|
read_collection ||
|
|
376
387
|
read_blankNodePropertyList ||
|
|
377
388
|
read_literal ||
|
|
378
|
-
|
|
389
|
+
read_quotedTriple
|
|
379
390
|
|
|
380
391
|
add_statement(:object, RDF::Statement(subject, predicate, object)) if subject && predicate
|
|
381
392
|
object
|
|
@@ -383,35 +394,71 @@ module RDF::Turtle
|
|
|
383
394
|
end
|
|
384
395
|
end
|
|
385
396
|
|
|
386
|
-
# Read an RDF
|
|
397
|
+
# Read an RDF-star reified statement
|
|
387
398
|
# @return [RDF::Statement]
|
|
388
|
-
def
|
|
399
|
+
def read_quotedTriple
|
|
389
400
|
return unless @options[:rdfstar]
|
|
390
401
|
if @lexer.first.value == '<<'
|
|
391
|
-
prod(:
|
|
402
|
+
prod(:quotedTriple) do
|
|
392
403
|
@lexer.shift # eat <<
|
|
393
|
-
subject =
|
|
394
|
-
predicate = read_verb || error("Failed to parse predicate", production: :
|
|
395
|
-
object =
|
|
404
|
+
subject = read_qtSubject || error("Failed to parse subject", production: :quotedTriple, token: @lexer.first)
|
|
405
|
+
predicate = read_verb || error("Failed to parse predicate", production: :quotedTriple, token: @lexer.first)
|
|
406
|
+
object = read_qtObject || error("Failed to parse object", production: :quotedTriple, token: @lexer.first)
|
|
396
407
|
unless @lexer.first.value == '>>'
|
|
397
|
-
error("Failed to end of embedded triple", production: :
|
|
408
|
+
error("Failed to end of embedded triple", production: :quotedTriple, token: @lexer.first)
|
|
398
409
|
end
|
|
399
410
|
@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
|
|
411
|
+
statement = RDF::Statement(subject, predicate, object, quoted: true)
|
|
403
412
|
statement
|
|
404
413
|
end
|
|
405
414
|
end
|
|
406
415
|
end
|
|
407
416
|
|
|
417
|
+
# @return [RDF::Resource]
|
|
418
|
+
def read_qtSubject
|
|
419
|
+
prod(:qtSubject) do
|
|
420
|
+
read_iri ||
|
|
421
|
+
read_BlankNode ||
|
|
422
|
+
read_quotedTriple ||
|
|
423
|
+
error( "Expected embedded subject", production: :qtSubject, token: @lexer.first)
|
|
424
|
+
end
|
|
425
|
+
end
|
|
426
|
+
|
|
427
|
+
# @return [RDF::Term]
|
|
428
|
+
def read_qtObject(subject = nil, predicate = nil)
|
|
429
|
+
prod(:qtObject) do
|
|
430
|
+
read_iri ||
|
|
431
|
+
read_BlankNode ||
|
|
432
|
+
read_literal ||
|
|
433
|
+
read_quotedTriple
|
|
434
|
+
end
|
|
435
|
+
end
|
|
436
|
+
|
|
437
|
+
# Read an annotation on a triple
|
|
438
|
+
def read_annotation(subject, predicate, object)
|
|
439
|
+
error("Unexpected end of file", production: :annotation) unless token = @lexer.first
|
|
440
|
+
if token === '{|'
|
|
441
|
+
prod(:annotation, %(|})) do
|
|
442
|
+
@lexer.shift
|
|
443
|
+
|
|
444
|
+
# Statement becomes subject for predicateObjectList
|
|
445
|
+
statement = RDF::Statement(subject, predicate, object, quoted: true)
|
|
446
|
+
read_predicateObjectList(statement) ||
|
|
447
|
+
error("Expected predicateObjectList", production: :annotation, token: @lexer.first)
|
|
448
|
+
error("annotation", "Expected closing '|}'") unless @lexer.first === '|}'
|
|
449
|
+
@lexer.shift
|
|
450
|
+
end
|
|
451
|
+
end
|
|
452
|
+
|
|
453
|
+
end
|
|
454
|
+
|
|
408
455
|
# @return [RDF::Literal]
|
|
409
456
|
def read_literal
|
|
410
457
|
error("Unexpected end of file", production: :literal) unless token = @lexer.first
|
|
411
458
|
case token.type || token.value
|
|
412
459
|
when :INTEGER then prod(:literal) {literal(@lexer.shift.value, datatype: RDF::XSD.integer)}
|
|
413
460
|
when :DECIMAL
|
|
414
|
-
prod(:
|
|
461
|
+
prod(:literal) do
|
|
415
462
|
value = @lexer.shift.value
|
|
416
463
|
value = "0#{value}" if value.start_with?(".")
|
|
417
464
|
literal(value, datatype: RDF::XSD.decimal)
|
|
@@ -455,7 +502,7 @@ module RDF::Turtle
|
|
|
455
502
|
if token === '['
|
|
456
503
|
prod(:blankNodePropertyList, %{]}) do
|
|
457
504
|
@lexer.shift
|
|
458
|
-
|
|
505
|
+
progress("blankNodePropertyList", depth: options[:depth]) {"token: #{token.inspect}"}
|
|
459
506
|
node = bnode
|
|
460
507
|
read_predicateObjectList(node)
|
|
461
508
|
error("blankNodePropertyList", "Expected closing ']'") unless @lexer.first === ']'
|
|
@@ -471,7 +518,7 @@ module RDF::Turtle
|
|
|
471
518
|
prod(:collection, %{)}) do
|
|
472
519
|
@lexer.shift
|
|
473
520
|
token = @lexer.first
|
|
474
|
-
|
|
521
|
+
progress("collection", depth: options[:depth]) {"token: #{token.inspect}"}
|
|
475
522
|
objects = []
|
|
476
523
|
while object = read_object
|
|
477
524
|
objects << object
|
|
@@ -508,7 +555,7 @@ module RDF::Turtle
|
|
|
508
555
|
def prod(production, recover_to = [])
|
|
509
556
|
@prod_stack << {prod: production, recover_to: recover_to}
|
|
510
557
|
@options[:depth] += 1
|
|
511
|
-
|
|
558
|
+
recover("#{production}(start)", depth: options[:depth], token: @lexer.first)
|
|
512
559
|
yield
|
|
513
560
|
rescue EBNF::LL1::Lexer::Error, SyntaxError, Recovery => e
|
|
514
561
|
# Lexer encountered an illegal token or the parser encountered
|
|
@@ -528,13 +575,13 @@ module RDF::Turtle
|
|
|
528
575
|
end
|
|
529
576
|
end
|
|
530
577
|
raise EOFError, "End of input found when recovering" if @lexer.first.nil?
|
|
531
|
-
|
|
578
|
+
debug("recovery", "current token: #{@lexer.first.inspect}", depth: options[:depth])
|
|
532
579
|
|
|
533
580
|
unless e.is_a?(Recovery)
|
|
534
581
|
# Get the list of follows for this sequence, this production and the stacked productions.
|
|
535
|
-
|
|
582
|
+
debug("recovery", "stack follows:", depth: options[:depth])
|
|
536
583
|
@prod_stack.reverse.each do |prod|
|
|
537
|
-
|
|
584
|
+
debug("recovery", level: 4, depth: options[:depth]) {" #{prod[:prod]}: #{prod[:recover_to].inspect}"}
|
|
538
585
|
end
|
|
539
586
|
end
|
|
540
587
|
|
|
@@ -544,9 +591,9 @@ module RDF::Turtle
|
|
|
544
591
|
# Skip tokens until one is found in follows
|
|
545
592
|
while (token = (@lexer.first rescue @lexer.recover)) && follows.none? {|t| token === t}
|
|
546
593
|
skipped = @lexer.shift
|
|
547
|
-
|
|
594
|
+
debug("recovery", depth: options[:depth]) {"skip #{skipped.inspect}"}
|
|
548
595
|
end
|
|
549
|
-
|
|
596
|
+
debug("recovery", depth: options[:depth]) {"found #{token.inspect} in follows"}
|
|
550
597
|
|
|
551
598
|
# Re-raise the error unless token is a follows of this production
|
|
552
599
|
raise Recovery unless Array(recover_to).any? {|t| token === t}
|
|
@@ -554,11 +601,35 @@ module RDF::Turtle
|
|
|
554
601
|
# Skip that token to get something reasonable to start the next production with
|
|
555
602
|
@lexer.shift
|
|
556
603
|
ensure
|
|
557
|
-
|
|
604
|
+
progress("#{production}(finish)", depth: options[:depth])
|
|
558
605
|
@options[:depth] -= 1
|
|
559
606
|
@prod_stack.pop
|
|
560
607
|
end
|
|
561
608
|
|
|
609
|
+
def progress(*args, &block)
|
|
610
|
+
lineno = (options[:token].lineno if options[:token].respond_to?(:lineno)) || (@lexer && @lexer.lineno)
|
|
611
|
+
opts = args.last.is_a?(Hash) ? args.pop : {}
|
|
612
|
+
opts[:level] ||= 1
|
|
613
|
+
opts[:lineno] ||= lineno
|
|
614
|
+
log_info(*args, **opts, &block)
|
|
615
|
+
end
|
|
616
|
+
|
|
617
|
+
def recover(*args, &block)
|
|
618
|
+
lineno = (options[:token].lineno if options[:token].respond_to?(:lineno)) || (@lexer && @lexer.lineno)
|
|
619
|
+
opts = args.last.is_a?(Hash) ? args.pop : {}
|
|
620
|
+
opts[:level] ||= 1
|
|
621
|
+
opts[:lineno] ||= lineno
|
|
622
|
+
log_recover(*args, **opts, &block)
|
|
623
|
+
end
|
|
624
|
+
|
|
625
|
+
def debug(*args, &block)
|
|
626
|
+
lineno = (options[:token].lineno if options[:token].respond_to?(:lineno)) || (@lexer && @lexer.lineno)
|
|
627
|
+
opts = args.last.is_a?(Hash) ? args.pop : {}
|
|
628
|
+
opts[:level] ||= 0
|
|
629
|
+
opts[:lineno] ||= lineno
|
|
630
|
+
log_debug(*args, **opts, &block)
|
|
631
|
+
end
|
|
632
|
+
|
|
562
633
|
##
|
|
563
634
|
# Error information, used as level `0` debug messages.
|
|
564
635
|
#
|
data/lib/rdf/turtle/writer.rb
CHANGED
|
@@ -65,7 +65,7 @@ module RDF::Turtle
|
|
|
65
65
|
|
|
66
66
|
##
|
|
67
67
|
# Writer options
|
|
68
|
-
# @see https://
|
|
68
|
+
# @see https://ruby-rdf.github.io/rdf/RDF/Writer#options-class_method
|
|
69
69
|
def self.options
|
|
70
70
|
super + [
|
|
71
71
|
RDF::CLI::Option.new(
|
|
@@ -201,7 +201,7 @@ module RDF::Turtle
|
|
|
201
201
|
super
|
|
202
202
|
end
|
|
203
203
|
|
|
204
|
-
# Return a
|
|
204
|
+
# Return a PName for the URI, or nil. Adds namespace of PName to defined prefixes
|
|
205
205
|
# @param [RDF::Resource] resource
|
|
206
206
|
# @return [String, nil] value to use to identify URI
|
|
207
207
|
def get_pname(resource)
|
|
@@ -223,14 +223,15 @@ module RDF::Turtle
|
|
|
223
223
|
unless u.to_s.empty?
|
|
224
224
|
prefix(prefix, u) unless u.to_s.empty?
|
|
225
225
|
log_debug("get_pname") {"add prefix #{prefix.inspect} => #{u}"}
|
|
226
|
-
|
|
226
|
+
# Escape suffix, as necessary
|
|
227
|
+
RDF::URI(uri).pname(prefixes: {prefix => u})
|
|
227
228
|
end
|
|
228
229
|
when @options[:standard_prefixes] && vocab = RDF::Vocabulary.each.to_a.detect {|v| uri.index(v.to_uri.to_s) == 0}
|
|
229
230
|
prefix = vocab.__name__.to_s.split('::').last.downcase
|
|
230
231
|
@uri_to_prefix[vocab.to_uri.to_s] = prefix
|
|
231
232
|
prefix(prefix, vocab.to_uri) # Define for output
|
|
232
233
|
log_debug("get_pname") {"add standard prefix #{prefix.inspect} => #{vocab.to_uri}"}
|
|
233
|
-
uri.
|
|
234
|
+
RDF::URI(uri).pname(prefixes: {prefix => vocab.to_uri})
|
|
234
235
|
else
|
|
235
236
|
nil
|
|
236
237
|
end
|
|
@@ -276,10 +277,23 @@ module RDF::Turtle
|
|
|
276
277
|
case literal
|
|
277
278
|
when RDF::Literal
|
|
278
279
|
case @options[:literal_shorthand] && literal.valid? ? literal.datatype : false
|
|
279
|
-
when RDF::XSD.boolean
|
|
280
|
-
literal.canonicalize.to_s
|
|
280
|
+
when RDF::XSD.boolean
|
|
281
|
+
%w(true false).include?(literal.value) ? literal.value : literal.canonicalize.to_s
|
|
282
|
+
when RDF::XSD.integer
|
|
283
|
+
literal.value.match?(/^[\+\-]?\d+$/) && !canonicalize? ? literal.value : literal.canonicalize.to_s
|
|
284
|
+
when RDF::XSD.decimal
|
|
285
|
+
literal.value.match?(/^[\+\-]?\d+\.\d+?$/) && !canonicalize? ?
|
|
286
|
+
literal.value :
|
|
287
|
+
literal.canonicalize.to_s
|
|
281
288
|
when RDF::XSD.double
|
|
282
|
-
|
|
289
|
+
in_form = case literal.value
|
|
290
|
+
when /[\+\-]?\d+\.\d*E[\+\-]?\d+$/i then true
|
|
291
|
+
when /[\+\-]?\.\d+E[\+\-]?\d+$/i then true
|
|
292
|
+
when /[\+\-]?\d+E[\+\-]?\d+$/i then true
|
|
293
|
+
else false
|
|
294
|
+
end && !canonicalize?
|
|
295
|
+
|
|
296
|
+
in_form ? literal.value : literal.canonicalize.to_s.sub('E', 'e').to_s
|
|
283
297
|
else
|
|
284
298
|
text = quoted(literal.value)
|
|
285
299
|
text << "@#{literal.language}" if literal.has_language?
|
|
@@ -314,12 +328,12 @@ module RDF::Turtle
|
|
|
314
328
|
end
|
|
315
329
|
|
|
316
330
|
##
|
|
317
|
-
# Returns an embedded
|
|
331
|
+
# Returns an embedded triple.
|
|
318
332
|
#
|
|
319
333
|
# @param [RDF::Statement] statement
|
|
320
334
|
# @param [Hash{Symbol => Object}] options
|
|
321
335
|
# @return [String]
|
|
322
|
-
def
|
|
336
|
+
def format_quotedTriple(statement, **options)
|
|
323
337
|
log_debug("rdfstar") {"#{statement.to_ntriples}"}
|
|
324
338
|
"<<%s %s %s>>" % statement.to_a.map { |value| format_term(value, **options) }
|
|
325
339
|
end
|
|
@@ -374,16 +388,16 @@ module RDF::Turtle
|
|
|
374
388
|
# Mark as seen lists that are part of another list
|
|
375
389
|
@lists.values.map(&:statements).
|
|
376
390
|
flatten.each do |st|
|
|
377
|
-
seen[st.object] = true if @lists.
|
|
391
|
+
seen[st.object] = true if @lists.key?(st.object)
|
|
378
392
|
end
|
|
379
393
|
|
|
380
|
-
|
|
381
|
-
|
|
394
|
+
# List elements which are bnodes should not be targets for top-level serialization
|
|
395
|
+
list_elements = @lists.values.map(&:to_a).flatten.select(&:node?).compact
|
|
382
396
|
|
|
383
|
-
# Sort subjects by resources over bnodes, ref_counts and the subject URI itself
|
|
397
|
+
# Sort subjects by resources and statements over bnodes, ref_counts and the subject URI itself
|
|
384
398
|
recursable = (@subjects.keys - list_elements).
|
|
385
399
|
select {|s| !seen.include?(s)}.
|
|
386
|
-
map {|r| [r.node? ? 1 : 0, ref_count(r), r]}.
|
|
400
|
+
map {|r| [r.node? ? 2 : (r.statement? ? 1 : 0), ref_count(r), r]}.
|
|
387
401
|
sort
|
|
388
402
|
|
|
389
403
|
subjects + recursable.map{|r| r.last}
|
|
@@ -469,7 +483,7 @@ module RDF::Turtle
|
|
|
469
483
|
# Can subject be represented as a blankNodePropertyList?
|
|
470
484
|
def blankNodePropertyList?(resource, position)
|
|
471
485
|
!resource.statement? && resource.node? &&
|
|
472
|
-
!
|
|
486
|
+
!collection?(resource) &&
|
|
473
487
|
(!is_done?(resource) || position == :subject) &&
|
|
474
488
|
ref_count(resource) == (position == :object ? 1 : 0)
|
|
475
489
|
end
|
|
@@ -507,35 +521,33 @@ module RDF::Turtle
|
|
|
507
521
|
private
|
|
508
522
|
|
|
509
523
|
# Checks if l is a valid RDF list, i.e. no nodes have other properties.
|
|
510
|
-
def
|
|
511
|
-
#log_debug("
|
|
512
|
-
return @lists
|
|
513
|
-
end
|
|
514
|
-
|
|
515
|
-
def do_list(l, position)
|
|
516
|
-
list = @lists[l]
|
|
517
|
-
log_debug("do_list") {list.inspect}
|
|
518
|
-
subject_done(RDF.nil)
|
|
519
|
-
index = 0
|
|
520
|
-
list.each_statement do |st|
|
|
521
|
-
next unless st.predicate == RDF.first
|
|
522
|
-
log_debug {" list this: #{st.subject} first: #{st.object}[#{position}]"}
|
|
523
|
-
@output.write(" ") if index > 0
|
|
524
|
-
path(st.object, position)
|
|
525
|
-
subject_done(st.subject)
|
|
526
|
-
position = :object
|
|
527
|
-
index += 1
|
|
528
|
-
end
|
|
524
|
+
def collection?(l)
|
|
525
|
+
#log_debug("collection?") {l.inspect}
|
|
526
|
+
return @lists.key?(l)
|
|
529
527
|
end
|
|
530
528
|
|
|
531
529
|
def collection(node, position)
|
|
532
|
-
return false if !
|
|
530
|
+
return false if !collection?(node)
|
|
533
531
|
return false if position == :subject && ref_count(node) > 0
|
|
534
532
|
return false if position == :object && prop_count(node) > 0
|
|
535
533
|
#log_debug("collection") {"#{node.to_ntriples}, #{position}"}
|
|
536
534
|
|
|
537
535
|
@output.write("(")
|
|
538
|
-
log_depth
|
|
536
|
+
log_depth do
|
|
537
|
+
list = @lists[node]
|
|
538
|
+
log_debug("collection") {list.inspect}
|
|
539
|
+
subject_done(RDF.nil)
|
|
540
|
+
index = 0
|
|
541
|
+
list.each_statement do |st|
|
|
542
|
+
next unless st.predicate == RDF.first
|
|
543
|
+
log_debug {" list this: #{st.subject} first: #{st.object}[#{position}]"}
|
|
544
|
+
@output.write(" ") if index > 0
|
|
545
|
+
path(st.object, position)
|
|
546
|
+
subject_done(st.subject)
|
|
547
|
+
position = :object
|
|
548
|
+
index += 1
|
|
549
|
+
end
|
|
550
|
+
end
|
|
539
551
|
@output.write(')')
|
|
540
552
|
end
|
|
541
553
|
|
|
@@ -568,7 +580,7 @@ module RDF::Turtle
|
|
|
568
580
|
log_debug("path") do
|
|
569
581
|
"#{resource.to_ntriples}, " +
|
|
570
582
|
"pos: #{position}, " +
|
|
571
|
-
"()?: #{
|
|
583
|
+
"()?: #{collection?(resource)}, " +
|
|
572
584
|
"[]?: #{blankNodePropertyList?(resource, position)}, " +
|
|
573
585
|
"rc: #{ref_count(resource)}"
|
|
574
586
|
end
|
|
@@ -588,7 +600,7 @@ module RDF::Turtle
|
|
|
588
600
|
end
|
|
589
601
|
|
|
590
602
|
# Render an objectList having a common subject and predicate
|
|
591
|
-
def objectList(objects)
|
|
603
|
+
def objectList(subject, predicate, objects)
|
|
592
604
|
log_debug("objectList") {objects.inspect}
|
|
593
605
|
return if objects.empty?
|
|
594
606
|
|
|
@@ -599,6 +611,15 @@ module RDF::Turtle
|
|
|
599
611
|
@output.write ",\n#{indent(4)}"
|
|
600
612
|
end
|
|
601
613
|
path(obj, :object)
|
|
614
|
+
|
|
615
|
+
# If subject, predicate, and object are embedded, write those bits out too.
|
|
616
|
+
emb = RDF::Statement(subject, predicate, obj)
|
|
617
|
+
if !@graph.query({subject: emb}).empty?
|
|
618
|
+
@output.write ' {| '
|
|
619
|
+
predicateObjectList(emb, true)
|
|
620
|
+
@output.write ' |}'
|
|
621
|
+
subject_done(emb)
|
|
622
|
+
end
|
|
602
623
|
end
|
|
603
624
|
end
|
|
604
625
|
|
|
@@ -611,17 +632,18 @@ module RDF::Turtle
|
|
|
611
632
|
end
|
|
612
633
|
|
|
613
634
|
prop_list = sort_properties(properties)
|
|
614
|
-
prop_list -= [RDF.first.to_s, RDF.rest.to_s] if @lists.
|
|
635
|
+
prop_list -= [RDF.first.to_s, RDF.rest.to_s] if @lists.key?(subject)
|
|
615
636
|
log_debug("predicateObjectList") {prop_list.inspect}
|
|
616
637
|
return 0 if prop_list.empty?
|
|
617
638
|
|
|
618
639
|
@output.write("\n#{indent(2)}") if properties.keys.length > 1 && from_bpl
|
|
619
640
|
prop_list.each_with_index do |prop, i|
|
|
620
641
|
begin
|
|
642
|
+
pred = RDF::URI.intern(prop)
|
|
621
643
|
@output.write(";\n#{indent(2)}") if i > 0
|
|
622
|
-
predicate(
|
|
644
|
+
predicate(pred)
|
|
623
645
|
@output.write(" ")
|
|
624
|
-
objectList(properties[prop])
|
|
646
|
+
objectList(subject, pred, properties[prop])
|
|
625
647
|
end
|
|
626
648
|
end
|
|
627
649
|
properties.keys.length
|
data/lib/rdf/turtle.rb
CHANGED
|
@@ -15,7 +15,7 @@ module RDF
|
|
|
15
15
|
# end
|
|
16
16
|
# end
|
|
17
17
|
#
|
|
18
|
-
# @see https://
|
|
18
|
+
# @see https://ruby-rdf.github.io/rdf/master/frames
|
|
19
19
|
# @see https://dvcs.w3.org/hg/rdf/raw-file/default/rdf-turtle/index.html
|
|
20
20
|
#
|
|
21
21
|
# @author [Gregg Kellogg](https://greggkellogg.net/)
|
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
|
|
4
|
+
version: 3.2.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Gregg Kellogg
|
|
8
|
-
autorequire:
|
|
8
|
+
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2022-05-28 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rdf
|
|
@@ -16,146 +16,140 @@ dependencies:
|
|
|
16
16
|
requirements:
|
|
17
17
|
- - "~>"
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: '3.
|
|
20
|
-
- - ">="
|
|
21
|
-
- !ruby/object:Gem::Version
|
|
22
|
-
version: 3.1.2
|
|
19
|
+
version: '3.2'
|
|
23
20
|
type: :runtime
|
|
24
21
|
prerelease: false
|
|
25
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
26
23
|
requirements:
|
|
27
24
|
- - "~>"
|
|
28
25
|
- !ruby/object:Gem::Version
|
|
29
|
-
version: '3.
|
|
30
|
-
- - ">="
|
|
31
|
-
- !ruby/object:Gem::Version
|
|
32
|
-
version: 3.1.2
|
|
26
|
+
version: '3.2'
|
|
33
27
|
- !ruby/object:Gem::Dependency
|
|
34
28
|
name: ebnf
|
|
35
29
|
requirement: !ruby/object:Gem::Requirement
|
|
36
30
|
requirements:
|
|
37
31
|
- - "~>"
|
|
38
32
|
- !ruby/object:Gem::Version
|
|
39
|
-
version: '2.
|
|
33
|
+
version: '2.3'
|
|
40
34
|
type: :runtime
|
|
41
35
|
prerelease: false
|
|
42
36
|
version_requirements: !ruby/object:Gem::Requirement
|
|
43
37
|
requirements:
|
|
44
38
|
- - "~>"
|
|
45
39
|
- !ruby/object:Gem::Version
|
|
46
|
-
version: '2.
|
|
40
|
+
version: '2.3'
|
|
47
41
|
- !ruby/object:Gem::Dependency
|
|
48
|
-
name:
|
|
42
|
+
name: erubis
|
|
49
43
|
requirement: !ruby/object:Gem::Requirement
|
|
50
44
|
requirements:
|
|
51
45
|
- - "~>"
|
|
52
46
|
- !ruby/object:Gem::Version
|
|
53
|
-
version: '
|
|
47
|
+
version: '2.7'
|
|
54
48
|
type: :development
|
|
55
49
|
prerelease: false
|
|
56
50
|
version_requirements: !ruby/object:Gem::Requirement
|
|
57
51
|
requirements:
|
|
58
52
|
- - "~>"
|
|
59
53
|
- !ruby/object:Gem::Version
|
|
60
|
-
version: '
|
|
54
|
+
version: '2.7'
|
|
61
55
|
- !ruby/object:Gem::Dependency
|
|
62
|
-
name:
|
|
56
|
+
name: htmlentities
|
|
63
57
|
requirement: !ruby/object:Gem::Requirement
|
|
64
58
|
requirements:
|
|
65
59
|
- - "~>"
|
|
66
60
|
- !ruby/object:Gem::Version
|
|
67
|
-
version: '
|
|
61
|
+
version: '4.3'
|
|
68
62
|
type: :development
|
|
69
63
|
prerelease: false
|
|
70
64
|
version_requirements: !ruby/object:Gem::Requirement
|
|
71
65
|
requirements:
|
|
72
66
|
- - "~>"
|
|
73
67
|
- !ruby/object:Gem::Version
|
|
74
|
-
version: '
|
|
68
|
+
version: '4.3'
|
|
75
69
|
- !ruby/object:Gem::Dependency
|
|
76
|
-
name:
|
|
70
|
+
name: rspec
|
|
77
71
|
requirement: !ruby/object:Gem::Requirement
|
|
78
72
|
requirements:
|
|
79
73
|
- - "~>"
|
|
80
74
|
- !ruby/object:Gem::Version
|
|
81
|
-
version: '3.
|
|
75
|
+
version: '3.10'
|
|
82
76
|
type: :development
|
|
83
77
|
prerelease: false
|
|
84
78
|
version_requirements: !ruby/object:Gem::Requirement
|
|
85
79
|
requirements:
|
|
86
80
|
- - "~>"
|
|
87
81
|
- !ruby/object:Gem::Version
|
|
88
|
-
version: '3.
|
|
82
|
+
version: '3.10'
|
|
89
83
|
- !ruby/object:Gem::Dependency
|
|
90
|
-
name:
|
|
84
|
+
name: rspec-its
|
|
91
85
|
requirement: !ruby/object:Gem::Requirement
|
|
92
86
|
requirements:
|
|
93
87
|
- - "~>"
|
|
94
88
|
- !ruby/object:Gem::Version
|
|
95
|
-
version: '3
|
|
89
|
+
version: '1.3'
|
|
96
90
|
type: :development
|
|
97
91
|
prerelease: false
|
|
98
92
|
version_requirements: !ruby/object:Gem::Requirement
|
|
99
93
|
requirements:
|
|
100
94
|
- - "~>"
|
|
101
95
|
- !ruby/object:Gem::Version
|
|
102
|
-
version: '3
|
|
96
|
+
version: '1.3'
|
|
103
97
|
- !ruby/object:Gem::Dependency
|
|
104
|
-
name: rdf-
|
|
98
|
+
name: rdf-isomorphic
|
|
105
99
|
requirement: !ruby/object:Gem::Requirement
|
|
106
100
|
requirements:
|
|
107
101
|
- - "~>"
|
|
108
102
|
- !ruby/object:Gem::Version
|
|
109
|
-
version: '3.
|
|
103
|
+
version: '3.2'
|
|
110
104
|
type: :development
|
|
111
105
|
prerelease: false
|
|
112
106
|
version_requirements: !ruby/object:Gem::Requirement
|
|
113
107
|
requirements:
|
|
114
108
|
- - "~>"
|
|
115
109
|
- !ruby/object:Gem::Version
|
|
116
|
-
version: '3.
|
|
110
|
+
version: '3.2'
|
|
117
111
|
- !ruby/object:Gem::Dependency
|
|
118
|
-
name:
|
|
112
|
+
name: json-ld
|
|
119
113
|
requirement: !ruby/object:Gem::Requirement
|
|
120
114
|
requirements:
|
|
121
115
|
- - "~>"
|
|
122
116
|
- !ruby/object:Gem::Version
|
|
123
|
-
version: '3.
|
|
117
|
+
version: '3.2'
|
|
124
118
|
type: :development
|
|
125
119
|
prerelease: false
|
|
126
120
|
version_requirements: !ruby/object:Gem::Requirement
|
|
127
121
|
requirements:
|
|
128
122
|
- - "~>"
|
|
129
123
|
- !ruby/object:Gem::Version
|
|
130
|
-
version: '3.
|
|
124
|
+
version: '3.2'
|
|
131
125
|
- !ruby/object:Gem::Dependency
|
|
132
|
-
name:
|
|
126
|
+
name: rdf-spec
|
|
133
127
|
requirement: !ruby/object:Gem::Requirement
|
|
134
128
|
requirements:
|
|
135
129
|
- - "~>"
|
|
136
130
|
- !ruby/object:Gem::Version
|
|
137
|
-
version: '
|
|
131
|
+
version: '3.2'
|
|
138
132
|
type: :development
|
|
139
133
|
prerelease: false
|
|
140
134
|
version_requirements: !ruby/object:Gem::Requirement
|
|
141
135
|
requirements:
|
|
142
136
|
- - "~>"
|
|
143
137
|
- !ruby/object:Gem::Version
|
|
144
|
-
version: '
|
|
138
|
+
version: '3.2'
|
|
145
139
|
- !ruby/object:Gem::Dependency
|
|
146
|
-
name:
|
|
140
|
+
name: rdf-vocab
|
|
147
141
|
requirement: !ruby/object:Gem::Requirement
|
|
148
142
|
requirements:
|
|
149
143
|
- - "~>"
|
|
150
144
|
- !ruby/object:Gem::Version
|
|
151
|
-
version:
|
|
145
|
+
version: '3.2'
|
|
152
146
|
type: :development
|
|
153
147
|
prerelease: false
|
|
154
148
|
version_requirements: !ruby/object:Gem::Requirement
|
|
155
149
|
requirements:
|
|
156
150
|
- - "~>"
|
|
157
151
|
- !ruby/object:Gem::Version
|
|
158
|
-
version:
|
|
152
|
+
version: '3.2'
|
|
159
153
|
description: RDF::Turtle is an Turtle reader/writer for the RDF.rb library suite.
|
|
160
154
|
email: public-rdf-ruby@w3.org
|
|
161
155
|
executables: []
|
|
@@ -178,8 +172,13 @@ files:
|
|
|
178
172
|
homepage: https://github.com/ruby-rdf/rdf-turtle
|
|
179
173
|
licenses:
|
|
180
174
|
- Unlicense
|
|
181
|
-
metadata:
|
|
182
|
-
|
|
175
|
+
metadata:
|
|
176
|
+
documentation_uri: https://ruby-rdf.github.io/rdf-turtle
|
|
177
|
+
bug_tracker_uri: https://github.com/ruby-rdf/rdf-turtle/issues
|
|
178
|
+
homepage_uri: https://github.com/ruby-rdf/rdf-turtle
|
|
179
|
+
mailing_list_uri: https://lists.w3.org/Archives/Public/public-rdf-ruby/
|
|
180
|
+
source_code_uri: https://github.com/ruby-rdf/rdf-turtle
|
|
181
|
+
post_install_message:
|
|
183
182
|
rdoc_options: []
|
|
184
183
|
require_paths:
|
|
185
184
|
- lib
|
|
@@ -187,15 +186,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
187
186
|
requirements:
|
|
188
187
|
- - ">="
|
|
189
188
|
- !ruby/object:Gem::Version
|
|
190
|
-
version: '2.
|
|
189
|
+
version: '2.6'
|
|
191
190
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
192
191
|
requirements:
|
|
193
192
|
- - ">="
|
|
194
193
|
- !ruby/object:Gem::Version
|
|
195
194
|
version: '0'
|
|
196
195
|
requirements: []
|
|
197
|
-
rubygems_version: 3.
|
|
198
|
-
signing_key:
|
|
196
|
+
rubygems_version: 3.3.3
|
|
197
|
+
signing_key:
|
|
199
198
|
specification_version: 4
|
|
200
199
|
summary: Turtle reader/writer for Ruby.
|
|
201
200
|
test_files: []
|