A semantic search engine for source code https://bitshift.benkurtovic.com/
No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.
 
 
 
 
 
 

273 líneas
7.3 KiB

  1. from ..languages import LANGS
  2. __all__ = ["String", "Regex", "Text", "Language", "Author", "Date", "Symbol",
  3. "BinaryOp", "UnaryOp"]
  4. class _Node(object):
  5. """Represents a single node in a query tree.
  6. Generally speaking, a node is a constraint applied to the database. Thus,
  7. a :py:class:`~.Language` node represents a constraint where only codelets
  8. of a specific language are selected.
  9. """
  10. def sortkey(self):
  11. """Return a string sort key for the node."""
  12. return ""
  13. def parameterize(self, tables):
  14. """Parameterize the node.
  15. Returns a 3-tuple of (conditional string, rank list, parameter list).
  16. If the rank list is empty, then it is assumed to contain the
  17. conditional string.
  18. """
  19. return "", [], []
  20. class _Literal(object):
  21. """Represents a literal component of a search query, present at the leaves.
  22. A literal might be a string or a regular expression.
  23. """
  24. pass
  25. class String(_Literal):
  26. """Represents a string literal."""
  27. def __init__(self, string):
  28. """
  29. :type string: unicode
  30. """
  31. self.string = string
  32. def __repr__(self):
  33. return "String({0!r})".format(self.string)
  34. def sortkey(self):
  35. return self.string
  36. class Regex(_Literal):
  37. """Represents a regular expression literal."""
  38. def __init__(self, regex):
  39. """
  40. :type string: unicode
  41. """
  42. self.regex = regex
  43. def __repr__(self):
  44. return "Regex({0!r})".format(self.regex)
  45. def sortkey(self):
  46. return self.regex
  47. class Text(_Node):
  48. """Represents a text node.
  49. Searches in codelet names (full-text search), symbols (equality), and
  50. source code (full-text search).
  51. """
  52. def __init__(self, text):
  53. """
  54. :type text: :py:class:`._Literal`
  55. """
  56. self.text = text
  57. def __repr__(self):
  58. return "Text({0})".format(self.text)
  59. def sortkey(self):
  60. return self.text.sortkey()
  61. def parameterize(self, tables):
  62. tables |= {"code", "symbols"}
  63. if isinstance(self.text, Regex):
  64. ranks = ["(codelet_name REGEXP ?)", "(symbol_name REGEXP ?)",
  65. "(code_code REGEXP ?)"]
  66. text = self.text.regex
  67. else:
  68. ranks = ["(MATCH(codelet_name) AGAINST (? IN BOOLEAN MODE))",
  69. "(MATCH(code_code) AGAINST (? IN BOOLEAN MODE))",
  70. "(symbol_name = ?)"]
  71. text = self.text.string
  72. return cond, ranks, [text] * 3
  73. class Language(_Node):
  74. """Represents a language node.
  75. Searches in the code_lang field.
  76. """
  77. def __init__(self, lang):
  78. """
  79. :type lang: int
  80. """
  81. self.lang = lang
  82. def __repr__(self):
  83. return "Language({0})".format(LANGS[self.lang])
  84. def sortkey(self):
  85. return LANGS[self.lang]
  86. def parameterize(self, tables):
  87. tables |= {"code"}
  88. return "(code_lang = ?)", [], [self.lang]
  89. class Author(_Node):
  90. """Represents a author node.
  91. Searches in the author_name field (full-text search).
  92. """
  93. def __init__(self, name):
  94. """
  95. :type name: :py:class:`_Literal`
  96. """
  97. self.name = name
  98. def __repr__(self):
  99. return "Author({0})".format(self.name)
  100. def sortkey(self):
  101. return self.name.sortkey()
  102. def parameterize(self, tables):
  103. tables |= {"authors"}
  104. if isinstance(self.name, Regex):
  105. return "(author_name REGEXP ?)", [], [self.name.regex]
  106. cond = "(MATCH(author_name) AGAINST (? IN BOOLEAN MODE))"
  107. return cond, [], [self.name.string]
  108. class Date(_Node):
  109. """Represents a date node.
  110. Searches in the codelet_date_created or codelet_date_modified fields.
  111. """
  112. CREATE = 1
  113. MODIFY = 2
  114. BEFORE = 1
  115. AFTER = 2
  116. def __init__(self, type_, relation, date):
  117. """
  118. :type type_: int (``CREATE`` or ``MODIFY``)
  119. :type relation: int (``BEFORE``, ``AFTER``)
  120. :type date: datetime.datetime
  121. """
  122. self.type = type_
  123. self.relation = relation
  124. self.date = date
  125. def __repr__(self):
  126. types = {self.CREATE: "CREATE", self.MODIFY: "MODIFY"}
  127. relations = {self.BEFORE: "BEFORE", self.AFTER: "AFTER"}
  128. tm = "Date({0}, {1}, {2})"
  129. return tm.format(types[self.type], relations[self.relation], self.date)
  130. def sortkey(self):
  131. return self.date.strftime("%Y%m%d%H%M%S")
  132. def parameterize(self, tables):
  133. column = {self.CREATE: "codelet_date_created",
  134. self.MODIFY: "codelet_date_modified"}[self.type]
  135. op = {self.BEFORE: "<=", self.AFTER: ">="}[self.relation]
  136. return "(" + column + " " + op + " ?)", [], [self.date]
  137. class Symbol(_Node):
  138. """Represents a symbol node.
  139. Searches in symbol_type and symbol_name.
  140. """
  141. ALL = 0
  142. FUNCTION = 1
  143. CLASS = 2
  144. VARIABLE = 3
  145. TYPES = {FUNCTION: "FUNCTION", CLASS: "CLASS", VARIABLE: "VARIABLE"}
  146. def __init__(self, type_, name):
  147. """
  148. :type type_: int (``ALL``, ``FUNCTION``, ``CLASS``, etc.)
  149. :type name: :py:class:`._Literal`
  150. """
  151. self.type = type_
  152. self.name = name
  153. def __repr__(self):
  154. type_ = self.TYPES.get(self.type, "ALL")
  155. return "Symbol({0}, {1})".format(type_, self.name)
  156. def sortkey(self):
  157. return self.name.sortkey()
  158. def parameterize(self, tables):
  159. tables |= {"code", "symbols"}
  160. if isinstance(self.name, Regex):
  161. cond, name = "symbol_name REGEXP ?", self.name.regex
  162. else:
  163. cond, name = "symbol_name = ?", self.name.string
  164. if self.type == self.ALL:
  165. types = ", ".join(str(type_) for type_ in self.TYPES)
  166. cond += " AND symbol_type IN (%s)" % types
  167. if self.type != self.ALL:
  168. cond += " AND symbol_type = %d" % self.type
  169. return "(" + cond + ")", [], [name]
  170. class BinaryOp(_Node):
  171. """Represents a relationship between two nodes: ``and``, ``or``."""
  172. AND = object()
  173. OR = object()
  174. OPS = {AND: "AND", OR: "OR"}
  175. def __init__(self, left, op, right):
  176. self.left = left
  177. self.op = op
  178. self.right = right
  179. def __repr__(self):
  180. tmpl = "BinaryOp({0}, {1}, {2})"
  181. return tmpl.format(self.left, self.OPS[self.op], self.right)
  182. def sortkey(self):
  183. return self.left.sortkey() + self.right.sortkey()
  184. def parameterize(self, tables):
  185. lcond, lranks, largs = self.left.parameterize(tables)
  186. rcond, rranks, rargs = self.right.parameterize(tables)
  187. lranks, rranks = lranks or [lcond], rranks or [rcond]
  188. op = self.OPS[self.op]
  189. cond = "(" + lcond + " " + op + " " + rcond + ")"
  190. return cond, lranks + rranks, largs + rargs
  191. class UnaryOp(_Node):
  192. """Represents a transformation applied to one node: ``not``."""
  193. NOT = object()
  194. OPS = {NOT: "NOT"}
  195. def __init__(self, op, node):
  196. self.op = op
  197. self.node = node
  198. def __repr__(self):
  199. return "UnaryOp({0}, {1})".format(self.OPS[self.op], self.node)
  200. def sortkey(self):
  201. return self.node.sortkey()
  202. def parameterize(self, tables):
  203. cond, ranks, args = self.node.parameterize(tables)
  204. ranks = ranks or [cond]
  205. return "(" + self.OPS[self.op] + " " + cond + ")", ranks, args