trig.py 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. from __future__ import annotations
  2. from typing import Any, MutableSequence
  3. from rdflib.graph import ConjunctiveGraph, Graph
  4. from rdflib.parser import InputSource, Parser
  5. from .notation3 import RDFSink, SinkParser
  6. def becauseSubGraph(*args, **kwargs): # noqa: N802
  7. pass
  8. class TrigSinkParser(SinkParser):
  9. def directiveOrStatement(self, argstr: str, h: int) -> int: # noqa: N802
  10. # import pdb; pdb.set_trace()
  11. i = self.skipSpace(argstr, h)
  12. if i < 0:
  13. return i # EOF
  14. j = self.graph(argstr, i)
  15. if j >= 0:
  16. return j
  17. j = self.sparqlDirective(argstr, i)
  18. if j >= 0:
  19. return j
  20. j = self.directive(argstr, i)
  21. if j >= 0:
  22. return self.checkDot(argstr, j)
  23. j = self.statement(argstr, i)
  24. if j >= 0:
  25. return self.checkDot(argstr, j)
  26. return j
  27. def labelOrSubject( # noqa: N802
  28. self, argstr: str, i: int, res: MutableSequence[Any]
  29. ) -> int:
  30. j = self.skipSpace(argstr, i)
  31. if j < 0:
  32. return j # eof
  33. i = j
  34. j = self.uri_ref2(argstr, i, res)
  35. if j >= 0:
  36. return j
  37. if argstr[i] == "[":
  38. j = self.skipSpace(argstr, i + 1)
  39. if j < 0:
  40. self.BadSyntax(argstr, i, "Expected ] got EOF")
  41. if argstr[j] == "]":
  42. res.append(self.blankNode())
  43. return j + 1
  44. return -1
  45. def graph(self, argstr: str, i: int) -> int:
  46. """
  47. Parse trig graph, i.e.
  48. <urn:graphname> = { .. triples .. }
  49. return -1 if it doesn't look like a graph-decl
  50. raise Exception if it looks like a graph, but isn't.
  51. """
  52. need_graphid = False
  53. # import pdb; pdb.set_trace()
  54. j = self.sparqlTok("GRAPH", argstr, i) # optional GRAPH keyword
  55. if j >= 0:
  56. i = j
  57. need_graphid = True
  58. r: MutableSequence[Any] = []
  59. j = self.labelOrSubject(argstr, i, r)
  60. if j >= 0:
  61. graph = r[0]
  62. i = j
  63. elif need_graphid:
  64. self.BadSyntax(argstr, i, "GRAPH keyword must be followed by graph name")
  65. else:
  66. graph = self._store.graph.identifier # hack
  67. j = self.skipSpace(argstr, i)
  68. if j < 0:
  69. self.BadSyntax(argstr, i, "EOF found when expected graph")
  70. if argstr[j : j + 1] == "=": # optional = for legacy support
  71. i = self.skipSpace(argstr, j + 1)
  72. if i < 0:
  73. self.BadSyntax(argstr, i, "EOF found when expecting '{'")
  74. else:
  75. i = j
  76. if argstr[i : i + 1] != "{":
  77. return -1 # the node wasn't part of a graph
  78. j = i + 1
  79. if self._context is not None:
  80. self.BadSyntax(argstr, i, "Nested graphs are not allowed")
  81. oldParentContext = self._parentContext # noqa: N806
  82. self._parentContext = self._context
  83. reason2 = self._reason2
  84. self._reason2 = becauseSubGraph
  85. # type error: Incompatible types in assignment (expression has type "Graph", variable has type "Optional[Formula]")
  86. self._context = self._store.newGraph(graph) # type: ignore[assignment]
  87. while 1:
  88. i = self.skipSpace(argstr, j)
  89. if i < 0:
  90. self.BadSyntax(argstr, i, "needed '}', found end.")
  91. if argstr[i : i + 1] == "}":
  92. j = i + 1
  93. break
  94. j = self.directiveOrStatement(argstr, i)
  95. if j < 0:
  96. self.BadSyntax(argstr, i, "expected statement or '}'")
  97. self._context = self._parentContext
  98. self._reason2 = reason2
  99. self._parentContext = oldParentContext
  100. # res.append(subj.close()) # No use until closed
  101. return j
  102. class TrigParser(Parser):
  103. """
  104. An RDFLib parser for TriG
  105. """
  106. def __init__(self):
  107. pass
  108. def parse(self, source: InputSource, graph: Graph, encoding: str = "utf-8") -> None:
  109. if encoding not in [None, "utf-8"]:
  110. raise Exception(
  111. # type error: Unsupported left operand type for % ("Tuple[str, str]")
  112. ("TriG files are always utf-8 encoded, ", "I was passed: %s") # type: ignore[operator]
  113. % encoding
  114. )
  115. # we're currently being handed a Graph, not a ConjunctiveGraph
  116. assert graph.store.context_aware, "TriG Parser needs a context-aware store!"
  117. conj_graph = ConjunctiveGraph(store=graph.store, identifier=graph.identifier)
  118. conj_graph.default_context = graph # TODO: CG __init__ should have a
  119. # default_context arg
  120. # TODO: update N3Processor so that it can use conj_graph as the sink
  121. conj_graph.namespace_manager = graph.namespace_manager
  122. sink = RDFSink(conj_graph)
  123. baseURI = conj_graph.absolutize( # noqa: N806
  124. source.getPublicId() or source.getSystemId() or ""
  125. )
  126. p = TrigSinkParser(sink, baseURI=baseURI, turtle=True)
  127. stream = source.getCharacterStream() # try to get str stream first
  128. if not stream:
  129. # fallback to get the bytes stream
  130. stream = source.getByteStream()
  131. p.loadStream(stream)
  132. for prefix, namespace in p._bindings.items():
  133. conj_graph.bind(prefix, namespace)
  134. # return ???