| /**************************************************************************** |
| ** |
| ** 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 |
| |