home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / usr / share / python-support / python-rdflib / rdflib / syntax / parsers / ntriples.py < prev    next >
Encoding:
Python Source  |  2009-02-22  |  6.4 KB  |  246 lines

  1. """
  2. N-Triples Parser
  3. License: GPL 2, W3C, BSD, or MIT
  4. Author: Sean B. Palmer, inamidst.com
  5. Documentation:
  6.    http://inamidst.com/proj/rdf/ntriples-doc
  7.  
  8. Command line usage:
  9.    ./ntriples.py <URI>    - parses URI as N-Triples
  10.    ./ntriples.py --help   - prints out this help message
  11. # @@ fully empty document?
  12. """
  13.  
  14. import re
  15.  
  16. uriref = r'<([^:]+:[^\s"<>]+)>'
  17. literal = r'"([^"\\]*(?:\\.[^"\\]*)*)"'
  18. litinfo = r'(?:@([a-z]+(?:-[a-z0-9]+)*)|\^\^' + uriref + r')?'
  19.  
  20. r_line = re.compile(r'([^\r\n]*)(?:\r\n|\r|\n)')
  21. r_wspace = re.compile(r'[ \t]*')
  22. r_wspaces = re.compile(r'[ \t]+')
  23. r_tail = re.compile(r'[ \t]*\.[ \t]*')
  24. r_uriref = re.compile(uriref)
  25. r_nodeid = re.compile(r'_:([A-Za-z][A-Za-z0-9]*)')
  26. r_literal = re.compile(literal + litinfo)
  27.  
  28. bufsiz = 2048
  29. validate = False
  30.  
  31. class Node(unicode): pass
  32.  
  33. # class URI(Node): pass
  34. # class bNode(Node): pass
  35. # class Literal(Node):
  36. #    def __new__(cls, lit, lang=None, dtype=None):
  37. #       n = str(lang) + ' ' + str(dtype) + ' ' + lit
  38. #       return unicode.__new__(cls, n)
  39.  
  40. from rdflib import URIRef as URI
  41. from rdflib import BNode as bNode
  42. from rdflib import Literal
  43.  
  44. class Sink(object):
  45.    def __init__(self):
  46.       self.length = 0
  47.  
  48.    def triple(self, s, p, o):
  49.       self.length += 1
  50.       print (s, p, o)
  51.  
  52. class ParseError(Exception): pass
  53.  
  54. quot = {'t': '\t', 'n': '\n', 'r': '\r', '"': '"', '\\': '\\'}
  55. r_safe = re.compile(r'([\x20\x21\x23-\x5B\x5D-\x7E]+)')
  56. r_quot = re.compile(r'\\(t|n|r|"|\\)')
  57. r_uniquot = re.compile(r'\\u([0-9A-F]{4})|\\U([0-9A-F]{8})')
  58.  
  59. def unquote(s):
  60.    """Unquote an N-Triples string."""
  61.    result = []
  62.    while s:
  63.       m = r_safe.match(s)
  64.       if m:
  65.          s = s[m.end():]
  66.          result.append(m.group(1))
  67.          continue
  68.  
  69.       m = r_quot.match(s)
  70.       if m:
  71.          s = s[2:]
  72.          result.append(quot[m.group(1)])
  73.          continue
  74.  
  75.       m = r_uniquot.match(s)
  76.       if m:
  77.          s = s[m.end():]
  78.          u, U = m.groups()
  79.          codepoint = int(u or U, 16)
  80.          if codepoint > 0x10FFFF:
  81.             raise ParseError("Disallowed codepoint: %08X" % codepoint)
  82.          result.append(unichr(codepoint))
  83.       elif s.startswith('\\'):
  84.          raise ParseError("Illegal escape at: %s..." % s[:10])
  85.       else: raise ParseError("Illegal literal character: %r" % s[0])
  86.    return unicode(''.join(result))
  87.  
  88. if not validate:
  89.    def unquote(s):
  90.       return s.decode('unicode-escape')
  91.  
  92. r_hibyte = re.compile(r'([\x80-\xFF])')
  93.  
  94. def uriquote(uri):
  95.    return r_hibyte.sub(lambda m: '%%%02X' % ord(m.group(1)), uri)
  96. if not validate:
  97.    def uriquote(uri):
  98.       return uri
  99.  
  100. class NTriplesParser(object):
  101.    """An N-Triples Parser.
  102.       Usage:
  103.          p = NTriplesParser(sink=MySink())
  104.          sink = p.parse(f) # file; use parsestring for a string
  105.    """
  106.  
  107.    def __init__(self, sink=None):
  108.       if sink is not None:
  109.          self.sink = sink
  110.       else: self.sink = Sink()
  111.  
  112.    def parse(self, f):
  113.       """Parse f as an N-Triples file."""
  114.       if not hasattr(f, 'read'):
  115.          raise ParseError("Item to parse must be a file-like object.")
  116.  
  117.       self.file = f
  118.       self.buffer = ''
  119.       while True:
  120.          self.line = self.readline()
  121.          if self.line is None: break
  122.          try: self.parseline()
  123.          except ParseError:
  124.             raise ParseError("Invalid line: %r" % self.line)
  125.       return self.sink
  126.  
  127.    def parsestring(self, s):
  128.       """Parse s as an N-Triples string."""
  129.       if not isinstance(s, basestring):
  130.          raise ParseError("Item to parse must be a string instance.")
  131.       from cStringIO import StringIO
  132.       f = StringIO()
  133.       f.write(s)
  134.       f.seek(0)
  135.       self.parse(f)
  136.  
  137.    def readline(self):
  138.       """Read an N-Triples line from buffered input."""
  139.       # N-Triples lines end in either CRLF, CR, or LF
  140.       # Therefore, we can't just use f.readline()
  141.       if not self.buffer:
  142.          buffer = self.file.read(bufsiz)
  143.          if not buffer: return None
  144.          self.buffer = buffer
  145.  
  146.       while True:
  147.          m = r_line.match(self.buffer)
  148.          if m: # the more likely prospect
  149.             self.buffer = self.buffer[m.end():]
  150.             return m.group(1)
  151.          else:
  152.             buffer = self.file.read(bufsiz)
  153.             if not buffer:
  154.                raise ParseError("EOF in line")
  155.             self.buffer += buffer
  156.  
  157.    def parseline(self):
  158.       self.eat(r_wspace)
  159.       if (not self.line) or self.line.startswith('#'):
  160.          return # The line is empty or a comment
  161.  
  162.       subject = self.subject()
  163.       self.eat(r_wspaces)
  164.  
  165.       predicate = self.predicate()
  166.       self.eat(r_wspaces)
  167.  
  168.       object = self.object()
  169.       self.eat(r_tail)
  170.  
  171.       if self.line:
  172.          raise ParseError("Trailing garbage")
  173.       self.sink.triple(subject, predicate, object)
  174.  
  175.    def peek(self, token):
  176.       return self.line.startswith(token)
  177.  
  178.    def eat(self, pattern):
  179.       m = pattern.match(self.line)
  180.       if not m: # @@ Why can't we get the original pattern?
  181.          raise ParseError("Failed to eat %s" % pattern)
  182.       self.line = self.line[m.end():]
  183.       return m
  184.  
  185.    def subject(self):
  186.       # @@ Consider using dictionary cases
  187.       subj = self.uriref() or self.nodeid()
  188.       if not subj:
  189.         raise ParseError("Subject must be uriref or nodeID")
  190.       return subj
  191.  
  192.    def predicate(self):
  193.       pred = self.uriref()
  194.       if not pred:
  195.          raise ParseError("Predicate must be uriref")
  196.       return pred
  197.  
  198.    def object(self):
  199.       objt = self.uriref() or self.nodeid() or self.literal()
  200.       if objt is False:
  201.          raise ParseError("Unrecognised object type")
  202.       return objt
  203.  
  204.    def uriref(self):
  205.       if self.peek('<'):
  206.          uri = self.eat(r_uriref).group(1)
  207.          uri = unquote(uri)
  208.          uri = uriquote(uri)
  209.          return URI(uri)
  210.       return False
  211.  
  212.    def nodeid(self):
  213.       if self.peek('_'):
  214.          return bNode(self.eat(r_nodeid).group(1))
  215.       return False
  216.  
  217.    def literal(self):
  218.       if self.peek('"'):
  219.          lit, lang, dtype = self.eat(r_literal).groups()
  220.          lang = lang or None
  221.          dtype = dtype or None
  222.          if lang and dtype:
  223.             raise ParseError("Can't have both a language and a datatype")
  224.          lit = unquote(lit)
  225.          return Literal(lit, lang, dtype)
  226.       return False
  227.  
  228. def parseURI(uri):
  229.    import urllib
  230.    parser = NTriplesParser()
  231.    u = urllib.urlopen(uri)
  232.    sink = parser.parse(u)
  233.    u.close()
  234.    # for triple in sink:
  235.    #    print triple
  236.    print 'Length of input:', sink.length
  237.  
  238. def main():
  239.    import sys
  240.    if len(sys.argv) == 2:
  241.       parseURI(sys.argv[1])
  242.    else: print __doc__
  243.  
  244. if __name__=="__main__":
  245.    main()
  246.