Baloo

advancedqueryparser.cpp
1/*
2 SPDX-FileCopyrightText: 2014-2015 Vishesh Handa <vhanda@kde.org>
3 SPDX-FileCopyrightText: 2014 Denis Steckelmacher <steckdenis@yahoo.fr>
4
5 SPDX-License-Identifier: LGPL-2.1-or-later
6*/
7
8#include "advancedqueryparser.h"
9
10#include <QStringList>
11#include <QStack>
12#include <QDate>
13
14using namespace Baloo;
15
16AdvancedQueryParser::AdvancedQueryParser()
17{
18}
19
20static QStringList lex(const QString& text)
21{
22 QStringList tokenList;
23 QString token;
24 bool inQuotes = false;
25
26 for (int i = 0, end = text.size(); i != end; ++i) {
27 QChar c = text.at(i);
28
29 if (c == QLatin1Char('"')) {
30 // Quotes start or end string literals
31 if (inQuotes) {
32 tokenList.append(token);
33 token.clear();
34 }
35 inQuotes = !inQuotes;
36 } else if (inQuotes) {
37 // Don't do any processing in strings
38 token.append(c);
39 } else if (c.isSpace()) {
40 // Spaces end tokens
41 if (!token.isEmpty()) {
42 tokenList.append(token);
43 token.clear();
44 }
45 } else if (c == QLatin1Char('(') || c == QLatin1Char(')')) {
46 // Parentheses end tokens, and are tokens by themselves
47 if (!token.isEmpty()) {
48 tokenList.append(token);
49 token.clear();
50 }
51 tokenList.append(c);
52 } else if (c == QLatin1Char('>') || c == QLatin1Char('<') || c == QLatin1Char(':') || c == QLatin1Char('=')) {
53 // Operators end tokens
54 if (!token.isEmpty()) {
55 tokenList.append(token);
56 token.clear();
57 }
58 // accept '=' after any of the above
59 if (((i + 1) < end) && (text.at(i + 1) == QLatin1Char('='))) {
60 tokenList.append(text.mid(i, 2));
61 i++;
62 } else {
63 tokenList.append(c);
64 }
65 } else {
66 // Simply extend the current token
67 token.append(c);
68 }
69 }
70
71 if (!token.isEmpty()) {
72 tokenList.append(token);
73 }
74
75 return tokenList;
76}
77
78static void addTermToStack(QStack<Term>& stack, const Term& termInConstruction, Term::Operation op)
79{
80 Term &tos = stack.top();
81
82 tos = Term(tos, op, termInConstruction);
83}
84
85Term AdvancedQueryParser::parse(const QString& text)
86{
87 // The parser does not do any look-ahead but has to store some state
88 QStack<Term> stack;
90 Term termInConstruction;
91 bool valueExpected = false;
92
93 stack.push(Term());
94 ops.push(Term::And);
95
96 // Lex the input string
97 QStringList tokens = lex(text);
98 for (const QString &token : tokens) {
99 // If a key and an operator have been parsed, now is time for a value
100 if (valueExpected) {
101 // When the parser encounters a literal, it puts it in the value of
102 // termInConstruction so that "foo bar baz" is parsed as expected.
103 auto property = termInConstruction.value().toString();
104 if (property.isEmpty()) {
105 qDebug() << "Binary operator without first argument encountered:" << text;
106 return Term();
107 }
108 termInConstruction.setProperty(property);
109
110 termInConstruction.setValue(token);
111 valueExpected = false;
112 continue;
113 }
114
115 // Handle the logic operators
116 if (token == QLatin1String("AND")) {
117 if (!termInConstruction.isEmpty()) {
118 addTermToStack(stack, termInConstruction, ops.top());
119 termInConstruction = Term();
120 }
121 ops.top() = Term::And;
122 continue;
123 } else if (token == QLatin1String("OR")) {
124 if (!termInConstruction.isEmpty()) {
125 addTermToStack(stack, termInConstruction, ops.top());
126 termInConstruction = Term();
127 }
128 ops.top() = Term::Or;
129 continue;
130 }
131
132 // Handle the different comparators (and braces)
133 Term::Comparator comparator = Term::Auto;
134
135 switch (token.isEmpty() ? '\0' : token.at(0).toLatin1()) {
136 case ':':
137 comparator = Term::Contains;
138 break;
139 case '=':
140 comparator = Term::Equal;
141 break;
142 case '<': {
143 if (token.size() == 1) {
144 comparator = Term::Less;
145 } else if (token[1] == QLatin1Char('=')) {
146 comparator = Term::LessEqual;
147 }
148 break;
149 }
150 case '>': {
151 if (token.size() == 1) {
152 comparator = Term::Greater;
153 } else if (token[1] == QLatin1Char('=')) {
154 comparator = Term::GreaterEqual;
155 }
156 break;
157 }
158 case '(':
159 if (!termInConstruction.isEmpty()) {
160 addTermToStack(stack, termInConstruction, ops.top());
161 ops.top() = Term::And;
162 }
163
164 stack.push(Term());
165 ops.push(Term::And);
166 termInConstruction = Term();
167
168 continue;
169 case ')':
170 // Prevent a stack underflow if the user writes "a b ))))"
171 if (stack.size() > 1) {
172 // Don't forget the term just before the closing brace
173 if (termInConstruction.value().isValid()) {
174 addTermToStack(stack, termInConstruction, ops.top());
175 }
176
177 // stack.pop() is the term that has just been closed. Append
178 // it to the term just above it.
179 ops.pop();
180 addTermToStack(stack, stack.pop(), ops.top());
181 ops.top() = Term::And;
182 termInConstruction = Term();
183 }
184
185 continue;
186 default:
187 break;
188 }
189
190 if (comparator != Term::Auto) {
191 // Set the comparator of the term in construction and expect a value
192 termInConstruction.setComparator(comparator);
193 valueExpected = true;
194 } else {
195 // A new term will be started, so termInConstruction has to be appended
196 // to the top-level subterm list.
197 if (!termInConstruction.isEmpty()) {
198 addTermToStack(stack, termInConstruction, ops.top());
199 ops.top() = Term::And;
200 }
201
202 termInConstruction = Term(QString(), token);
203 }
204 }
205
206 if (valueExpected) {
207 termInConstruction.setProperty(termInConstruction.value().toString());
208 termInConstruction.setValue(QString());
209 termInConstruction.setComparator(Term::Contains);
210 }
211
212 if (termInConstruction.value().isValid()) {
213 addTermToStack(stack, termInConstruction, ops.top());
214 }
215
216 // Process unclosed parentheses
217 ops.pop();
218 while (stack.size() > 1) {
219 // stack.pop() is the term that has to be closed. Append
220 // it to the term just above it.
221 addTermToStack(stack, stack.pop(), ops.top());
222 }
223
224 return stack.top();
225}
Implements storage for docIds without any associated data Instantiated for:
Definition coding.cpp:11
const QList< QKeySequence > & end()
bool isSpace(char32_t ucs4)
void append(QList< T > &&value)
qsizetype size() const const
void push(const T &t)
T & top()
QString & append(QChar ch)
const QChar at(qsizetype position) const const
void clear()
bool isEmpty() const const
QString mid(qsizetype position, qsizetype n) const const
qsizetype size() const const
bool isValid() const const
QString toString() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Jul 12 2024 12:03:48 by doxygen 1.11.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.