1 /****************************************************************************
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
6 ** This file is part of the QtXmlPatterns module of the Qt Toolkit.
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 Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
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 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
40 ****************************************************************************/
45 #include "qcalltemplate_p.h"
46 #include "qcommonsequencetypes_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"
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"
60 #include "qexpressionfactory_p.h"
64 namespace QPatternist {
67 * @short The entry point to the parser.
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()
75 extern int XPathparse(QPatternist::ParserContext *const info);
77 Expression::Ptr ExpressionFactory::createExpression(const QString &expr,
78 const StaticContext::Ptr &context,
79 const QXmlQuery::QueryLanguage lang,
80 const SequenceType::Ptr &requiredType,
82 const QXmlName &initialTemplateName)
84 if(lang == QXmlQuery::XSLT20)
86 QByteArray query(expr.toUtf8());
87 QBuffer buffer(&query);
88 buffer.open(QIODevice::ReadOnly);
90 return createExpression(&buffer,
99 return createExpression(Tokenizer::Ptr(new XQueryTokenizer(expr, queryURI)),
104 initialTemplateName);
108 Expression::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)
116 Q_ASSERT(device->isReadable());
118 Tokenizer::Ptr tokenizer;
120 if(lang == QXmlQuery::XSLT20)
121 tokenizer = Tokenizer::Ptr(new XSLTTokenizer(device, queryURI, context, context->namePool()));
123 tokenizer = Tokenizer::Ptr(new XQueryTokenizer(QString::fromUtf8(device->readAll()), queryURI));
125 return createExpression(tokenizer, context, lang, requiredType, queryURI, initialTemplateName);
128 Expression::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)
136 Q_ASSERT(requiredType);
137 Q_ASSERT(queryURI.isValid());
139 Tokenizer::Ptr effectiveTokenizer(tokenizer);
140 #ifdef Patternist_DEBUG
141 effectiveTokenizer = Tokenizer::Ptr(new TokenRevealer(queryURI, tokenizer));
144 OptimizationPasses::Coordinator::init();
146 const ParserContext::Ptr info(new ParserContext(context, lang, effectiveTokenizer.data()));
147 info->initialTemplateName = initialTemplateName;
149 effectiveTokenizer->setParserContext(info);
151 const int bisonRetval = XPathparse(info.data());
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. */
158 Expression::Ptr result(info->queryBody);
162 context->error(QtXmlPatterns::tr("A library module cannot be evaluated "
163 "directly. It must be imported from a "
165 ReportContext::XPST0003,
166 QSourceLocation(queryURI, 1, 1));
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.
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. */
180 const bool hasExternalFocus = context->contextItemType();
182 if(lang == QXmlQuery::XSLT20)
184 /* Bind xsl:call-template instructions to their template bodies.
186 * We do this before type checking and compressing them, because a
187 * CallTemplate obviously needs its template before being compressed.
189 * Also, we do this before type checking and compressing user
190 * functions, since they can contain template call sites.
192 for(int i = 0; i < info->templateCalls.count(); ++i)
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));
199 site->setTemplate(t);
202 context->error(QtXmlPatterns::tr("No template by name %1 exists.").arg(formatKeyword(context->namePool(), targetName)),
203 ReportContext::XTSE0650,
209 /* Type check and compress user functions. */
211 const UserFunction::List::const_iterator end(info->userFunctions.constEnd());
212 UserFunction::List::const_iterator it(info->userFunctions.constBegin());
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
217 StaticContext::Ptr effectiveContext;
221 effectiveContext = StaticContext::Ptr(new StaticFocusContext(ItemType::Ptr(),
225 effectiveContext = context;
227 for(; it != end; ++it)
229 pDebug() << "----- User Function Typecheck -----";
230 registerLastPath((*it)->body());
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() << "------------------------------";
243 pDebug() << "----- User Function Compress -----";
244 const Expression::Ptr comp(typeCheck->compress(effectiveContext));
245 (*it)->setBody(comp);
246 processTreePass(comp, UserFunctionCompression);
247 pDebug() << "------------------------------";
251 /* Type check and compress global variables. */
253 const VariableDeclaration::Stack::const_iterator vend(info->variables.constEnd());
254 VariableDeclaration::Stack::const_iterator vit(info->variables.constBegin());
255 for(; vit != vend; ++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)
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() << "------------------------------";
277 /* Do all tests specific to XSL-T. */
278 if(lang == QXmlQuery::XSLT20)
280 /* Type check and compress named templates. */
282 pDebug() << "Have " << info->namedTemplates.count() << "named templates";
284 QMutableHashIterator<QXmlName, Template::Ptr> it(info->namedTemplates);
289 processNamedTemplate(it.key(), it.value()->body, TemplateInitial);
291 it.value()->body = it.value()->body->typeCheck(context, CommonSequenceTypes::ZeroOrMoreItems);
292 processNamedTemplate(it.key(), it.value()->body, TemplateTypeCheck);
294 it.value()->body = it.value()->body->compress(context);
295 processNamedTemplate(it.key(), it.value()->body, TemplateCompress);
297 it.value()->compileParameters(context);
301 /* Type check and compress template rules. */
303 QHashIterator<QXmlName, TemplateMode::Ptr> it(info->templateRules);
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;
313 patternContext = context;
315 patternContext = StaticContext::Ptr(new StaticFocusContext(BuiltinTypes::node, context));
317 /* For each template pattern. */
321 const TemplateMode::Ptr &mode = it.value();
322 const int len = mode->templatePatterns.count();
323 TemplatePattern::ID currentTemplateID = -1;
324 bool hasDoneItOnce = false;
326 /* For each template pattern. */
327 for(int i = 0; i < len; ++i)
329 /* We can't use references for these two members, since we
331 const TemplatePattern::Ptr &pattern = mode->templatePatterns.at(i);
332 Expression::Ptr matchPattern(pattern->matchPattern());
334 processTemplateRule(pattern->templateTarget()->body,
335 pattern, mode->name(), TemplateInitial);
337 matchPattern = matchPattern->typeCheck(patternContext, CommonSequenceTypes::ZeroOrMoreItems);
338 matchPattern = matchPattern->compress(patternContext);
339 pattern->setMatchPattern(matchPattern);
341 if(currentTemplateID == -1 && hasDoneItOnce)
343 currentTemplateID = pattern->id();
346 else if(currentTemplateID == pattern->id() && hasDoneItOnce)
348 hasDoneItOnce = false;
352 hasDoneItOnce = true;
353 currentTemplateID = pattern->id();
354 Expression::Ptr body(pattern->templateTarget()->body);
356 /* Patterns for a new template has started, we must
357 * deal with the body & parameters. */
359 /* TODO type is wrong, it has to be the union of all
361 const StaticContext::Ptr focusContext(new StaticFocusContext(matchPattern->staticType()->itemType(),
363 body = body->typeCheck(focusContext, CommonSequenceTypes::ZeroOrMoreItems);
365 pattern->templateTarget()->compileParameters(focusContext);
368 processTemplateRule(body, pattern, mode->name(), TemplateTypeCheck);
370 body = body->compress(context);
372 pattern->templateTarget()->body = body;
373 processTemplateRule(body, pattern, mode->name(), TemplateCompress);
380 /* Add templates in mode #all to all other modes.
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. */
386 const QXmlName nameModeAll(QXmlName(StandardNamespaces::InternalXSLT,
387 StandardLocalNames::all));
388 const TemplateMode::Ptr &modeAll = info->templateRules[nameModeAll];
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);
398 /* Don't add mode #all to mode #all. */
399 if(it.key() == nameModeAll)
402 it.value()->addMode(modeAll);
407 /* Type check and compress the query body. */
409 pDebug() << "----- Initial AST build. -----";
410 processTreePass(result, QueryBodyInitial);
411 pDebug() << "------------------------------";
413 pDebug() << "----- Type Check -----";
414 registerLastPath(result);
415 result->rewrite(result, result->typeCheck(context, requiredType), context);
416 processTreePass(result, QueryBodyTypeCheck);
417 pDebug() << "------------------------------";
419 pDebug() << "----- Compress -----";
420 result->rewrite(result, result->compress(context), context);
421 processTreePass(result, QueryBodyCompression);
422 pDebug() << "------------------------------";
428 void ExpressionFactory::registerLastPath(const Expression::Ptr &operand)
430 OperandsIterator it(operand, OperandsIterator::IncludeParent);
431 Expression::Ptr next(it.next());
435 if(next->is(Expression::IDPath))
437 next->as<Path>()->setLast();
438 next = it.skipOperands();
445 void ExpressionFactory::processTreePass(const Expression::Ptr &,
446 const CompilationStage)
450 void ExpressionFactory::processTemplateRule(const Expression::Ptr &body,
451 const TemplatePattern::Ptr &pattern,
452 const QXmlName &mode,
453 const TemplateCompilationStage stage)
461 void ExpressionFactory::processNamedTemplate(const QXmlName &name,
462 const Expression::Ptr &tree,
463 const TemplateCompilationStage stage)
470 } // namespace QPatternist