| @@ -4,6 +4,7 @@ frontend into trees that can be used by the database backend. | |||||
| """ | """ | ||||
| from __future__ import unicode_literals | from __future__ import unicode_literals | ||||
| from re import IGNORECASE, search | |||||
| from shlex import split | from shlex import split | ||||
| from dateutil.parser import parse as parse_date | from dateutil.parser import parse as parse_date | ||||
| @@ -35,10 +36,35 @@ class _QueryParser(object): | |||||
| self._parse_variable: ["v", "var", "variable"] | self._parse_variable: ["v", "var", "variable"] | ||||
| } | } | ||||
| def _parse_literal(self, literal): | |||||
| """Parse part of a search query into a string or regular expression.""" | |||||
| if literal.startswith(("r:", "re:", "regex:", "regexp:")): | |||||
| return Regex(literal.split(":", 1)[1]) | |||||
| return String(literal) | |||||
| def _parse_language(self, term): | def _parse_language(self, term): | ||||
| """Parse part of a query into a language node and return it.""" | """Parse part of a query into a language node and return it.""" | ||||
| ## TODO: look up language ID | |||||
| return Language(0) | |||||
| term = self._parse_literal(term) | |||||
| if isinstance(term, Regex): | |||||
| langs = [i for i, lang in enumerate(LANGS) | |||||
| if search(term.regex, lang, IGNORECASE)] | |||||
| if not langs: | |||||
| err = "No languages found for regex: %r" % term.regex | |||||
| raise QueryParseException(err) | |||||
| node = Language(langs.pop()) | |||||
| while langs: | |||||
| node = BinaryOp(Language(langs.pop()), BinaryOp.OR, node) | |||||
| return node | |||||
| needle = term.string.lower() | |||||
| for i, lang in enumerate(LANGS): | |||||
| if lang.lower() == needle: | |||||
| return Language(i) | |||||
| for i, lang in enumerate(LANGS): | |||||
| if lang.lower().startswith(needle): | |||||
| return Language(i) | |||||
| err = "No languages found for string: %r" % term.string | |||||
| raise QueryParseException(err) | |||||
| def _parse_author(self, term): | def _parse_author(self, term): | ||||
| """Parse part of a query into an author node and return it.""" | """Parse part of a query into an author node and return it.""" | ||||
| @@ -53,11 +79,11 @@ class _QueryParser(object): | |||||
| relation = Date.AFTER | relation = Date.AFTER | ||||
| dtstr = term.split(":", 1)[1] | dtstr = term.split(":", 1)[1] | ||||
| else: | else: | ||||
| raise QueryParseException('Bad relation for date: "%s"' % term) | |||||
| raise QueryParseException("Bad relation for date node: %r" % term) | |||||
| try: | try: | ||||
| dt = parse_date(dtstr) | dt = parse_date(dtstr) | ||||
| except (TypeError, ValueError): | except (TypeError, ValueError): | ||||
| raise QueryParseException('Bad datetime for date: "%s"' % dtstr) | |||||
| raise QueryParseException("Bad datetime for date node: %r" % dtstr) | |||||
| return Date(type_, relation, dt) | return Date(type_, relation, dt) | ||||
| def _parse_modified(self, term): | def _parse_modified(self, term): | ||||
| @@ -84,12 +110,6 @@ class _QueryParser(object): | |||||
| """Parse part of a query into a variable node and return it.""" | """Parse part of a query into a variable node and return it.""" | ||||
| return Symbol(Symbol.VARIABLE, self._parse_literal(term)) | return Symbol(Symbol.VARIABLE, self._parse_literal(term)) | ||||
| def _parse_literal(self, literal): | |||||
| """Parse part of a search query into a string or regular expression.""" | |||||
| if literal.startswith(("r:", "re:", "regex:", "regexp:")): | |||||
| return Regex(literal.split(":", 1)[1]) | |||||
| return String(literal) | |||||
| def _parse_term(self, term): | def _parse_term(self, term): | ||||
| """Parse a query term into a tree node and return it.""" | """Parse a query term into a tree node and return it.""" | ||||
| if ":" in term and not term[0] == ":": | if ":" in term and not term[0] == ":": | ||||