blob: b0e43be7889e077f2e825fd36e9dac5a022596ac [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtXmlPatterns module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QBuffer>
#include <QByteArray>
#include "qcalltemplate_p.h"
#include "qcommonsequencetypes_p.h"
#include "qxmldebug_p.h"
#include "qexpression_p.h"
#include "qgenericstaticcontext_p.h"
#include "qoperandsiterator_p.h"
#include "qoptimizationpasses_p.h"
#include "qparsercontext_p.h"
#include "qpath_p.h"
#include "qquerytransformparser_p.h"
#include "qstaticfocuscontext_p.h"
#include "qtokenrevealer_p.h"
#include "qxquerytokenizer_p.h"
#include "qxslttokenizer_p.h"
#include "qexpressionfactory_p.h"
QT_BEGIN_NAMESPACE
namespace QPatternist {
/**
* @short The entry point to the parser.
*
* @param info supplies the information the parser & scanner
* needs to create expressions. The created expression, if everything
* succeeds, can be retrieved via the object @p info points to.
* @returns non-negative if the parser fails.
* @see ExpressionFactory::createExpression()
*/
extern int XPathparse(QPatternist::ParserContext *const info);
Expression::Ptr ExpressionFactory::createExpression(const QString &expr,
const StaticContext::Ptr &context,
const QXmlQuery::QueryLanguage lang,
const SequenceType::Ptr &requiredType,
const QUrl &queryURI,
const QXmlName &initialTemplateName)
{
if(lang == QXmlQuery::XSLT20)
{
QByteArray query(expr.toUtf8());
QBuffer buffer(&query);
buffer.open(QIODevice::ReadOnly);
return createExpression(&buffer,
context,
lang,
requiredType,
queryURI,
initialTemplateName);
}
else
{
return createExpression(Tokenizer::Ptr(new XQueryTokenizer(expr, queryURI)),
context,
lang,
requiredType,
queryURI,
initialTemplateName);
}
}
Expression::Ptr ExpressionFactory::createExpression(QIODevice *const device,
const StaticContext::Ptr &context,
const QXmlQuery::QueryLanguage lang,
const SequenceType::Ptr &requiredType,
const QUrl &queryURI,
const QXmlName &initialTemplateName)
{
Q_ASSERT(device);
Q_ASSERT(device->isReadable());
Tokenizer::Ptr tokenizer;
if(lang == QXmlQuery::XSLT20)
tokenizer = Tokenizer::Ptr(new XSLTTokenizer(device, queryURI, context, context->namePool()));
else
tokenizer = Tokenizer::Ptr(new XQueryTokenizer(QString::fromUtf8(device->readAll()), queryURI));
return createExpression(tokenizer, context, lang, requiredType, queryURI, initialTemplateName);
}
Expression::Ptr ExpressionFactory::createExpression(const Tokenizer::Ptr &tokenizer,
const StaticContext::Ptr &context,
const QXmlQuery::QueryLanguage lang,
const SequenceType::Ptr &requiredType,
const QUrl &queryURI,
const QXmlName &initialTemplateName)
{
Q_ASSERT(context);
Q_ASSERT(requiredType);
Q_ASSERT(queryURI.isValid());
Tokenizer::Ptr effectiveTokenizer(tokenizer);
#ifdef Patternist_DEBUG
effectiveTokenizer = Tokenizer::Ptr(new TokenRevealer(queryURI, tokenizer));
#endif
OptimizationPasses::Coordinator::init();
const ParserContext::Ptr info(new ParserContext(context, lang, effectiveTokenizer.data()));
info->initialTemplateName = initialTemplateName;
effectiveTokenizer->setParserContext(info);
const int bisonRetval = QPatternist::XPathparse(info.data());
Q_ASSERT_X(bisonRetval == 0, Q_FUNC_INFO,
"We shouldn't be able to get an error, because we throw exceptions.");
Q_UNUSED(bisonRetval); /* Needed when not compiled in debug mode, since bisonRetval won't
* be used in the Q_ASSERT_X above. */
Expression::Ptr result(info->queryBody);
if(!result)
{
context->error(QtXmlPatterns::tr("A library module cannot be evaluated "
"directly. It must be imported from a "
"main module."),
ReportContext::XPST0003,
QSourceLocation(queryURI, 1, 1));
}
/* Optimization: I think many things are done in the wrong order below. We
* probably want everything typechecked before compressing, since we can
* have references all over the place(variable references, template
* invocations, function callsites). This could even be a source to bugs.
*/
/* Here, we type check user declared functions and global variables. This
* means that variables and functions that are not used are type
* checked(which they otherwise wouldn't have been), and those which are
* used, are type-checked twice, unfortunately. */
const bool hasExternalFocus = context->contextItemType();
if(lang == QXmlQuery::XSLT20)
{
/* Bind xsl:call-template instructions to their template bodies.
*
* We do this before type checking and compressing them, because a
* CallTemplate obviously needs its template before being compressed.
*
* Also, we do this before type checking and compressing user
* functions, since they can contain template call sites.
*/
for(int i = 0; i < info->templateCalls.count(); ++i)
{
CallTemplate *const site = info->templateCalls.at(i)->as<CallTemplate>();
const QXmlName targetName(site->name());
const Template::Ptr t(info->namedTemplates.value(targetName));
if(t)
site->setTemplate(t);
else
{
context->error(QtXmlPatterns::tr("No template by name %1 exists.").arg(formatKeyword(context->namePool(), targetName)),
ReportContext::XTSE0650,
site);
}
}
}
/* Type check and compress user functions. */
{
const UserFunction::List::const_iterator end(info->userFunctions.constEnd());
UserFunction::List::const_iterator it(info->userFunctions.constBegin());
/* If the query has a focus(which is common, in the case of a
* stylesheet), we must ensure that the focus isn't visible in the
* function body. */
StaticContext::Ptr effectiveContext;
if(hasExternalFocus)
{
effectiveContext = StaticContext::Ptr(new StaticFocusContext(ItemType::Ptr(),
context));
}
else
effectiveContext = context;
for(; it != end; ++it)
{
pDebug() << "----- User Function Typecheck -----";
registerLastPath((*it)->body());
/* We will most likely call body()->typeCheck() again, once for
* each callsite. That is, it will be called from
* UserFunctionCallsite::typeCheck(), which will be called
* indirectly when we check the query body. */
const Expression::Ptr typeCheck((*it)->body()->typeCheck(effectiveContext,
(*it)->signature()->returnType()));
/* We don't have to call (*it)->setBody(typeCheck) here since it's
* only used directly below. */
processTreePass(typeCheck, UserFunctionTypeCheck);
pDebug() << "------------------------------";
pDebug() << "----- User Function Compress -----";
const Expression::Ptr comp(typeCheck->compress(effectiveContext));
(*it)->setBody(comp);
processTreePass(comp, UserFunctionCompression);
pDebug() << "------------------------------";
}
}
/* Type check and compress global variables. */
{
const VariableDeclaration::Stack::const_iterator vend(info->variables.constEnd());
VariableDeclaration::Stack::const_iterator vit(info->variables.constBegin());
for(; vit != vend; ++vit)
{
Q_ASSERT(*vit);
/* This is a bit murky, the global variable will have it
* Expression::typeCheck() function called from all its references,
* but we also want to check it here globally, so we do
* typechecking using a proper focus. */
if((*vit)->type == VariableDeclaration::ExternalVariable)
continue;
pDebug() << "----- Global Variable Typecheck -----";
Q_ASSERT((*vit)->expression());
/* We supply ZeroOrMoreItems, meaning the variable can evaluate to anything. */
// FIXME which is a source to bugs
// TODO What about compressing variables?
const Expression::Ptr
nev((*vit)->expression()->typeCheck(context, CommonSequenceTypes::ZeroOrMoreItems));
processTreePass(nev, GlobalVariableTypeCheck);
pDebug() << "------------------------------";
}
}
/* Do all tests specific to XSL-T. */
if(lang == QXmlQuery::XSLT20)
{
/* Type check and compress named templates. */
{
pDebug() << "Have " << info->namedTemplates.count() << "named templates";
for (auto it = info->namedTemplates.cbegin(), end = info->namedTemplates.cend(); it != end; ++it) {
processNamedTemplate(it.key(), it.value()->body, TemplateInitial);
it.value()->body = it.value()->body->typeCheck(context, CommonSequenceTypes::ZeroOrMoreItems);
processNamedTemplate(it.key(), it.value()->body, TemplateTypeCheck);
it.value()->body = it.value()->body->compress(context);
processNamedTemplate(it.key(), it.value()->body, TemplateCompress);
it.value()->compileParameters(context);
}
}
/* Type check and compress template rules. */
{
/* Since a pattern can exist of AxisStep, its typeCheck() stage
* requires a focus. In the case that we're invoked with a name but
* no focus, this will yield a compile error, unless we declare a
* focus manually. This only needs to be done for the pattern
* expression, since the static type of the pattern is used as the
* static type for the focus of the template body. */
StaticContext::Ptr patternContext;
if(hasExternalFocus)
patternContext = context;
else
patternContext = StaticContext::Ptr(new StaticFocusContext(BuiltinTypes::node, context));
/* For each template pattern. */
for (auto it = info->templateRules.cbegin(), end = info->templateRules.cend(); it != end; ++it) {
const TemplateMode::Ptr &mode = it.value();
const int len = mode->templatePatterns.count();
TemplatePattern::ID currentTemplateID = -1;
bool hasDoneItOnce = false;
/* For each template pattern. */
for(int i = 0; i < len; ++i)
{
/* We can't use references for these two members, since we
* assign to them. */
const TemplatePattern::Ptr &pattern = mode->templatePatterns.at(i);
Expression::Ptr matchPattern(pattern->matchPattern());
processTemplateRule(pattern->templateTarget()->body,
pattern, mode->name(), TemplateInitial);
matchPattern = matchPattern->typeCheck(patternContext, CommonSequenceTypes::ZeroOrMoreItems);
matchPattern = matchPattern->compress(patternContext);
pattern->setMatchPattern(matchPattern);
if(currentTemplateID == -1 && hasDoneItOnce)
{
currentTemplateID = pattern->id();
continue;
}
else if(currentTemplateID == pattern->id() && hasDoneItOnce)
{
hasDoneItOnce = false;
continue;
}
hasDoneItOnce = true;
currentTemplateID = pattern->id();
Expression::Ptr body(pattern->templateTarget()->body);
/* Patterns for a new template has started, we must
* deal with the body & parameters. */
{
/* TODO type is wrong, it has to be the union of all
* patterns. */
const StaticContext::Ptr focusContext(new StaticFocusContext(matchPattern->staticType()->itemType(),
context));
body = body->typeCheck(focusContext, CommonSequenceTypes::ZeroOrMoreItems);
pattern->templateTarget()->compileParameters(focusContext);
}
processTemplateRule(body, pattern, mode->name(), TemplateTypeCheck);
body = body->compress(context);
pattern->templateTarget()->body = body;
processTemplateRule(body, pattern, mode->name(), TemplateCompress);
}
mode->finalize();
}
}
/* Add templates in mode #all to all other modes.
*
* We do this after the templates has been typechecked and compressed,
* since otherwise it will be done N times for the built-in templates,
* where N is the count of different templates, instead of once. */
{
const QXmlName nameModeAll(QXmlName(StandardNamespaces::InternalXSLT,
StandardLocalNames::all));
const TemplateMode::Ptr &modeAll = info->templateRules[nameModeAll];
Q_ASSERT_X(modeAll, Q_FUNC_INFO,
"We should at least have the builtin templates.");
for (auto it = info->templateRules.cbegin(), end = info->templateRules.cend(); it != end; ++it) {
/* Don't add mode #all to mode #all. */
if(it.key() == nameModeAll)
continue;
it.value()->addMode(modeAll);
}
}
}
/* Type check and compress the query body. */
{
pDebug() << "----- Initial AST build. -----";
processTreePass(result, QueryBodyInitial);
pDebug() << "------------------------------";
pDebug() << "----- Type Check -----";
registerLastPath(result);
result->rewrite(result, result->typeCheck(context, requiredType), context);
processTreePass(result, QueryBodyTypeCheck);
pDebug() << "------------------------------";
pDebug() << "----- Compress -----";
result->rewrite(result, result->compress(context), context);
processTreePass(result, QueryBodyCompression);
pDebug() << "------------------------------";
}
return result;
}
void ExpressionFactory::registerLastPath(const Expression::Ptr &operand)
{
OperandsIterator it(operand, OperandsIterator::IncludeParent);
Expression::Ptr next(it.next());
while(next)
{
if(next->is(Expression::IDPath))
{
next->as<Path>()->setLast();
next = it.skipOperands();
}
else
next = it.next();
}
}
void ExpressionFactory::processTreePass(const Expression::Ptr &,
const CompilationStage)
{
}
void ExpressionFactory::processTemplateRule(const Expression::Ptr &body,
const TemplatePattern::Ptr &pattern,
const QXmlName &mode,
const TemplateCompilationStage stage)
{
Q_UNUSED(body);
Q_UNUSED(pattern);
Q_UNUSED(mode);
Q_UNUSED(stage);
}
void ExpressionFactory::processNamedTemplate(const QXmlName &name,
const Expression::Ptr &tree,
const TemplateCompilationStage stage)
{
Q_UNUSED(name);
Q_UNUSED(tree);
Q_UNUSED(stage);
}
} // namespace QPatternist
QT_END_NAMESPACE