Skip to content

Commit

Permalink
* Allow annotation* in objectList; reads 0 or many annotations for a …
Browse files Browse the repository at this point in the history
…given object.

* Change "reification" to "reifier".
  • Loading branch information
gkellogg committed Apr 20, 2024
1 parent f98a2d4 commit d0d30ac
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 31 deletions.
8 changes: 4 additions & 4 deletions etc/turtle.bnf
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ sparqlPrefix ::= "PREFIX" PNAME_NS IRIREF
sparqlBase ::= "BASE" IRIREF
triples ::= subject predicateObjectList | blankNodePropertyList predicateObjectList?
predicateObjectList ::= verb objectList (';' (verb objectList)? )*
objectList ::= object annotation? ( ',' object annotation? )*
objectList ::= object annotation* ( ',' object annotation* )*
verb ::= predicate | 'a'
subject ::= iri | BlankNode | collection | reification
subject ::= iri | BlankNode | collection | reifier
predicate ::= iri
object ::= iri | BlankNode | collection | blankNodePropertyList | literal | tripleTerm | reification
object ::= iri | BlankNode | collection | blankNodePropertyList | literal | tripleTerm | reifier
literal ::= RDFLiteral | NumericLiteral | BooleanLiteral
blankNodePropertyList ::= '[' predicateObjectList ']'
collection ::= '(' object* ')'
Expand All @@ -23,7 +23,7 @@ String ::= STRING_LITERAL_QUOTE | STRING_LITERAL_SINGLE_QUOTE
iri ::= IRIREF | PrefixedName
PrefixedName ::= PNAME_LN | PNAME_NS
BlankNode ::= BLANK_NODE_LABEL | ANON
reification ::= '<<' ((iri | BlankNode) '|' )? subject predicate object '>>'
reifier ::= '<<' ((iri | BlankNode) '|' )? subject predicate object '>>'
tripleTerm ::= '<<(' subject predicate ttObject ')>>'
ttObject ::= iri | BlankNode | literal | tripleTerm
annotation ::= '{|' ( (iri | BlankNode) '|' )? predicateObjectList '|}'
Expand Down
51 changes: 25 additions & 26 deletions lib/rdf/turtle/reader.rb
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,7 @@ def read_predicateObjectList(subject)
##
# Read objectList
#
# objectList ::= object annotation? ( ',' object annotation? )*
# objectList ::= object annotation* ( ',' object annotation* )*
#
# @return [RDF::Term] the last matched subject
def read_objectList(subject, predicate)
Expand All @@ -382,8 +382,8 @@ def read_objectList(subject, predicate)
while object = prod(:_objectList_2) {read_object(subject, predicate)}
last_object = object

# If object is followed by an annotation, read that and also emit the reification.
read_annotation(subject, predicate, object)
# If object is followed by annotations, read them.
read_annotations(subject, predicate, object)

break unless @lexer.first === ','
@lexer.shift while @lexer.first === ','
Expand All @@ -410,15 +410,15 @@ def read_verb
end
end

# subject ::= iri | BlankNode | collection | reification
# subject ::= iri | BlankNode | collection | reifier
#
# @return [RDF::Resource]
def read_subject
prod(:subject) do
read_iri ||
read_BlankNode ||
read_collection ||
read_reification ||
read_reifier ||
error( "Expected subject", production: :subject, token: @lexer.first)
end
end
Expand All @@ -427,7 +427,7 @@ def read_subject
# Read object
#
# object ::= iri | BlankNode | collection | blankNodePropertyList
# | literal | tripleTerm | reification
# | literal | tripleTerm | reifier
#
# @return [void]
def read_object(subject = nil, predicate = nil)
Expand All @@ -438,7 +438,7 @@ def read_object(subject = nil, predicate = nil)
read_blankNodePropertyList ||
read_literal ||
read_tripleTerm ||
read_reification
read_reifier

add_statement(:object, RDF::Statement(subject, predicate, object)) if subject && predicate
object
Expand All @@ -447,32 +447,32 @@ def read_object(subject = nil, predicate = nil)
end

##
# Read reification
# Read reifier
#
# reification ::= '<<' ((iri | BlankNode) '|' )? subject predicate object '>>'
# reifier ::= '<<' ((iri | BlankNode) '|' )? subject predicate object '>>'
#
# @return [RDF::Term]
def read_reification
def read_reifier
return unless @options[:rdfstar]
if @lexer.first.value == '<<'
prod(:reification) do
prod(:reifier) do
@lexer.shift # eat <<
# Optional identifier for reification
# Optional identifier for reifier
id = read_iri || read_BlankNode
if id && @lexer.first.value == '|'
@lexer.shift # eat |
subject = read_subject || error("Failed to parse subject", production: :reification, token: @lexer.first)
subject = read_subject || error("Failed to parse subject", production: :reifier, token: @lexer.first)
elsif @lexer.first.value == '|'
error("Failed to parse reification identifier", production: :reification, token: @lexer.first)
error("Failed to parse reifier identifier", production: :reifier, token: @lexer.first)
else
# No ID read or missing separator
subject = id || read_subject || error("Failed to parse subject", production: :reification, token: @lexer.first)
subject = id || read_subject || error("Failed to parse subject", production: :reifier, token: @lexer.first)
id = bnode
end
predicate = read_verb || error("Failed to parse predicate", production: :reification, token: @lexer.first)
object = read_object || error("Failed to parse object", production: :reification, token: @lexer.first)
predicate = read_verb || error("Failed to parse predicate", production: :reifier, token: @lexer.first)
object = read_object || error("Failed to parse object", production: :reifier, token: @lexer.first)
unless @lexer.first.value == '>>'
error("Failed to end of triple occurence", production: :reification, token: @lexer.first)
error("Failed to end of triple occurence", production: :reifier, token: @lexer.first)
end
@lexer.shift
tt = RDF::Statement(subject, predicate, object, tripleTerm: true)
Expand Down Expand Up @@ -526,18 +526,18 @@ def read_ttObject(subject = nil, predicate = nil)
##
# Read an annotation on a triple
#
# annotation := '{|' ( (iri | BlankNode) '|' )? predicateObjectList '|}'
#
def read_annotation(subject, predicate, object)
error("Unexpected end of file", production: :annotation) unless token = @lexer.first
if token === '{|'
# annotation := ('{|' ( (iri | BlankNode) '|' )? predicateObjectList '|}')*
def read_annotations(subject, predicate, object)
error("Unexpected end of file", production: :annotation) unless @lexer.first
while @lexer.first === '{|'
prod(:annotation, %(|})) do
@lexer.shift
# Optional identifier for reification
# Optional identifier for reifier
tt = RDF::Statement(subject, predicate, object, tripleTerm: true)
id = read_iri || read_BlankNode
if id && @lexer.first.value == '|'
@lexer.shift # eat |
progress("anotation", depth: options[:depth]) {"identifier: #{id.to_ntriples}"}
# Parsed annotation identifier
elsif @lexer.first.value == '|'
# expected annotation identifier
Expand All @@ -555,8 +555,7 @@ def read_annotation(subject, predicate, object)
id = bnode
end

## XXX replacement for rdf:reifies
statement = RDF::Statement(id, RDF.to_uri + 'reifies', tt)
statement = RDF::Statement(id, RDF.reifies, tt)
add_statement('annotation', statement)

# id becomes subject for predicateObjectList
Expand Down
15 changes: 14 additions & 1 deletion spec/reader_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1149,7 +1149,20 @@
_:anno2 <http://example/graph> <http://host2/> .
_:anno2 <http://example/date> "2020-12-31"^^<http://www.w3.org/2001/XMLSchema#date> .
)
]
],
'multiple annotations' => [
%(
PREFIX : <http://example/>
:s :p :o {| :id1 | :r :z |} {| :id2 | :s :w |}.
),
%(
<http://example/s> <http://example/p> <http://example/o> .
<http://example/id1> <http://www.w3.org/1999/02/22-rdf-syntax-ns#reifies> <<(<http://example/s> <http://example/p> <http://example/o>)>> .
<http://example/id1> <http://example/r> <http://example/z> .
<http://example/id2> <http://www.w3.org/1999/02/22-rdf-syntax-ns#reifies> <<(<http://example/s> <http://example/p> <http://example/o>)>> .
<http://example/id2> <http://example/s> <http://example/w> .
)
],
}.each do |name, (ttl, nt)|
it name do
expect_graph = RDF::Graph.new {|g| g << RDF::NTriples::Reader.new(nt, rdfstar: true)}
Expand Down

0 comments on commit d0d30ac

Please sign in to comment.