source: trunk/src/xmlpatterns/expr/qexpressionfactory.cpp

Last change on this file was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 18.9 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the QtXmlPatterns module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include <QBuffer>
43#include <QByteArray>
44
45#include "qcalltemplate_p.h"
46#include "qcommonsequencetypes_p.h"
47#include "qdebug_p.h"
48#include "qexpression_p.h"
49#include "qgenericstaticcontext_p.h"
50#include "qoperandsiterator_p.h"
51#include "qoptimizationpasses_p.h"
52#include "qparsercontext_p.h"
53#include "qpath_p.h"
54#include "qquerytransformparser_p.h"
55#include "qstaticfocuscontext_p.h"
56#include "qtokenrevealer_p.h"
57#include "qxquerytokenizer_p.h"
58#include "qxslttokenizer_p.h"
59
60#include "qexpressionfactory_p.h"
61
62QT_BEGIN_NAMESPACE
63
64namespace QPatternist {
65
66/**
67 * @short The entry point to the parser.
68 *
69 * @param info supplies the information the parser & scanner
70 * needs to create expressions. The created expression, if everything
71 * succeeds, can be retrieved via the object @p info points to.
72 * @returns non-negative if the parser fails.
73 * @see ExpressionFactory::createExpression()
74 */
75extern int XPathparse(QPatternist::ParserContext *const info);
76
77Expression::Ptr ExpressionFactory::createExpression(const QString &expr,
78 const StaticContext::Ptr &context,
79 const QXmlQuery::QueryLanguage lang,
80 const SequenceType::Ptr &requiredType,
81 const QUrl &queryURI,
82 const QXmlName &initialTemplateName)
83{
84 if(lang == QXmlQuery::XSLT20)
85 {
86 QByteArray query(expr.toUtf8());
87 QBuffer buffer(&query);
88 buffer.open(QIODevice::ReadOnly);
89
90 return createExpression(&buffer,
91 context,
92 lang,
93 requiredType,
94 queryURI,
95 initialTemplateName);
96 }
97 else
98 {
99 return createExpression(Tokenizer::Ptr(new XQueryTokenizer(expr, queryURI)),
100 context,
101 lang,
102 requiredType,
103 queryURI,
104 initialTemplateName);
105 }
106}
107
108Expression::Ptr ExpressionFactory::createExpression(QIODevice *const device,
109 const StaticContext::Ptr &context,
110 const QXmlQuery::QueryLanguage lang,
111 const SequenceType::Ptr &requiredType,
112 const QUrl &queryURI,
113 const QXmlName &initialTemplateName)
114{
115 Q_ASSERT(device);
116 Q_ASSERT(device->isReadable());
117
118 Tokenizer::Ptr tokenizer;
119
120 if(lang == QXmlQuery::XSLT20)
121 tokenizer = Tokenizer::Ptr(new XSLTTokenizer(device, queryURI, context, context->namePool()));
122 else
123 tokenizer = Tokenizer::Ptr(new XQueryTokenizer(QString::fromUtf8(device->readAll()), queryURI));
124
125 return createExpression(tokenizer, context, lang, requiredType, queryURI, initialTemplateName);
126}
127
128Expression::Ptr ExpressionFactory::createExpression(const Tokenizer::Ptr &tokenizer,
129 const StaticContext::Ptr &context,
130 const QXmlQuery::QueryLanguage lang,
131 const SequenceType::Ptr &requiredType,
132 const QUrl &queryURI,
133 const QXmlName &initialTemplateName)
134{
135 Q_ASSERT(context);
136 Q_ASSERT(requiredType);
137 Q_ASSERT(queryURI.isValid());
138
139 Tokenizer::Ptr effectiveTokenizer(tokenizer);
140#ifdef Patternist_DEBUG
141 effectiveTokenizer = Tokenizer::Ptr(new TokenRevealer(queryURI, tokenizer));
142#endif
143
144 OptimizationPasses::Coordinator::init();
145
146 const ParserContext::Ptr info(new ParserContext(context, lang, effectiveTokenizer.data()));
147 info->initialTemplateName = initialTemplateName;
148
149 effectiveTokenizer->setParserContext(info);
150
151 const int bisonRetval = XPathparse(info.data());
152
153 Q_ASSERT_X(bisonRetval == 0, Q_FUNC_INFO,
154 "We shouldn't be able to get an error, because we throw exceptions.");
155 Q_UNUSED(bisonRetval); /* Needed when not compiled in debug mode, since bisonRetval won't
156 * be used in the Q_ASSERT_X above. */
157
158 Expression::Ptr result(info->queryBody);
159
160 if(!result)
161 {
162 context->error(QtXmlPatterns::tr("A library module cannot be evaluated "
163 "directly. It must be imported from a "
164 "main module."),
165 ReportContext::XPST0003,
166 QSourceLocation(queryURI, 1, 1));
167 }
168
169 /* Optimization: I think many things are done in the wrong order below. We
170 * probably want everything typechecked before compressing, since we can
171 * have references all over the place(variable references, template
172 * invocations, function callsites). This could even be a source to bugs.
173 */
174
175 /* Here, we type check user declared functions and global variables. This
176 * means that variables and functions that are not used are type
177 * checked(which they otherwise wouldn't have been), and those which are
178 * used, are type-checked twice, unfortunately. */
179
180 const bool hasExternalFocus = context->contextItemType();
181
182 if(lang == QXmlQuery::XSLT20)
183 {
184 /* Bind xsl:call-template instructions to their template bodies.
185 *
186 * We do this before type checking and compressing them, because a
187 * CallTemplate obviously needs its template before being compressed.
188 *
189 * Also, we do this before type checking and compressing user
190 * functions, since they can contain template call sites.
191 */
192 for(int i = 0; i < info->templateCalls.count(); ++i)
193 {
194 CallTemplate *const site = info->templateCalls.at(i)->as<CallTemplate>();
195 const QXmlName targetName(site->name());
196 const Template::Ptr t(info->namedTemplates.value(targetName));
197
198 if(t)
199 site->setTemplate(t);
200 else
201 {
202 context->error(QtXmlPatterns::tr("No template by name %1 exists.").arg(formatKeyword(context->namePool(), targetName)),
203 ReportContext::XTSE0650,
204 site);
205 }
206 }
207 }
208
209 /* Type check and compress user functions. */
210 {
211 const UserFunction::List::const_iterator end(info->userFunctions.constEnd());
212 UserFunction::List::const_iterator it(info->userFunctions.constBegin());
213
214 /* If the query has a focus(which is common, in the case of a
215 * stylesheet), we must ensure that the focus isn't visible in the
216 * function body. */
217 StaticContext::Ptr effectiveContext;
218
219 if(hasExternalFocus)
220 {
221 effectiveContext = StaticContext::Ptr(new StaticFocusContext(ItemType::Ptr(),
222 context));
223 }
224 else
225 effectiveContext = context;
226
227 for(; it != end; ++it)
228 {
229 pDebug() << "----- User Function Typecheck -----";
230 registerLastPath((*it)->body());
231
232 /* We will most likely call body()->typeCheck() again, once for
233 * each callsite. That is, it will be called from
234 * UserFunctionCallsite::typeCheck(), which will be called
235 * indirectly when we check the query body. */
236 const Expression::Ptr typeCheck((*it)->body()->typeCheck(effectiveContext,
237 (*it)->signature()->returnType()));
238 /* We don't have to call (*it)->setBody(typeCheck) here since it's
239 * only used directly below. */
240 processTreePass(typeCheck, UserFunctionTypeCheck);
241 pDebug() << "------------------------------";
242
243 pDebug() << "----- User Function Compress -----";
244 const Expression::Ptr comp(typeCheck->compress(effectiveContext));
245 (*it)->setBody(comp);
246 processTreePass(comp, UserFunctionCompression);
247 pDebug() << "------------------------------";
248 }
249 }
250
251 /* Type check and compress global variables. */
252 {
253 const VariableDeclaration::Stack::const_iterator vend(info->variables.constEnd());
254 VariableDeclaration::Stack::const_iterator vit(info->variables.constBegin());
255 for(; vit != vend; ++vit)
256 {
257 Q_ASSERT(*vit);
258 /* This is a bit murky, the global variable will have it
259 * Expression::typeCheck() function called from all its references,
260 * but we also want to check it here globally, so we do
261 * typechecking using a proper focus. */
262 if((*vit)->type == VariableDeclaration::ExternalVariable)
263 continue;
264
265 pDebug() << "----- Global Variable Typecheck -----";
266 Q_ASSERT((*vit)->expression());
267 /* We supply ZeroOrMoreItems, meaning the variable can evaluate to anything. */
268 // FIXME which is a source to bugs
269 // TODO What about compressing variables?
270 const Expression::Ptr
271 nev((*vit)->expression()->typeCheck(context, CommonSequenceTypes::ZeroOrMoreItems));
272 processTreePass(nev, GlobalVariableTypeCheck);
273 pDebug() << "------------------------------";
274 }
275 }
276
277 /* Do all tests specific to XSL-T. */
278 if(lang == QXmlQuery::XSLT20)
279 {
280 /* Type check and compress named templates. */
281 {
282 pDebug() << "Have " << info->namedTemplates.count() << "named templates";
283
284 QMutableHashIterator<QXmlName, Template::Ptr> it(info->namedTemplates);
285
286 while(it.hasNext())
287 {
288 it.next();
289 processNamedTemplate(it.key(), it.value()->body, TemplateInitial);
290
291 it.value()->body = it.value()->body->typeCheck(context, CommonSequenceTypes::ZeroOrMoreItems);
292 processNamedTemplate(it.key(), it.value()->body, TemplateTypeCheck);
293
294 it.value()->body = it.value()->body->compress(context);
295 processNamedTemplate(it.key(), it.value()->body, TemplateCompress);
296
297 it.value()->compileParameters(context);
298 }
299 }
300
301 /* Type check and compress template rules. */
302 {
303 QHashIterator<QXmlName, TemplateMode::Ptr> it(info->templateRules);
304
305 /* Since a pattern can exist of AxisStep, its typeCheck() stage
306 * requires a focus. In the case that we're invoked with a name but
307 * no focus, this will yield a compile error, unless we declare a
308 * focus manually. This only needs to be done for the pattern
309 * expression, since the static type of the pattern is used as the
310 * static type for the focus of the template body. */
311 StaticContext::Ptr patternContext;
312 if(hasExternalFocus)
313 patternContext = context;
314 else
315 patternContext = StaticContext::Ptr(new StaticFocusContext(BuiltinTypes::node, context));
316
317 /* For each template pattern. */
318 while(it.hasNext())
319 {
320 it.next();
321 const TemplateMode::Ptr &mode = it.value();
322 const int len = mode->templatePatterns.count();
323 TemplatePattern::ID currentTemplateID = -1;
324 bool hasDoneItOnce = false;
325
326 /* For each template pattern. */
327 for(int i = 0; i < len; ++i)
328 {
329 /* We can't use references for these two members, since we
330 * assign to them. */
331 const TemplatePattern::Ptr &pattern = mode->templatePatterns.at(i);
332 Expression::Ptr matchPattern(pattern->matchPattern());
333
334 processTemplateRule(pattern->templateTarget()->body,
335 pattern, mode->name(), TemplateInitial);
336
337 matchPattern = matchPattern->typeCheck(patternContext, CommonSequenceTypes::ZeroOrMoreItems);
338 matchPattern = matchPattern->compress(patternContext);
339 pattern->setMatchPattern(matchPattern);
340
341 if(currentTemplateID == -1 && hasDoneItOnce)
342 {
343 currentTemplateID = pattern->id();
344 continue;
345 }
346 else if(currentTemplateID == pattern->id() && hasDoneItOnce)
347 {
348 hasDoneItOnce = false;
349 continue;
350 }
351
352 hasDoneItOnce = true;
353 currentTemplateID = pattern->id();
354 Expression::Ptr body(pattern->templateTarget()->body);
355
356 /* Patterns for a new template has started, we must
357 * deal with the body & parameters. */
358 {
359 /* TODO type is wrong, it has to be the union of all
360 * patterns. */
361 const StaticContext::Ptr focusContext(new StaticFocusContext(matchPattern->staticType()->itemType(),
362 context));
363 body = body->typeCheck(focusContext, CommonSequenceTypes::ZeroOrMoreItems);
364
365 pattern->templateTarget()->compileParameters(focusContext);
366 }
367
368 processTemplateRule(body, pattern, mode->name(), TemplateTypeCheck);
369
370 body = body->compress(context);
371
372 pattern->templateTarget()->body = body;
373 processTemplateRule(body, pattern, mode->name(), TemplateCompress);
374 }
375
376 mode->finalize();
377 }
378 }
379
380 /* Add templates in mode #all to all other modes.
381 *
382 * We do this after the templates has been typechecked and compressed,
383 * since otherwise it will be done N times for the built-in templates,
384 * where N is the count of different templates, instead of once. */
385 {
386 const QXmlName nameModeAll(QXmlName(StandardNamespaces::InternalXSLT,
387 StandardLocalNames::all));
388 const TemplateMode::Ptr &modeAll = info->templateRules[nameModeAll];
389
390 Q_ASSERT_X(modeAll, Q_FUNC_INFO,
391 "We should at least have the builtin templates.");
392 QHashIterator<QXmlName, TemplateMode::Ptr> it(info->templateRules);
393
394 while(it.hasNext())
395 {
396 it.next();
397
398 /* Don't add mode #all to mode #all. */
399 if(it.key() == nameModeAll)
400 continue;
401
402 it.value()->addMode(modeAll);
403 }
404 }
405 }
406
407 /* Type check and compress the query body. */
408 {
409 pDebug() << "----- Initial AST build. -----";
410 processTreePass(result, QueryBodyInitial);
411 pDebug() << "------------------------------";
412
413 pDebug() << "----- Type Check -----";
414 registerLastPath(result);
415 result->rewrite(result, result->typeCheck(context, requiredType), context);
416 processTreePass(result, QueryBodyTypeCheck);
417 pDebug() << "------------------------------";
418
419 pDebug() << "----- Compress -----";
420 result->rewrite(result, result->compress(context), context);
421 processTreePass(result, QueryBodyCompression);
422 pDebug() << "------------------------------";
423 }
424
425 return result;
426}
427
428void ExpressionFactory::registerLastPath(const Expression::Ptr &operand)
429{
430 OperandsIterator it(operand, OperandsIterator::IncludeParent);
431 Expression::Ptr next(it.next());
432
433 while(next)
434 {
435 if(next->is(Expression::IDPath))
436 {
437 next->as<Path>()->setLast();
438 next = it.skipOperands();
439 }
440 else
441 next = it.next();
442 }
443}
444
445void ExpressionFactory::processTreePass(const Expression::Ptr &,
446 const CompilationStage)
447{
448}
449
450void ExpressionFactory::processTemplateRule(const Expression::Ptr &body,
451 const TemplatePattern::Ptr &pattern,
452 const QXmlName &mode,
453 const TemplateCompilationStage stage)
454{
455 Q_UNUSED(body);
456 Q_UNUSED(pattern);
457 Q_UNUSED(mode);
458 Q_UNUSED(stage);
459}
460
461void ExpressionFactory::processNamedTemplate(const QXmlName &name,
462 const Expression::Ptr &tree,
463 const TemplateCompilationStage stage)
464{
465 Q_UNUSED(name);
466 Q_UNUSED(tree);
467 Q_UNUSED(stage);
468}
469
470} // namespace QPatternist
471
472QT_END_NAMESPACE
473
Note: See TracBrowser for help on using the repository browser.