1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtXmlPatterns module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qboolean_p.h"
41#include "qcommonvalues_p.h"
42#include "qemptysequence_p.h"
43#include "qliteral_p.h"
44#include "qliteralsequence_p.h"
45#include "qoperandsiterator_p.h"
46#include "qoptimizerframework_p.h"
47#include "qstaticfocuscontext_p.h"
48#include "qtypechecker_p.h"
49
50#include "qexpression_p.h"
51
52QT_BEGIN_NAMESPACE
53
54using namespace QPatternist;
55
56Expression::~Expression()
57{
58}
59
60StaticContext::Ptr Expression::finalizeStaticContext(const StaticContext::Ptr &context) const
61{
62 Q_ASSERT(context);
63 const ItemType::Ptr focusType(newFocusType());
64 Q_ASSERT(focusType);
65 return StaticContext::Ptr(new StaticFocusContext(focusType, context));
66}
67
68Expression::Ptr Expression::typeCheck(const StaticContext::Ptr &context,
69 const SequenceType::Ptr &reqType)
70{
71 Q_ASSERT(reqType);
72 typeCheckOperands(context);
73 return TypeChecker::applyFunctionConversion(operand: Expression::Ptr(this), reqType, context);
74}
75
76void Expression::typeCheckOperands(const StaticContext::Ptr &context)
77{
78 const Expression::List ops(operands());
79
80 /* Check if this expression has any operands at all. */
81 if(ops.isEmpty())
82 return; /* We're done, early exit. */
83
84 const SequenceType::List opTypes(expectedOperandTypes());
85 Expression::List result;
86
87 /* If we create a focus, we handle the last one specially, so avoid it in the loop. */
88 const bool createsFocus = has(prop: CreatesFocusForLast);
89 const SequenceType::List::const_iterator typeEnd(createsFocus ? --opTypes.constEnd()
90 : opTypes.constEnd());
91 const Expression::List::const_iterator end(createsFocus ? --ops.constEnd()
92 : ops.constEnd());
93
94 SequenceType::List::const_iterator reqType(opTypes.constBegin());
95 SequenceType::Ptr t(*reqType);
96 // TODO we assign twice to t here(also below in loop) when ops.size() > 1
97
98 Expression::List::const_iterator it(ops.constBegin());
99
100 for(; it != end; ++it)
101 {
102 /* This ensures that the last expectedOperandType stays, and is
103 * used for all other operands. This is used for expressions that
104 * have an infinite amount of operands, such as the concat() function. */
105 if(reqType != typeEnd)
106 {
107 t = *reqType;
108 ++reqType;
109 }
110
111 /* Let the child & its children typecheck. */
112 result.append(t: (*it)->typeCheck(context, reqType: t));
113 }
114
115 if(createsFocus)
116 {
117 const StaticContext::Ptr newContext(finalizeStaticContext(context));
118 result.append(t: ops.last()->typeCheck(context: newContext, reqType: opTypes.last()));
119 }
120
121 setOperands(result);
122}
123
124Expression::Ptr Expression::invokeOptimizers(const Expression::Ptr &expr,
125 const StaticContext::Ptr &context)
126{
127 Q_ASSERT(expr);
128
129 const OptimizationPass::List opts(expr->optimizationPasses());
130
131 if(opts.isEmpty()) /* Early exit. */
132 {
133 return expr;
134 }
135
136 const OptimizationPass::List::const_iterator passEnd(opts.constEnd());
137 OptimizationPass::List::const_iterator passIt(opts.constBegin());
138
139 for(; passIt != passEnd; ++passIt) /* Invoke each optimization pass. */
140 {
141 const OptimizationPass::Ptr pass(*passIt); /* Alias, for readability. */
142 OptimizationPass::ExpressionMarker sourceMarker(pass->sourceExpression);
143
144 if(pass->startIdentifier && !pass->startIdentifier->matches(expr))
145 {
146 /* This pass specified a start identifier and it did
147 * not match -- let's try the next OptimizationPass. */
148 continue;
149 }
150
151 ExpressionIdentifier::List::const_iterator idIt(pass->operandIdentifiers.constBegin());
152 const Expression::List ops(expr->operands());
153 const Expression::List::const_iterator opEnd(ops.constEnd());
154 Expression::List::const_iterator opIt(ops.constBegin());
155
156 switch(pass->operandsMatchMethod)
157 {
158 case OptimizationPass::Sequential:
159 {
160 for(; opIt != opEnd; ++opIt)
161 {
162 const Expression::Ptr operand(*opIt); /* Alias, for readability. */
163 const ExpressionIdentifier::Ptr opIdentifier(*idIt); /* Alias, for readability. */
164 if(opIdentifier && !opIdentifier->matches(expr: operand))
165 {
166 break;
167 }
168
169 ++idIt;
170 }
171
172 if(opIt == opEnd)
173 break; /* All operands matched, so this pass matched. */
174 else
175 {
176 /* The loop above did not finish which means all operands did not match.
177 Therefore, this OptimizationPass did not match -- let's try the next one. */
178 continue;
179 }
180 }
181 case OptimizationPass::AnyOrder:
182 {
183 Q_ASSERT_X(ops.count() == 2, Q_FUNC_INFO,
184 "AnyOrder is currently only supported for Expressions with two operands.");
185 if(pass->operandIdentifiers.first()->matches(expr: ops.first()) &&
186 pass->operandIdentifiers.last()->matches(expr: ops.last()))
187 {
188 break;
189 }
190 else if(pass->operandIdentifiers.first()->matches(expr: ops.last()) &&
191 pass->operandIdentifiers.last()->matches(expr: ops.first()))
192 {
193 sourceMarker.first() = 1;
194 sourceMarker[1] = 0;
195 break; /* This pass matched. */
196 }
197 else
198 continue; /* This pass didn't match, let's loop through the next pass. */
199 }
200 }
201
202 /* Figure out the source Expression, if any. */
203 Expression::List operands;
204 Expression::Ptr sourceExpr;
205
206 if(!sourceMarker.isEmpty())
207 {
208 const OptimizationPass::ExpressionMarker::const_iterator mEnd(sourceMarker.constEnd());
209 OptimizationPass::ExpressionMarker::const_iterator mIt(sourceMarker.constBegin());
210 sourceExpr = expr;
211
212 for(; mIt != mEnd; ++mIt)
213 {
214 Q_ASSERT(*mIt >= 0);
215 sourceExpr = sourceExpr->operands().at(i: *mIt);
216 }
217
218 operands.append(t: sourceExpr);
219 }
220
221 if(operands.isEmpty())
222 {
223 Q_ASSERT(pass->resultCreator);
224 return pass->resultCreator->create(operands: Expression::List(), context, expr.data())->compress(context);
225 }
226 else if(pass->resultCreator)
227 return pass->resultCreator->create(operands, context, expr.data())->compress(context);
228 else
229 {
230 return sourceExpr;
231 }
232 }
233
234 return expr;
235}
236
237Expression::Ptr Expression::compress(const StaticContext::Ptr &context)
238{
239 if(!compressOperands(context))
240 {
241 /* At least one of the operands cannot be evaluated at compile, so
242 * 'this' Expression cannot const fold. */
243 return invokeOptimizers(expr: Expression::Ptr(this), context);
244 }
245
246 Expression::Ptr retval;
247
248 if(hasDependency(prop: DisableElimination))
249 retval = Expression::Ptr(this);
250 else
251 retval = constantPropagate(context);
252
253 return invokeOptimizers(expr: retval, context);
254}
255
256Expression::Ptr Expression::constantPropagate(const StaticContext::Ptr &context) const
257{
258 Q_ASSERT(context);
259
260 /* Optimization: We rewrite literals to literals here, which is pointless.
261 * Maybe we should have a property which says "doesn't disable elimination
262 * but don't eliminate me." */
263 if(staticType()->cardinality().allowsMany())
264 {
265 Item::Iterator::Ptr it(evaluateSequence(context: context->dynamicContext()));
266 Item::List result;
267 Item item(it->next());
268
269 while(item)
270 {
271 result.append(t: item);
272 item = it->next();
273 }
274
275 switch(result.count())
276 {
277 case 0:
278 return EmptySequence::create(replacementFor: this, context);
279 case 1:
280 return rewrite(to: Expression::Ptr(new Literal(result.first())), context);
281 default:
282 return rewrite(to: Expression::Ptr(new LiteralSequence(result)), context);
283 }
284 }
285 else
286 {
287 const Item item(evaluateSingleton(context: context->dynamicContext()));
288
289 if(item)
290 return rewrite(to: Expression::Ptr(new Literal(item)), context);
291 else
292 return EmptySequence::create(replacementFor: this, context);
293 }
294}
295
296Item::Iterator::Ptr Expression::evaluateSequence(const DynamicContext::Ptr &context) const
297{
298 const Item item(evaluateSingleton(context));
299
300 if(item)
301 return makeSingletonIterator(item);
302 else
303 return CommonValues::emptyIterator;
304}
305
306Item Expression::evaluateSingleton(const DynamicContext::Ptr &context) const
307{
308 return Boolean::fromValue(value: evaluateEBV(context));
309}
310
311bool Expression::evaluateEBV(const DynamicContext::Ptr &context) const
312{
313 return Boolean::evaluateEBV(e: evaluateSequence(context), context);
314}
315
316void Expression::evaluateToSequenceReceiver(const DynamicContext::Ptr &context) const
317{
318 QAbstractXmlReceiver *const receiver = context->outputReceiver();
319 const Item::Iterator::Ptr it(evaluateSequence(context));
320 Item next(it->next());
321
322 while(next)
323 {
324 receiver->item(item: next);
325 next = it->next();
326 }
327}
328
329ItemType::Ptr Expression::expectedContextItemType() const
330{
331 Q_ASSERT_X(false, Q_FUNC_INFO,
332 "expectedContextItemType() must be overridden when RequiresContextItem is set.");
333 return ItemType::Ptr();
334}
335
336Expression::Properties Expression::properties() const
337{
338 return Properties();
339}
340
341Expression::Properties Expression::dependencies() const
342{
343 OperandsIterator it(Ptr(const_cast<Expression *>(this)), OperandsIterator::ExcludeParent);
344 Expression::Ptr next(it.next());
345
346 Properties dependencies(properties());
347
348 while(next)
349 {
350 dependencies |= next->dependencies();
351 next = it.next();
352 }
353
354 return dependencies & (Expression::RequiresFocus | Expression::IsEvaluated | Expression::DisableElimination);
355}
356
357void Expression::announceFocusType(const ItemType::Ptr &itemType)
358{
359 const Expression::List ops(operands());
360 const int len = ops.count();
361
362 for(int i = 0; i < len; ++i)
363 ops.at(i)->announceFocusType(itemType);
364}
365
366Expression::Properties Expression::deepProperties() const
367{
368 Properties props(properties());
369 const Expression::List ops(operands());
370 const int len = ops.count();
371
372 for(int i = 0; i < len; ++i)
373 props |= ops.at(i)->deepProperties();
374
375 return props;
376}
377
378Expression::ID Expression::id() const
379{
380 return IDIgnorableExpression;
381}
382
383OptimizationPass::List Expression::optimizationPasses() const
384{
385 return OptimizationPass::List();
386}
387
388ItemType::Ptr Expression::newFocusType() const
389{
390 Q_ASSERT_X(false, Q_FUNC_INFO,
391 "This function must be overridden when CreatesFocusForLast is set.");
392 return ItemType::Ptr();
393}
394
395const SourceLocationReflection *Expression::actualReflection() const
396{
397 return this;
398}
399
400QString Expression::description() const
401{
402 return QString::fromLatin1(str: "Expression, id: %1").arg(a: QString::number(id()));
403}
404
405PatternPriority Expression::patternPriority() const
406{
407 return 0.5;
408}
409
410QT_END_NAMESPACE
411

source code of qtxmlpatterns/src/xmlpatterns/expr/qexpression.cpp