A semantic search engine for source code https://bitshift.benkurtovic.com/
Não pode escolher mais do que 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 
 
 
 
 

264 linhas
7.0 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 (query conditional string, table set, param list).
  16. """
  17. return "", tables, []
  18. class _Literal(object):
  19. """Represents a literal component of a search query, present at the leaves.
  20. A literal might be a string or a regular expression.
  21. """
  22. pass
  23. class String(_Literal):
  24. """Represents a string literal."""
  25. def __init__(self, string):
  26. """
  27. :type string: unicode
  28. """
  29. self.string = string
  30. def __repr__(self):
  31. return "String({0!r})".format(self.string)
  32. def sortkey(self):
  33. return self.string
  34. class Regex(_Literal):
  35. """Represents a regular expression literal."""
  36. def __init__(self, regex):
  37. """
  38. :type string: unicode
  39. """
  40. self.regex = regex
  41. def __repr__(self):
  42. return "Regex({0!r})".format(self.regex)
  43. def sortkey(self):
  44. return self.regex
  45. class Text(_Node):
  46. """Represents a text node.
  47. Searches in codelet names (full-text search), symbols (equality), and
  48. source code (full-text search).
  49. """
  50. def __init__(self, text):
  51. """
  52. :type text: :py:class:`._Literal`
  53. """
  54. self.text = text
  55. def __repr__(self):
  56. return "Text({0})".format(self.text)
  57. def sortkey(self):
  58. return self.text.sortkey()
  59. def parameterize(self, tables):
  60. tables |= {"code", "symbols"}
  61. if isinstance(self.text, Regex):
  62. cols = ["codelet_name", "symbol_name", "code_code"]
  63. cond = "((" + " REGEXP ?) OR (".join(cols) + " REGEXP ?))"
  64. return cond, [self.text.regex] * 3
  65. conds = ["MATCH(codelet_name) AGAINST (? IN BOOLEAN MODE)",
  66. "MATCH(code_code) AGAINST (? IN BOOLEAN MODE)",
  67. "symbol_name = ?"]
  68. cond = "((" + ") OR (".join(conds) + "))"
  69. return cond, tables, [self.text.string] * 3
  70. class Language(_Node):
  71. """Represents a language node.
  72. Searches in the code_lang field.
  73. """
  74. def __init__(self, lang):
  75. """
  76. :type lang: int
  77. """
  78. self.lang = lang
  79. def __repr__(self):
  80. return "Language({0})".format(LANGS[self.lang])
  81. def sortkey(self):
  82. return LANGS[self.lang]
  83. def parameterize(self, tables):
  84. tables |= {"code"}
  85. return "(code_lang = ?)", tables, [self.lang]
  86. class Author(_Node):
  87. """Represents a author node.
  88. Searches in the author_name field (full-text search).
  89. """
  90. def __init__(self, name):
  91. """
  92. :type name: :py:class:`_Literal`
  93. """
  94. self.name = name
  95. def __repr__(self):
  96. return "Author({0})".format(self.name)
  97. def sortkey(self):
  98. return self.name.sortkey()
  99. def parameterize(self, tables):
  100. tables |= {"authors"}
  101. if isinstance(self.name, Regex):
  102. return "(author_name REGEXP ?)", [self.name.regex]
  103. cond = "(MATCH(author_name) AGAINST (? IN BOOLEAN MODE))"
  104. return cond, tables, [self.name.string]
  105. class Date(_Node):
  106. """Represents a date node.
  107. Searches in the codelet_date_created or codelet_date_modified fields.
  108. """
  109. CREATE = 1
  110. MODIFY = 2
  111. BEFORE = 1
  112. AFTER = 2
  113. def __init__(self, type_, relation, date):
  114. """
  115. :type type_: int (``CREATE`` or ``MODIFY``)
  116. :type relation: int (``BEFORE``, ``AFTER``)
  117. :type date: datetime.datetime
  118. """
  119. self.type = type_
  120. self.relation = relation
  121. self.date = date
  122. def __repr__(self):
  123. types = {self.CREATE: "CREATE", self.MODIFY: "MODIFY"}
  124. relations = {self.BEFORE: "BEFORE", self.AFTER: "AFTER"}
  125. tm = "Date({0}, {1}, {2})"
  126. return tm.format(types[self.type], relations[self.relation], self.date)
  127. def sortkey(self):
  128. return self.date.strftime("%Y%m%d%H%M%S")
  129. def parameterize(self, tables):
  130. column = {self.CREATE: "codelet_date_created",
  131. self.MODIFY: "codelet_date_modified"}[self.type]
  132. op = {self.BEFORE: "<=", self.AFTER: ">="}[self.relation]
  133. return "(" + column + " " + op + " ?)", tables, [self.date]
  134. class Symbol(_Node):
  135. """Represents a symbol node.
  136. Searches in symbol_type and symbol_name.
  137. """
  138. ALL = 0
  139. FUNCTION = 1
  140. CLASS = 2
  141. VARIABLE = 3
  142. TYPES = {ALL: "ALL", FUNCTION: "FUNCTION", CLASS: "CLASS",
  143. VARIABLE: "VARIABLE"}
  144. def __init__(self, type_, name):
  145. """
  146. :type type_: int (``ALL``, ``FUNCTION``, ``CLASS``, etc.)
  147. :type name: :py:class:`.Literal`
  148. """
  149. self.type = type_
  150. self.name = name
  151. def __repr__(self):
  152. return "Symbol({0}, {1})".format(self.TYPES[self.type], self.name)
  153. def sortkey(self):
  154. return self.name.sortkey()
  155. def parameterize(self, tables):
  156. tables |= {"symbols"}
  157. cond_base = "(symbol_type = ? AND symbol_name = ?)"
  158. if self.type != self.ALL:
  159. return cond_base, tables, [self.type, self.name]
  160. cond = "(" + " OR ".join([cond_base] * len(self.TYPES)) + ")"
  161. args = zip(self.TYPES.keys(), [self.name] * len(self.TYPES))
  162. return cond, tables, [arg for tup in args for arg in tup]
  163. class BinaryOp(_Node):
  164. """Represents a relationship between two nodes: ``and``, ``or``."""
  165. AND = object()
  166. OR = object()
  167. OPS = {AND: "AND", OR: "OR"}
  168. def __init__(self, left, op, right):
  169. self.left = left
  170. self.op = op
  171. self.right = right
  172. def __repr__(self):
  173. tmpl = "BinaryOp({0}, {1}, {2})"
  174. return tmpl.format(self.left, self.OPS[self.op], self.right)
  175. def sortkey(self):
  176. return self.left.sortkey() + self.right.sortkey()
  177. def parameterize(self, tables):
  178. left_cond, tables, left_args = self.left.parameterize(tables)
  179. right_cond, tables, right_args = self.right.parameterize(tables)
  180. op = self.OPS[self.op]
  181. cond = "(" + left_cond + " " + op + " " + right_cond + ")"
  182. return cond, tables, left_args + right_args
  183. class UnaryOp(_Node):
  184. """Represents a transformation applied to one node: ``not``."""
  185. NOT = object()
  186. OPS = {NOT: "NOT"}
  187. def __init__(self, op, node):
  188. self.op = op
  189. self.node = node
  190. def __repr__(self):
  191. return "UnaryOp({0}, {1})".format(self.OPS[self.op], self.node)
  192. def sortkey(self):
  193. return self.node.sortkey()
  194. def parameterize(self, tables):
  195. cond, tables, args = self.node.parameterize(tables)
  196. return "(" + self.OPS[self.op] + " " + cond + ")", tables, args