require 'rdf/turtle' module RDF::TriG ## # A parser for the TriG # # Leverages the Turtle reader class Reader < RDF::Turtle::Reader format Format # Terminals passed to lexer. Order matters! terminal(:ANON, ANON) terminal(:BLANK_NODE_LABEL, BLANK_NODE_LABEL) terminal(:IRIREF, IRIREF, unescape: true) terminal(:DOUBLE, DOUBLE) terminal(:DECIMAL, DECIMAL) terminal(:INTEGER, INTEGER) terminal(:PNAME_LN, PNAME_LN, unescape: true) terminal(:PNAME_NS, PNAME_NS) terminal(:STRING_LITERAL_LONG_SINGLE_QUOTE, STRING_LITERAL_LONG_SINGLE_QUOTE, unescape: true, partial_regexp: /^'''/) terminal(:STRING_LITERAL_LONG_QUOTE, STRING_LITERAL_LONG_QUOTE, unescape: true, partial_regexp: /^"""/) terminal(:STRING_LITERAL_QUOTE, STRING_LITERAL_QUOTE, unescape: true) terminal(:STRING_LITERAL_SINGLE_QUOTE, STRING_LITERAL_SINGLE_QUOTE, unescape: true) # String terminals terminal(nil, %r([\{\}\(\),.;\[\]a]|\^\^|true|false)) terminal(:GRAPH, /graph/i) terminal(:PREFIX, PREFIX) terminal(:BASE, BASE) terminal(:LANGTAG, LANGTAG) ## # Iterates the given block for each RDF statement in the input. # # @yield [statement] # @yieldparam [RDF::Statement] statement # @return [void] def each_statement(&block) if block_given? @recovering = false @callback = block begin while (@lexer.first rescue true) read_trigDoc end rescue EBNF::LL1::Lexer::Error, SyntaxError, EOFError, Recovery # Terminate loop if EOF found while recovering end if validate? if !warnings.empty? && !@options[:warnings] $stderr.puts "Warnings: #{warnings.join("\n")}" end if !errors.empty? $stderr.puts "Errors: #{errors.join("\n")}" unless @options[:errors] raise RDF::ReaderError, "Errors found during processing" end end end enum_for(:each_statement) end ## # Iterates the given block for each RDF quad in the input. # # @yield [subject, predicate, object, graph_name] # @yieldparam [RDF::Resource] subject # @yieldparam [RDF::URI] predicate # @yieldparam [RDF::Value] object # @yieldparam [RDF::URI] graph_name # @return [void] def each_quad(&block) if block_given? each_statement do |statement| block.call(*statement.to_quad) end end enum_for(:each_quad) end # add a statement, object can be literal or URI or bnode # # @param [Symbol] production # @param [RDF::Statement] statement the subject of the statement # @return [RDF::Statement] Added statement # @raise [RDF::ReaderError] Checks parameter types and raises if they are incorrect if parsing mode is _validate_. def add_statement(production, statement) error("Statement is invalid: #{statement.inspect.inspect}", production: produciton) if validate? && statement.invalid? statement.graph_name = @graph_name if @graph_name @callback.call(statement) if statement.subject && statement.predicate && statement.object && (validate? ? statement.valid? : true) end protected # @return [Object] def read_trigDoc prod(:trigDoc, %(} .)) do read_directive || read_block end end # @return [Object] def read_block prod(:block, %(})) do @graph_name = nil token = @lexer.first case token && (token.type || token.value) when :GRAPH @lexer.shift @graph_name = read_labelOrSubject || error("Expected label or subject", production: :block, token: @lexer.first) read_wrappedGraph || error("Expected wrappedGraph", production: :block, token: @lexer.first) @graph_name = nil when :IRIREF, :BLANK_NODE_LABEL, :ANON, :PNAME_LN, :PNAME_NS read_triplesOrGraph || error("Expected triplesOrGraph", production: :block, token: @lexer.first) when '{' read_wrappedGraph || error("Expected wrappedGraph", production: :block, token: @lexer.first) when '(', '[' read_triples2 || error("Expected collection or blankNodePropertyList", production: :block, token: @lexer.first) when nil # End of input else error("Unexpected token", production: :block, token: @lexer.first) end end end # @return [Object] def read_triplesOrGraph while name = read_labelOrSubject prod(:triplesOrGraph, %(} .)) do token = @lexer.first case token && token.value when '{' @graph_name = name read_wrappedGraph || error("Expected wrappedGraph", production: :triplesOrGraph, token: @lexer.first) @graph_name = nil true else read_predicateObjectList(name) || error("Expected predicateObjectList", production: :triplesOrGraph, token: @lexer.first) unless @recovering # If recovering, we will have eaten the closing '.' token = @lexer.shift unless token && token.value == '.' error("Expected '.' following triple", production: :triplesOrGraph, token: token) end end end end end true end # @return [Object] def read_triples2 token = @lexer.first case token && token.value when '[' prod(:triples2) do # blankNodePropertyList predicateObjectList? subject = read_blankNodePropertyList || error("Failed to parse blankNodePropertyList", production: :triples2, token: @lexer.first) read_predicateObjectList(subject) if !@recovering || @lexer.first === '.' # If recovering, we will have eaten the closing '.' token = @lexer.shift unless token && token.value == '.' error("Expected '.' following triple", production: :triples2, token: token) end end true end when '(' prod(:triples2) do subject = read_collection || error("Failed to parse read_collection", production: :triples2, token: @lexer.first) token = @lexer.first case token && (token.type || token.value) when 'a', :IRIREF, :PNAME_LN, :PNAME_NS then read_predicateObjectList(subject) else error("Expected predicateObjectList after collection subject", production: :triples2, token: token) end if !@recovering || @lexer.first === '.' # If recovering, we will have eaten the closing '.' token = @lexer.shift unless token && token.value == '.' error("Expected '.' following triple", production: :triples2, token: token) end end true end end end # @return [Object] def read_wrappedGraph token = @lexer.first if token && token.value == '{' prod(:wrappedGraph, %w(})) do @lexer.shift while read_triplesBlock # Read until nothing found end if !@recovering || @lexer.first === '}' # If recovering, we will have eaten the closing '}' token = @lexer.shift unless token && token.value == '}' error("Expected '}' following triple", production: :wrappedGraph, token: token) end end true end end end # @return [Object] def read_triplesBlock prod(:triplesBlock, %w(.)) do while (token = @lexer.first) && token.value != '}' && read_triples break unless @lexer.first === '.' @lexer.shift end end end # @return [RDF::Resource] def read_labelOrSubject prod(:labelOrSubject) do read_iri || read_BlankNode end end end # class Reader end # module RDF::Turtle