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

Last change on this file since 117 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

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