diff --git a/etc/turtle.bnf b/etc/turtle.bnf index 81d5f1e..3faf101 100644 --- a/etc/turtle.bnf +++ b/etc/turtle.bnf @@ -26,7 +26,7 @@ iri ::= IRIREF | PrefixedName PrefixedName ::= PNAME_LN | PNAME_NS BlankNode ::= BLANK_NODE_LABEL | ANON reifier ::= '~' (iri | BlankNode)? -reifiedTriple ::= '<<' (subject | reifiedTriple) predicate object reifier* '>>' +reifiedTriple ::= '<<' (subject | reifiedTriple) predicate object reifier? '>>' tripleTerm ::= '<<(' ttSubject predicate ttObject ')>>' ttSubject ::= iri | BlankNode ttObject ::= iri | BlankNode | literal | tripleTerm diff --git a/lib/rdf/turtle/reader.rb b/lib/rdf/turtle/reader.rb index b269fcb..a23388a 100644 --- a/lib/rdf/turtle/reader.rb +++ b/lib/rdf/turtle/reader.rb @@ -451,7 +451,7 @@ def read_object(subject = nil, predicate = nil) ## # Read reifiedTriple # - # reifiedTriple ::= '<<' (subject | reifiedTriple) predicate object reifier* '>>' + # reifiedTriple ::= '<<' (subject | reifiedTriple) predicate object reifier? '>>' # # @return [RDF::Term] def read_reifiedTriple @@ -537,7 +537,7 @@ def read_ttObject(subject = nil, predicate = nil) ## # Read an annotation on a triple # - # annotation := (reifier | '{|' predicateObjectList '|}')* + # annotation := (reifier | annotationBlock)* # # The `reifier` becomes the identifier for a subsequent annotation block (if it exists). If there is no reifier, then a blank node is created. def read_annotation(subject, predicate, object) @@ -548,7 +548,7 @@ def read_annotation(subject, predicate, object) while %w(~ {|).include? @lexer.first.to_s if @lexer.first === '~' - prod(:annotation, %(~})) do + prod(:annotation, %(~)) do @lexer.shift # eat '~' # Emit any pending reifiedTriple if there was no annotation block add_statement('annotation', RDF::Statement(id, RDF.reifies, tt)) if id diff --git a/lib/rdf/turtle/writer.rb b/lib/rdf/turtle/writer.rb index 680899e..ac35916 100644 --- a/lib/rdf/turtle/writer.rb +++ b/lib/rdf/turtle/writer.rb @@ -335,7 +335,7 @@ def format_node(node, **options) # @param [Hash{Symbol => Object}] options # @return [String] def format_tripleTerm(statement, **options) - log_debug("rdfstar") {"#{statement.to_ntriples}"} + log_debug("tripleTerm") {"#{statement.to_ntriples}"} "<<(%s %s %s)>>" % statement.to_a.map { |value| format_term(value, **options) } end @@ -443,7 +443,7 @@ def preprocess_statement(statement, as_subject: true) statement.to_a.each {|t| @in_triple_term[t] = true} end - # If it fits, allow this to be rendered as a reification + # If it fits, allow this to be rendered as a reifiedTriple if statement.object.statement? && statement.predicate == RDF.reifies @reification[statement.subject] ||= [] @reification[statement.subject] << statement.object unless @@ -483,7 +483,7 @@ def reset @serialized = {} @subjects = {} @reification = {} - @as_reification = {} + @as_reifiedTriple = {} @in_triple_term = {} end @@ -572,7 +572,7 @@ def collection(node, position) def blankNodePropertyList?(resource, position) !resource.statement? && resource.node? && !collection?(resource) && - !reification?(resource, position) && + !reifiedTriple?(resource, position) && !in_triple_term?(resource) && (!is_done?(resource) || position == :subject) && ref_count(resource) == (position == :object ? 1 : 0) @@ -590,33 +590,34 @@ def blankNodePropertyList(resource, position) true end - # Is this a reification? - def reification?(resource, position) + # Is this a reifiedTriple? + def reifiedTriple?(resource, position) @reification.key?(resource) && (position == :subject ? (prop_count(resource) > 0) : (prop_count(resource) == 0)) end - # Render a reification - def reification(resource, position) - return false unless reification?(resource, position) + # Render a reifiedTriple + def reifiedTriple(resource, position) + return false unless reifiedTriple?(resource, position) write_id = resource.iri? || ref_count(resource) > 1 - @as_reification[resource] = true # Prevent rdf:reifies from being emitted + @as_reifiedTriple[resource] = true # Prevent rdf:reifies from being emitted - log_debug("reification") {resource.to_ntriples} + log_debug("reifiedTriple") {resource.to_ntriples} subject_done(resource) - # There may be multiple reifications using this resource + # There may be multiple reifiedTriples using this resource @reification[resource].each do |tt| @output.write(position == :subject ? "\n#{indent} << " : '<< ') + reifiedTriple(tt.subject, :object) || p_term(tt.subject, :object) + @output.write(' ') + predicate(tt.predicate) + @output.write(' ') + reifiedTriple(tt.object, :object) || p_term(tt.object, :object) + if write_id # Only need to output blank node identifiers if they have more than one reference + @output.write(' ~') p_term(resource, :subject) - @output.write(' | ') end - reification(tt.subject, :object) || p_term(tt.subject, :object) - @output.write(' ') - predicate(tt.predicate) - @output.write(' ') - reification(tt.object, :object) || p_term(tt.object, :object) @output.write(' >>') end true @@ -645,7 +646,7 @@ def path(resource, position) end raise RDF::WriterError, "Cannot serialize resource '#{resource}'" unless collection(resource, position) || - reification(resource, position) || + reifiedTriple(resource, position) || blankNodePropertyList(resource, position) || p_term(resource, position) end @@ -677,7 +678,7 @@ def objectList(subject, predicate, objects) reifs = @reification.select {|k, v| v.include?(tt)}.keys if reifs.length == 1 reif = reifs.first - @as_reification[reif] = true + @as_reifiedTriple[reif] = true @output.write ' {| ' predicateObjectList(reif, true) @output.write ' |}' @@ -693,7 +694,7 @@ def predicateObjectList(subject, from_bpl = false) prop_list = sort_properties(properties) prop_list -= [RDF.first, RDF.rest] if @lists.key?(subject) - prop_list -= [RDF.reifies] if @as_reification.key?(subject) + prop_list -= [RDF.reifies] if @as_reifiedTriple.key?(subject) log_debug("predicateObjectList") {prop_list.inspect} return 0 if prop_list.empty? diff --git a/spec/writer_spec.rb b/spec/writer_spec.rb index 2365a20..b823fa7 100644 --- a/spec/writer_spec.rb +++ b/spec/writer_spec.rb @@ -653,7 +653,7 @@ regexp: [ %r(ex:s ex:p <<\(\s*ex:s1 ex:p1 <<\(\s*ex:s2 ex:p2 ex:o2*\s*\)>>\s*\)>>) ] - }, + } }.each do |name, params| it name do graph = RDF::Graph.new {|g| g << params[:input]} @@ -661,7 +661,7 @@ end end - context "reifications" do + context "reifiedTriples" do { "subject-iii": { input: %( @@ -765,6 +765,28 @@ %r(<<\s*<<\s*ex:s2 ex:p2 ex:o2\s*>> ex:p1 ex:o1\s*>> ex:p ex:o .) ] }, + "explicit reifier as subject": { + input: %( + PREFIX : + PREFIX rdf: + :r rdf:reifies <<( :s :p :o )>> . + :r :p1 :o1 . + ), + regexp: [ + %r(<<\s*ex:s ex:p ex:o\s*~\s*ex:r\s*>>\s*ex:p1 ex:o1 .) + ] + }, + "explicit reifier as object": { + input: %( + PREFIX : + PREFIX rdf: + :r rdf:reifies <<( :s :p :o )>> . + :s1 :p1 :r . + ), + regexp: [ + %r(ex:s1 ex:p1 <<\s*ex:s ex:p ex:o\s*~\s*ex:r\s*>>\s*.) + ] + }, }.each do |name, params| it name do graph = RDF::Graph.new {|g| g << parse(params[:input], rdfstar: true)}