require 'treetop' require File.join(File.dirname(__FILE__), 'parser') Treetop.load(File.join(File.dirname(__FILE__), "n3_grammar")) module RdfContext class Parser; end class N3Parser < Parser # Parse N3 document from a string or input stream to closure or graph. # # If the parser is called with a block, triples are passed to the block rather # than added to the graph. # # @param [String] n3_str:: the Notation3/Turtle string # @param [String] uri:: the URI of the document # @param [Hash] options:: Options include the following # options[:debug]:: Array to place debug messages # options[:strict]:: Abort or proceed on error # @return [Graph] # @raise RdfContext::RdfException or subclass # # @author Patrick Sinclair (metade) def parse(stream, uri = nil, options = {}, &block) # :yields: triple super @callback = block parser = N3GrammerParser.new @doc = stream.respond_to?(:read) ? stream.read : stream document = parser.parse(@doc) unless document reason = parser.failure_reason raise ParserException.new(reason) end process_directives(document) process_statements(document) @graph end protected def process_directives(document) directives = document.elements.find_all { |e| e.elements.first.respond_to? :directive } directives.map! { |d| d.elements.first } directives.each { |d| namespace(d.uri_ref.uri.text_value, d.nprefix.text_value) } end def namespace(uri, prefix) uri = @uri if uri == '#' prefix = '__local__' if prefix == '' @graph.bind(Namespace.new(uri, prefix)) end def process_statements(document) subjects = document.elements.find_all { |e| e.elements.first.respond_to? :subject } subjects.map! { |s| s.elements.first } subjects.each do |s| subject = process_node(s.subject) properties = process_properties(s.property_list) properties.each do |p| predicate = process_verb(p.verb) objects = process_objects(p.object_list) objects.each { |object| add_triple("statement", subject, predicate, object) } end end end def process_anonnode(anonnode) bnode = BNode.new properties = process_properties(anonnode.property_list) properties.each do |p| predicate = process_node(p.verb) objects = process_objects(p.object_list) objects.each { |object| add_triple("anonnode", bnode, predicate, object) } end bnode end def process_verb(verb) return URIRef.new('http://www.w3.org/1999/02/22-rdf-syntax-ns#type') if (verb.text_value=='a') return process_node(verb) end def process_node(node) if (node.respond_to? :uri) URIRef.new(node.uri.text_value) else prefix = (node.respond_to? :nprefix) ? node.nprefix.text_value : nil localname = node.localname.text_value build_uri(prefix, localname) end end def process_properties(properties) result = [] result << properties if (properties.respond_to? :verb) result << process_properties(properties.property_list) if (properties.respond_to? :property_list) result.flatten end def process_objects(objects) result = [] if (objects.respond_to? :object) result << process_object(objects.object) else result << process_object(objects) end result << process_objects(objects.object_list) if (objects.respond_to? :object_list) result.flatten end def process_object(object) if (object.respond_to? :localname or object.respond_to? :uri) process_node(object) elsif (object.respond_to? :property_list) process_anonnode(object) else process_literal(object) end end def process_literal(object) encoding, language = nil, nil string, type = object.elements unless type.elements.nil? #puts type.elements.inspect if (type.elements[0].text_value=='@') language = type.elements[1].text_value else encoding = type.elements[1].text_value end end # Evaluate text_value to remove redundant escapes #puts string.elements[1].text_value.dump Literal.n3_encoded(string.elements[1].text_value, language, encoding) end def build_uri(prefix, localname) prefix = '__local__' if prefix.nil? if (prefix=='_') BNode.new(localname, @named_bnodes) else @graph.nsbinding[prefix].send(localname) end end end end