/**************************************************************************** ** ** 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$ ** ****************************************************************************/ // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. %{ /**************************************************************************** ** ** 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$ ** ****************************************************************************/ // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * The cpp generated with bison 2.1 wants to * redeclare the C-like prototypes of 'malloc' and 'free', so we avoid that. */ #define YYMALLOC malloc #define YYFREE free QT_BEGIN_NAMESPACE /* Due to Qt's QT_BEGIN_NAMESPACE magic, we can't use `using namespace', for some * undocumented reason. */ namespace QPatternist { /** * "Macro that you define with #define in the Bison declarations * section to request verbose, specific error message strings when * yyerror is called." */ #define YYERROR_VERBOSE 1 #define YYLTYPE_IS_TRIVIAL 0 #define YYINITDEPTH 1 #define yyoverflow parseInfo->handleStackOverflow /* Suppresses `warning: "YYENABLE_NLS" is not defined` * @c YYENABLE_NLS enables Bison internationalization, and we don't * use that, so disable it. See the Bison Manual, section 4.5 Parser Internationalization. */ #define YYENABLE_NLS 0 static inline QSourceLocation fromYYLTYPE(const YYLTYPE &sourceLocator, const ParserContext *const parseInfo) { return QSourceLocation(parseInfo->tokenizer->queryURI(), sourceLocator.first_line, sourceLocator.first_column); } /** * @internal * @relates QXmlQuery */ typedef QFlags QueryLanguages; /** * @short Flags invalid expressions and declarations in the currently * parsed language. * * Since this grammar is used for several languages: XQuery 1.0, XSL-T 2.0, and * XPath 2.0 inside XSL-T, and field and selector patterns in W3C XML Schema's * identity constraints, it is the union of all the constructs in these * languages. However, when dealing with each language individually, we * regularly need to disallow some expressions, such as direct element * constructors when parsing XSL-T, or the typeswitch when parsing XPath. * * This is further complicated by that XSLTTokenizer sometimes generates code * which is allowed in XQuery but not in XPath. For that reason the token * INTERNAL is sometimes generated, which signals that an expression, for * instance the @c let clause, should not be flagged as an error, because it's * used for internal purposes. * * Hence, this function is called from each expression and declaration with @p * allowedLanguages stating what languages it is allowed in. * * If @p isInternal is @c true, no error is raised. Otherwise, if the current * language is not in @p allowedLanguages, an error is raised. */ static void allowedIn(const QueryLanguages allowedLanguages, const ParserContext *const parseInfo, const YYLTYPE &sourceLocator, const bool isInternal = false) { /* We treat XPath 2.0 as a subset of XSL-T 2.0, so if XPath 2.0 is allowed * and XSL-T is the language, it's ok. */ if(!isInternal && (!allowedLanguages.testFlag(parseInfo->languageAccent) && !(allowedLanguages.testFlag(QXmlQuery::XPath20) && parseInfo->languageAccent == QXmlQuery::XSLT20))) { QString langName; switch(parseInfo->languageAccent) { case QXmlQuery::XPath20: langName = QLatin1String("XPath 2.0"); break; case QXmlQuery::XSLT20: langName = QLatin1String("XSL-T 2.0"); break; case QXmlQuery::XQuery10: langName = QLatin1String("XQuery 1.0"); break; case QXmlQuery::XmlSchema11IdentityConstraintSelector: langName = QtXmlPatterns::tr("W3C XML Schema identity constraint selector"); break; case QXmlQuery::XmlSchema11IdentityConstraintField: langName = QtXmlPatterns::tr("W3C XML Schema identity constraint field"); break; } parseInfo->staticContext->error(QtXmlPatterns::tr("A construct was encountered " "which is disallowed in the current language(%1).").arg(langName), ReportContext::XPST0003, fromYYLTYPE(sourceLocator, parseInfo)); } } static inline bool isVariableReference(const Expression::ID id) { return id == Expression::IDExpressionVariableReference || id == Expression::IDRangeVariableReference || id == Expression::IDArgumentReference; } class ReflectYYLTYPE : public SourceLocationReflection { public: inline ReflectYYLTYPE(const YYLTYPE &sourceLocator, const ParserContext *const pi) : m_sl(sourceLocator) , m_parseInfo(pi) { } virtual const SourceLocationReflection *actualReflection() const { return this; } virtual QSourceLocation sourceLocation() const { return fromYYLTYPE(m_sl, m_parseInfo); } virtual QString description() const { Q_ASSERT(false); return QString(); } private: const YYLTYPE &m_sl; const ParserContext *const m_parseInfo; }; /** * @short Centralizes a translation string for the purpose of increasing consistency. */ static inline QString unknownType() { return QtXmlPatterns::tr("%1 is an unknown schema type."); } static inline Expression::Ptr create(Expression *const expr, const YYLTYPE &sourceLocator, const ParserContext *const parseInfo) { parseInfo->staticContext->addLocation(expr, fromYYLTYPE(sourceLocator, parseInfo)); return Expression::Ptr(expr); } static inline Template::Ptr create(Template *const expr, const YYLTYPE &sourceLocator, const ParserContext *const parseInfo) { parseInfo->staticContext->addLocation(expr, fromYYLTYPE(sourceLocator, parseInfo)); return Template::Ptr(expr); } static inline Expression::Ptr create(const Expression::Ptr &expr, const YYLTYPE &sourceLocator, const ParserContext *const parseInfo) { parseInfo->staticContext->addLocation(expr.data(), fromYYLTYPE(sourceLocator, parseInfo)); return expr; } static Expression::Ptr createSimpleContent(const Expression::Ptr &source, const YYLTYPE &sourceLocator, const ParserContext *const parseInfo) { return create(parseInfo->isXSLT() ? new XSLTSimpleContentConstructor(source) : new SimpleContentConstructor(source), sourceLocator, parseInfo); } static void loadPattern(const Expression::Ptr &matchPattern, TemplatePattern::Vector &ourPatterns, const TemplatePattern::ID id, const PatternPriority priority, const Template::Ptr &temp) { Q_ASSERT(temp); const PatternPriority effectivePriority = qIsNaN(priority) ? matchPattern->patternPriority() : priority; ourPatterns.append(TemplatePattern::Ptr(new TemplatePattern(matchPattern, effectivePriority, id, temp))); } static Expression::Ptr typeCheckTemplateBody(const Expression::Ptr &body, const SequenceType::Ptr &reqType, const ParserContext *const parseInfo) { return TypeChecker::applyFunctionConversion(body, reqType, parseInfo->staticContext, ReportContext::XTTE0505, TypeChecker::Options(TypeChecker::AutomaticallyConvert | TypeChecker::GeneratePromotion)); } static void registerNamedTemplate(const QXmlName &name, const Expression::Ptr &body, ParserContext *const parseInfo, const YYLTYPE &sourceLocator, const Template::Ptr &temp) { Template::Ptr &e = parseInfo->namedTemplates[name]; if(e) { parseInfo->staticContext->error(QtXmlPatterns::tr("A template with name %1 " "has already been declared.") .arg(formatKeyword(parseInfo->staticContext->namePool(), name)), ReportContext::XTSE0660, fromYYLTYPE(sourceLocator, parseInfo)); } else { e = temp; e->body = body; } } /** * @short Centralizes code for creating numeric literals. */ template Expression::Ptr createNumericLiteral(const QString &in, const YYLTYPE &sl, const ParserContext *const parseInfo) { const Item num(TNumberClass::fromLexical(in)); if(num.template as()->hasError()) { parseInfo->staticContext->error(QtXmlPatterns::tr("%1 is not a valid numeric literal.") .arg(formatData(in)), ReportContext::XPST0003, fromYYLTYPE(sl, parseInfo)); return Expression::Ptr(); /* Avoid compiler warning. */ } else return create(new Literal(num), sl, parseInfo); } /** * @short The generated Bison parser calls this function when there is a parse error. * * It is not called, nor should be, for logical errors(which the Bison not know about). For those, * ReportContext::error() is called. */ static int XPatherror(YYLTYPE *sourceLocator, const ParserContext *const parseInfo, const char *const msg) { Q_UNUSED(sourceLocator); Q_ASSERT(parseInfo); parseInfo->staticContext->error(escape(QLatin1String(msg)), ReportContext::XPST0003, fromYYLTYPE(*sourceLocator, parseInfo)); return 1; } /** * When we want to connect the OrderBy and ReturnOrderBy, it might be that we have other expressions, such * as @c where and @c let inbetween. We need to continue through them. This function does that. */ static ReturnOrderBy *locateReturnClause(const Expression::Ptr &expr) { Q_ASSERT(expr); const Expression::ID id = expr->id(); if(id == Expression::IDLetClause || id == Expression::IDIfThenClause || id == Expression::IDForClause) return locateReturnClause(expr->operands()[1]); else if(id == Expression::IDReturnOrderBy) return expr->as(); else return 0; } static inline bool isPredicate(const Expression::ID id) { return id == Expression::IDGenericPredicate || id == Expression::IDFirstItemPredicate; } /** * Assumes expr is an AxisStep wrapped in some kind of predicates or paths. Filters * through the predicates and returns the AxisStep. */ static Expression::Ptr findAxisStep(const Expression::Ptr &expr, const bool throughStructures = true) { Q_ASSERT(expr); if(!throughStructures) return expr; Expression *candidate = expr.data(); Expression::ID id = candidate->id(); while(isPredicate(id) || id == Expression::IDPath) { const Expression::List &children = candidate->operands(); if(children.isEmpty()) return Expression::Ptr(); else { candidate = children.first().data(); id = candidate->id(); } } if(id == Expression::IDEmptySequence) return Expression::Ptr(); else { Q_ASSERT(candidate->is(Expression::IDAxisStep)); return Expression::Ptr(candidate); } } static void changeToTopAxis(const Expression::Ptr &op) { /* This axis must have been written away by now. */ Q_ASSERT(op->as()->axis() != QXmlNodeModelIndex::AxisChild); if(op->as()->axis() != QXmlNodeModelIndex::AxisSelf) op->as()->setAxis(QXmlNodeModelIndex::AxisAttributeOrTop); } /** * @short Writes @p operand1 and @p operand2, two operands in an XSL-T pattern, * into an equivalent XPath expression. * * Essentially, the following rewrite is done: * * * axis1::test1(a)/axis2::test2(b) * => * child-or-top::test2(b)[parent::test1(a)] * * * Section 5.5.3 The Meaning of a Pattern talks about rewrites that are applied to * only the first step in a pattern, but since we're doing rewrites more radically, * its line of reasoning cannot be followed. * * Keep in mind the rewrites that non-terminal PatternStep do. * * @see createIdPatternPath() */ static inline Expression::Ptr createPatternPath(const Expression::Ptr &operand1, const Expression::Ptr &operand2, const QXmlNodeModelIndex::Axis axis, const YYLTYPE &sl, const ParserContext *const parseInfo) { const Expression::Ptr operandL(findAxisStep(operand1, false)); if(operandL->is(Expression::IDAxisStep)) operandL->as()->setAxis(axis); else findAxisStep(operand1)->as()->setAxis(axis); return create(GenericPredicate::create(operand2, operandL, parseInfo->staticContext, fromYYLTYPE(sl, parseInfo)), sl, parseInfo); } /** * @short Performs the same role as createPatternPath(), but is tailored * for @c fn:key() and @c fn:id(). * * @c fn:key() and @c fn:id() can be part of path patterns(only as the first step, * to be precise) and that poses a challenge to rewriting because what * createPatternPath() is not possible to express, since the functions cannot be * node tests. E.g, this rewrite is not possible: * * * id-or-key/abc * => * child-or-top::abc[parent::id-or-key] * * * Our approach is to rewrite like this: * * * id-or-key/abc * => * child-or-top::abc[parent::node is id-or-key] * * * @p operand1 is the call to @c fn:key() or @c fn:id(), @p operand2 * the right operand, and @p axis the target axis to rewrite to. * * @see createPatternPath() */ static inline Expression::Ptr createIdPatternPath(const Expression::Ptr &operand1, const Expression::Ptr &operand2, const QXmlNodeModelIndex::Axis axis, const YYLTYPE &sl, const ParserContext *const parseInfo) { const Expression::Ptr operandR(findAxisStep(operand2)); Q_ASSERT(operandR); changeToTopAxis(operandR); const Expression::Ptr parentStep(create(new AxisStep(axis, BuiltinTypes::node), sl, parseInfo)); const Expression::Ptr isComp(create(new NodeComparison(parentStep, QXmlNodeModelIndex::Is, operand1), sl, parseInfo)); return create(GenericPredicate::create(operandR, isComp, parseInfo->staticContext, fromYYLTYPE(sl, parseInfo)), sl, parseInfo); } /** * @short Centralizes a translation message, for the * purpose of consistency and modularization. */ static inline QString prologMessage(const char *const msg) { Q_ASSERT(msg); return QtXmlPatterns::tr("Only one %1 declaration can occur in the query prolog.").arg(formatKeyword(msg)); } /** * @short Resolves against the static base URI and checks that @p collation * is a supported Unicode Collation. * * "If a default collation declaration specifies a collation by a * relative URI, that relative URI is resolved to an absolute * URI using the base URI in the static context." * * @returns the Unicode Collation properly resolved, if @p collation is a valid collation */ template static QUrl resolveAndCheckCollation(const QString &collation, const ParserContext *const parseInfo, const YYLTYPE &sl) { Q_ASSERT(parseInfo); const ReflectYYLTYPE ryy(sl, parseInfo); QUrl uri(AnyURI::toQUrl(collation, parseInfo->staticContext, &ryy)); if(uri.isRelative()) uri = parseInfo->staticContext->baseURI().resolved(uri); XPathHelper::checkCollationSupport(uri.toString(), parseInfo->staticContext, &ryy); return uri; } /* The Bison generated parser declares macros that aren't used * so suppress the warnings by fake usage of them. * * We do the same for some more defines in the first action. */ #if defined(YYLSP_NEEDED) \ || defined(YYBISON) \ || defined(YYBISON_VERSION) \ || defined(YYPURE) \ || defined(yydebug) \ || defined(YYSKELETON_NAME) #endif /** * Wraps @p operand with a CopyOf in case it makes any difference. * * There is no need to wrap the return value in a call to create(), it's * already done. */ static Expression::Ptr createCopyOf(const Expression::Ptr &operand, const ParserContext *const parseInfo, const YYLTYPE &sl) { return create(new CopyOf(operand, parseInfo->inheritNamespacesMode, parseInfo->preserveNamespacesMode), sl, parseInfo); } static Expression::Ptr createCompatStore(const Expression::Ptr &expr, const YYLTYPE &sourceLocator, const ParserContext *const parseInfo) { return create(new StaticCompatibilityStore(expr), sourceLocator, parseInfo); } /** * @short Creates an Expression that corresponds to /. This is literally * fn:root(self::node()) treat as document-node(). */ static Expression::Ptr createRootExpression(const ParserContext *const parseInfo, const YYLTYPE &sl) { Q_ASSERT(parseInfo); const QXmlName name(StandardNamespaces::fn, StandardLocalNames::root); Expression::List args; args.append(create(new ContextItem(), sl, parseInfo)); const ReflectYYLTYPE ryy(sl, parseInfo); const Expression::Ptr fnRoot(parseInfo->staticContext->functionSignatures() ->createFunctionCall(name, args, parseInfo->staticContext, &ryy)); Q_ASSERT(fnRoot); return create(new TreatAs(create(fnRoot, sl, parseInfo), CommonSequenceTypes::ExactlyOneDocumentNode), sl, parseInfo); } static int XPathlex(YYSTYPE *lexVal, YYLTYPE *sourceLocator, const ParserContext *const parseInfo) { #ifdef Patternist_DEBUG_PARSER /** * "External integer variable set to zero by default. If yydebug * is given a nonzero value, the parser will output information on * input symbols and parser action. See section Debugging Your Parser." */ # define YYDEBUG 1 extern int XPathdebug; XPathdebug = 1; #endif Q_ASSERT(parseInfo); const Tokenizer::Token tok(parseInfo->tokenizer->nextToken(sourceLocator)); (*lexVal).sval = tok.value; return static_cast(tok.type); } /** * @short Creates a path expression which contains the step // between * @p begin and and @p end. * * begin//end is a short form for: begin/descendant-or-self::node()/end * * This will be compiled as two-path expression: (/)/(//.)/step/ */ static Expression::Ptr createSlashSlashPath(const Expression::Ptr &begin, const Expression::Ptr &end, const YYLTYPE &sourceLocator, const ParserContext *const parseInfo) { const Expression::Ptr twoSlash(create(new AxisStep(QXmlNodeModelIndex::AxisDescendantOrSelf, BuiltinTypes::node), sourceLocator, parseInfo)); const Expression::Ptr p1(create(new Path(begin, twoSlash), sourceLocator, parseInfo)); return create(new Path(p1, end), sourceLocator, parseInfo); } /** * @short Creates a call to fn:concat() with @p args as the arguments. */ static inline Expression::Ptr createConcatFN(const ParserContext *const parseInfo, const Expression::List &args, const YYLTYPE &sourceLocator) { Q_ASSERT(parseInfo); const QXmlName name(StandardNamespaces::fn, StandardLocalNames::concat); const ReflectYYLTYPE ryy(sourceLocator, parseInfo); return create(parseInfo->staticContext->functionSignatures()->createFunctionCall(name, args, parseInfo->staticContext, &ryy), sourceLocator, parseInfo); } static inline Expression::Ptr createDirAttributeValue(const Expression::List &content, const ParserContext *const parseInfo, const YYLTYPE &sourceLocator) { if(content.isEmpty()) return create(new EmptySequence(), sourceLocator, parseInfo); else if(content.size() == 1) return content.first(); else return createConcatFN(parseInfo, content, sourceLocator); } /** * @short Checks for variable initialization circularity. * * "A recursive function that checks for recursion is full of ironies." * * -- The Salsa Master * * Issues an error via @p parseInfo's StaticContext if the initialization * expression @p checkee for the global variable @p var, contains a variable * reference to @p var. That is, if there's a circularity. * * @see XQuery 1.0: An XML * Query Language, err:XQST0054 */ static void checkVariableCircularity(const VariableDeclaration::Ptr &var, const Expression::Ptr &checkee, const VariableDeclaration::Type type, FunctionSignature::List &signList, const ParserContext *const parseInfo) { Q_ASSERT(var); Q_ASSERT(checkee); Q_ASSERT(parseInfo); const Expression::ID id = checkee->id(); if(id == Expression::IDExpressionVariableReference) { const ExpressionVariableReference *const ref = static_cast(checkee.data()); if(var->slot == ref->slot() && type == ref->variableDeclaration()->type) { parseInfo->staticContext->error(QtXmlPatterns::tr("The initialization of variable %1 " "depends on itself").arg(formatKeyword(var, parseInfo->staticContext->namePool())), parseInfo->isXSLT() ? ReportContext::XTDE0640 : ReportContext::XQST0054, ref); return; } else { /* If the variable we're checking is below another variable, it can be a recursive * dependency through functions, so we need to check variable references too. */ checkVariableCircularity(var, ref->sourceExpression(), type, signList, parseInfo); return; } } else if(id == Expression::IDUserFunctionCallsite) { const UserFunctionCallsite::Ptr callsite(checkee); const FunctionSignature::Ptr sign(callsite->callTargetDescription()); const FunctionSignature::List::const_iterator end(signList.constEnd()); FunctionSignature::List::const_iterator it(signList.constBegin()); bool noMatch = true; for(; it != end; ++it) { if(*it == sign) { /* The variable we're checking is depending on a function that's recursive. The * user has written a weird query, in other words. Since it's the second time * we've encountered a callsite, we now skip it. */ noMatch = false; break; } } if(noMatch) { signList.append(sign); /* Check the body of the function being called. */ checkVariableCircularity(var, callsite->body(), type, signList, parseInfo); } /* Continue with the operands, such that we also check the arguments of the callsite. */ } else if(id == Expression::IDUnresolvedVariableReference) { /* We're called before it has rewritten itself. */ checkVariableCircularity(var, checkee->as()->replacement(), type, signList, parseInfo); } /* Check the operands. */ const Expression::List ops(checkee->operands()); if(ops.isEmpty()) return; const Expression::List::const_iterator end(ops.constEnd()); Expression::List::const_iterator it(ops.constBegin()); for(; it != end; ++it) checkVariableCircularity(var, *it, type, signList, parseInfo); } static void variableUnavailable(const QXmlName &variableName, const ParserContext *const parseInfo, const YYLTYPE &location) { parseInfo->staticContext->error(QtXmlPatterns::tr("No variable with name %1 exists") .arg(formatKeyword(parseInfo->staticContext->namePool(), variableName)), ReportContext::XPST0008, fromYYLTYPE(location, parseInfo)); } /** * The Cardinality in a TypeDeclaration for a variable in a quantification has no effect, * and this function ensures this by changing @p type to Cardinality Cardinality::zeroOrMore(). * * @see Bugzilla Bug 3305 * Cardinality + on range variables * @see ParserContext::finalizePushedVariable() */ static inline SequenceType::Ptr quantificationType(const SequenceType::Ptr &type) { Q_ASSERT(type); return makeGenericSequenceType(type->itemType(), Cardinality::zeroOrMore()); } /** * @p seqType and @p expr may be @c null. */ static Expression::Ptr pushVariable(const QXmlName name, const SequenceType::Ptr &seqType, const Expression::Ptr &expr, const VariableDeclaration::Type type, const YYLTYPE &sourceLocator, ParserContext *const parseInfo, const bool checkSource = true) { Q_ASSERT(!name.isNull()); Q_ASSERT(parseInfo); /* -2 will cause Q_ASSERTs to trigger if it isn't changed. */ VariableSlotID slot = -2; switch(type) { case VariableDeclaration::FunctionArgument: /* Fallthrough. */ case VariableDeclaration::ExpressionVariable: { slot = parseInfo->allocateExpressionSlot(); break; } case VariableDeclaration::GlobalVariable: { slot = parseInfo->allocateGlobalVariableSlot(); break; } case VariableDeclaration::RangeVariable: { slot = parseInfo->staticContext->allocateRangeSlot(); break; } case VariableDeclaration::PositionalVariable: { slot = parseInfo->allocatePositionalSlot(); break; } case VariableDeclaration::TemplateParameter: /* Fallthrough. We do nothing, template parameters * doesn't use context slots at all, they're hashed * on the name. */ case VariableDeclaration::ExternalVariable: /* We do nothing, external variables doesn't use *context slots/stack frames at all. */ ; } const VariableDeclaration::Ptr var(new VariableDeclaration(name, slot, type, seqType)); Expression::Ptr checked; if(checkSource && seqType) { if(expr) { /* We only want to add conversion for function arguments, and variables * if we're XSL-T. * * We unconditionally skip TypeChecker::CheckFocus because the StaticContext we * pass hasn't set up the focus yet, since that's the parent's responsibility. */ const TypeChecker::Options options(( type == VariableDeclaration::FunctionArgument || type == VariableDeclaration::TemplateParameter || parseInfo->isXSLT()) ? TypeChecker::AutomaticallyConvert : TypeChecker::Options()); checked = TypeChecker::applyFunctionConversion(expr, seqType, parseInfo->staticContext, parseInfo->isXSLT() ? ReportContext::XTTE0570 : ReportContext::XPTY0004, options); } } else checked = expr; /* Add an evaluation cache for all expression variables. No EvaluationCache is needed for * positional variables because in the end they are calls to Iterator::position(). Similarly, * no need to cache range variables either because they are calls to DynamicContext::rangeVariable(). * * We don't do it for function arguments because the Expression being cached depends -- it depends * on the callsite. UserFunctionCallsite is responsible for the evaluation caches in that case. * * In some cases the EvaluationCache instance isn't necessary, but in those cases EvaluationCache * optimizes itself away. */ if(type == VariableDeclaration::ExpressionVariable) checked = create(new EvaluationCache(checked, var, parseInfo->allocateCacheSlot()), sourceLocator, parseInfo); else if(type == VariableDeclaration::GlobalVariable) checked = create(new EvaluationCache(checked, var, parseInfo->allocateCacheSlot()), sourceLocator, parseInfo); var->setExpression(checked); parseInfo->variables.push(var); return checked; } static inline VariableDeclaration::Ptr variableByName(const QXmlName name, const ParserContext *const parseInfo) { Q_ASSERT(!name.isNull()); Q_ASSERT(parseInfo); /* We walk the list backwards. */ const VariableDeclaration::Stack::const_iterator start(parseInfo->variables.constBegin()); VariableDeclaration::Stack::const_iterator it(parseInfo->variables.constEnd()); while(it != start) { --it; Q_ASSERT(*it); if((*it)->name == name) return *it; } return VariableDeclaration::Ptr(); } static Expression::Ptr resolveVariable(const QXmlName &name, const YYLTYPE &sourceLocator, ParserContext *const parseInfo, const bool raiseErrorOnUnavailability) { const VariableDeclaration::Ptr var(variableByName(name, parseInfo)); Expression::Ptr retval; if(var && var->type != VariableDeclaration::ExternalVariable) { switch(var->type) { case VariableDeclaration::RangeVariable: { retval = create(new RangeVariableReference(var->expression(), var->slot), sourceLocator, parseInfo); break; } case VariableDeclaration::GlobalVariable: /* Fallthrough. From the perspective of an ExpressionVariableReference, it can't tell * a difference between a global and a local expression variable. However, the cache * mechanism must. */ case VariableDeclaration::ExpressionVariable: { retval = create(new ExpressionVariableReference(var->slot, var.data()), sourceLocator, parseInfo); break; } case VariableDeclaration::FunctionArgument: { retval = create(new ArgumentReference(var->sequenceType, var->slot), sourceLocator, parseInfo); break; } case VariableDeclaration::PositionalVariable: { retval = create(new PositionalVariableReference(var->slot), sourceLocator, parseInfo); break; } case VariableDeclaration::TemplateParameter: { retval = create(new TemplateParameterReference(var.data()), sourceLocator, parseInfo); break; } case VariableDeclaration::ExternalVariable: /* This code path will never be hit, but the case * label silences a warning. See above. */ ; } Q_ASSERT(retval); var->references.append(retval); } else { /* Let's see if your external variable loader can provide us with one. */ const SequenceType::Ptr varType(parseInfo->staticContext-> externalVariableLoader()->announceExternalVariable(name, CommonSequenceTypes::ZeroOrMoreItems)); if(varType) { const Expression::Ptr extRef(create(new ExternalVariableReference(name, varType), sourceLocator, parseInfo)); const Expression::Ptr checked(TypeChecker::applyFunctionConversion(extRef, varType, parseInfo->staticContext)); retval = checked; } else if(!raiseErrorOnUnavailability && parseInfo->isXSLT()) { /* In XSL-T, global variables are in scope for the whole * stylesheet, so we must resolve this first at the end. */ retval = create(new UnresolvedVariableReference(name), sourceLocator, parseInfo); parseInfo->unresolvedVariableReferences.insert(name, retval); } else variableUnavailable(name, parseInfo, sourceLocator); } return retval; } static Expression::Ptr createReturnOrderBy(const OrderSpecTransfer::List &orderSpecTransfer, const Expression::Ptr &returnExpr, const OrderBy::Stability stability, const YYLTYPE &sourceLocator, const ParserContext *const parseInfo) { // TODO do resize(orderSpec.size() + 1) Expression::List exprs; OrderBy::OrderSpec::Vector orderSpecs; exprs.append(returnExpr); const int len = orderSpecTransfer.size(); for(int i = 0; i < len; ++i) { exprs.append(orderSpecTransfer.at(i).expression); orderSpecs.append(orderSpecTransfer.at(i).orderSpec); } return create(new ReturnOrderBy(stability, orderSpecs, exprs), sourceLocator, parseInfo); } %} /* This grammar shouldn't be compiled with anything older than the Bison version * specified below. This '%require' directive was introduced in Bison 2.2. */ %require "2.3a" %name-prefix="XPath" /* Specifies the name of the generated parser. */ %output="qquerytransformparser.cpp" /* Output the .output file. */ %verbose /* Yes, we want descriptive error messages. */ %error-verbose /* We'd like to be reentrant/thread-safe */ %pure-parser /* We want code for line/columns to be generated. */ %locations /* Create a header file and put declarations there. */ %defines %parse-param {QT_PREPEND_NAMESPACE(QPatternist)::ParserContext *const parseInfo} %lex-param {QT_PREPEND_NAMESPACE(QPatternist)::ParserContext *const parseInfo} %expect 4 /* Silences the following: state 327 293 SequenceType: ItemType . OccurrenceIndicator "+" shift, and go to state 379 "*" shift, and go to state 380 "?" shift, and go to state 381 "+" [reduce using rule 295 (OccurrenceIndicator)] "*" [reduce using rule 295 (OccurrenceIndicator)] $default reduce using rule 295 (OccurrenceIndicator) OccurrenceIndicator go to state 382 state 45 200 PathExpr: "/" . RelativePathExpr 203 | "/" . [...] "<" [reduce using rule 203 (PathExpr)] "*" [reduce using rule 203 (PathExpr)] $default reduce using rule 203 (PathExpr) */ %token T_STRING_LITERAL "" /** * This token is only used in element content and signals content that * is not Boundary whitespace. Nevertheless, the token value can be all whitespace, * but it was specified using character references or CDATA sections by the user. */ %token T_NON_BOUNDARY_WS "" /* XPath 2.0 allows quotes and apostrophes to be escaped with "" and ''; this token is is used for XPath 2.0 literals such that we can flag syntax errors if running in 1.0 mode. */ %token T_XPATH2_STRING_LITERAL "" %token T_QNAME "QName" %token T_NCNAME "NCName" /* A QName as a clark name. See QXmlName::toClarkName(). */ %token T_CLARK_NAME "ClarkName" /** * Is "ncname:*". The token value does not include the colon and the star. */ %token T_ANY_LOCAL_NAME /** * Is "*:ncname". The token value does not include the colon and the star. */ %token T_ANY_PREFIX /** * An XPath 1.0 number literal. It is a string value because * Numeric::fromLexical() does the tokenization. */ %token T_NUMBER "" /** * XPath 2.0 number literal. It includes the use of 'e'/'E' */ %token T_XPATH2_NUMBER "" %token T_ANCESTOR "ancestor" %token T_ANCESTOR_OR_SELF "ancestor-or-self" %token T_AND "and" %token T_APOS "'" %token T_APPLY_TEMPLATE "apply-template" %token T_AS "as" %token T_ASCENDING "ascending" %token T_ASSIGN ":=" %token T_AT "at" %token T_AT_SIGN "@" %token T_ATTRIBUTE "attribute" %token T_AVT /* Synthetic token. Signals an attribute value template. */ %token T_BAR "|" %token T_BASEURI "base-uri" %token T_BEGIN_END_TAG ">" %token T_FOR_APPLY_TEMPLATE "for-apply-template" /* Synthetic token, used in XSL-T. */ %token T_FOR "for" %token T_FUNCTION "function" %token T_GE "ge" %token T_G_EQ "=" %token T_G_GE ">=" %token T_G_GT ">" %token T_G_LE "<=" %token T_G_LT "<" %token T_G_NE "!=" %token T_GREATEST "greatest" %token T_GT "gt" %token T_IDIV "idiv" %token T_IF "if" %token T_IMPORT "import" %token T_INHERIT "inherit" %token T_IN "in" %token T_INSTANCE "instance" %token T_INTERSECT "intersect" %token T_IS "is" %token T_ITEM "item" %token T_LAX "lax" %token T_LBRACKET "[" %token T_LEAST "least" %token T_LE "le" %token T_LET "let" %token T_LPAREN "(" %token T_LT "lt" %token T_MAP "map" /* Synthetic token, used in XSL-T. */ %token T_MATCHES "matches" %token T_MINUS "-" %token T_MODE "mode" /* Synthetic token, used in XSL-T. */ %token T_MOD "mod" %token T_MODULE "module" %token T_NAME "name" %token T_NAMESPACE "namespace" %token T_NE "ne" %token T_NODE "node" %token T_NO_INHERIT "no-inherit" %token T_NO_PRESERVE "no-preserve" %token T_OF "of" %token T_OPTION "option" %token T_ORDERED "ordered" %token T_ORDERING "ordering" %token T_ORDER "order" %token T_OR "or" %token T_PARENT "parent" %token T_PI_START "" %token T_QUOTE "\"" %token T_RBRACKET "]" %token T_RETURN "return" %token T_RPAREN ")" %token T_SATISFIES "satisfies" %token T_SCHEMA_ATTRIBUTE "schema-attribute" %token T_SCHEMA_ELEMENT "schema-element" %token T_SCHEMA "schema" %token T_SELF "self" %token T_SEMI_COLON ";" %token T_SLASH "/" %token T_SLASHSLASH "//" %token T_SOME "some" %token T_SORT "sort" /* Synthetic token, used in XSL-T. */ %token T_STABLE "stable" %token T_STAR "*" %token T_STRICT "strict" %token T_STRIP "strip" %token T_SUCCESS /* Synthetic token, used by the Tokenizer. */ %token T_COMMENT_CONTENT %token T_PI_CONTENT %token T_PI_TARGET %token T_XSLT_VERSION /* Synthetic token, used in XSL-T. */ %token T_TEMPLATE "template" %token T_TEXT "text" %token T_THEN "then" %token T_TO "to" %token T_TREAT "treat" %token T_TUNNEL "tunnel" /* Synthetic token, used in XSL-T. */ %token T_TYPESWITCH "typeswitch" %token T_UNION "union" %token T_UNORDERED "unordered" %token T_VALIDATE "validate" %token T_VARIABLE "variable" %token T_VERSION "version" %token T_WHERE "where" %token T_XQUERY "xquery" %token T_INTERNAL "internal" /* Synthetic token, used in XSL-T. */ %token T_INTERNAL_NAME "internal-name" /* Synthetic token, used in XSL-T. */ %token T_CURRENT "current" /* Synthetic token, used in XSL-T. */ /* Alphabetically. */ %type Attribute %type DirAttributeList %type OccurrenceIndicator %type Axis AxisToken %type BoundarySpacePolicy %type IntersectOperator %type ConstructionMode %type MultiplyOperator AdditiveOperator UnaryOperator %type NodeOperator %type OrderingEmptySequence EmptynessModifier %type DirectionModifier %type OrderingMode %type PositionalVar %type ValidationMode %type ValueComparisonOperator GeneralComparisonOperator %type OrExpr AndExpr ComparisonExpr UnionExpr Literal AdditiveExpr MultiplicativeExpr PrimaryExpr FilterExpr StepExpr PathExpr RelativePathExpr Expr ExprSingle VarRef ContextItemExpr IfExpr CastExpr CastableExpr TreatExpr InstanceOfExpr ValueExpr UnaryExpr NodeComp IntersectExceptExpr RangeExpr ParenthesizedExpr ValueComp FunctionCallExpr GeneralComp ForClause WhereClause FLWORExpr ForTail QuantifiedExpr QueryBody SomeQuantificationExpr SomeQuantificationTail EveryQuantificationExpr EveryQuantificationTail ExtensionExpr EnclosedOptionalExpr VariableValue EnclosedExpr FunctionBody ValidateExpr NumericLiteral OrderingExpr TypeswitchExpr LetClause LetTail Constructor DirectConstructor DirElemConstructor ComputedConstructor CompDocConstructor CompElemConstructor CompTextConstructor CompCommentConstructor CompPIConstructor DirPIConstructor CompAttrConstructor DirElemConstructorTail AxisStep ForwardStep ReverseStep AbbrevForwardStep CaseDefault CaseClause CaseTail CompAttributeName FilteredAxisStep DirCommentConstructor CompPIName DirAttributeValue AbbrevReverseStep CompNamespaceConstructor CompElementName CompNameExpr SatisfiesClause Pattern PathPattern PatternStep RelativePathPattern IdKeyPattern OptionalAssign OptionalDefaultValue %type OrderSpec %type ExpressionSequence FunctionArguments DirElemContent AttrValueContent %type OrderSpecList OrderByClause MandatoryOrderByClause %type Param %type ParamList %type KindTest ItemType AtomicType NodeTest NameTest WildCard NodeTestInAxisStep ElementTest AttributeTest SchemaElementTest SchemaAttributeTest TextTest CommentTest PITest DocumentTest AnyKindTest AnyAttributeTest %type ElementName QName VarName FunctionName PragmaName TypeName NCName CaseVariable AttributeName OptionalTemplateName TemplateName Mode OptionalMode %type Modes OptionalModes %type SequenceType SingleType TypeDeclaration %type URILiteral StringLiteral LexicalName %type IsInternal IsTunnel %type OptionalPriority %type MapOrSlash /* Operator Precendence * See: http://www.w3.org/TR/xpath20/#parse-note-occurrence-indicators */ %left T_STAR T_DIV %left T_PLUS T_MINUS %% /* Here, the grammar starts. In the brackets on the right you * find the number of corresponding EBNF rule in the XQuery 1.0 specification. If it * contains an X, it means the non-terminal has no counter part in the grammar, but * exists for implementation purposes. */ Module: VersionDecl LibraryModule /* [1] */ | VersionDecl MainModule VersionDecl: /* empty */ /* [2] */ | T_XQUERY T_VERSION StringLiteral Encoding Separator { /* Suppress more compiler warnings about unused defines. */ #if defined(YYNNTS) \ || defined(yyerrok) \ || defined(YYNSTATES) \ || defined(YYRHSLOC) \ || defined(YYRECOVERING) \ || defined(YYFAIL) \ || defined(YYERROR) \ || defined(YYNRULES) \ || defined(YYBACKUP) \ || defined(YYMAXDEPTH) \ || defined(yyclearin) \ || defined(YYERRCODE) \ || defined(YY_LOCATION_PRINT) \ || defined(YYLLOC_DEFAULT) #endif if($3 != QLatin1String("1.0")) { const ReflectYYLTYPE ryy(@$, parseInfo); parseInfo->staticContext->error(QtXmlPatterns::tr("Version %1 is not supported. The supported " "XQuery version is 1.0.") .arg(formatData($3)), ReportContext::XQST0031, &ryy); } } Encoding: /* empty */ /* [X] */ | T_ENCODING StringLiteral { const QRegExp encNameRegExp(QLatin1String("[A-Za-z][A-Za-z0-9._\\-]*")); if(!encNameRegExp.exactMatch($2)) { parseInfo->staticContext->error(QtXmlPatterns::tr("The encoding %1 is invalid. " "It must contain Latin characters only, " "must not contain whitespace, and must match " "the regular expression %2.") .arg(formatKeyword((yyvsp[(2) - (2)].sval)), formatExpression(encNameRegExp.pattern())), ReportContext::XQST0087, fromYYLTYPE(@$, parseInfo)); } } MainModule: Prolog QueryBody /* [3] */ { /* In XSL-T, we can have dangling variable references, so resolve them * before we proceed with other steps, such as checking circularity. */ if(parseInfo->isXSLT()) { typedef QHash Hash; const Hash::const_iterator end(parseInfo->unresolvedVariableReferences.constEnd()); for(Hash::const_iterator it(parseInfo->unresolvedVariableReferences.constBegin()); it != end; ++it) { const Expression::Ptr body(resolveVariable(it.key(), @$, parseInfo, true)); // TODO source locations vaise Q_ASSERT(body); it.value()->as()->bindTo(body); } } /* The UserFunction callsites aren't bound yet, so bind them(if possible!). */ { const UserFunctionCallsite::List::const_iterator cend(parseInfo->userFunctionCallsites.constEnd()); UserFunctionCallsite::List::const_iterator cit(parseInfo->userFunctionCallsites.constBegin()); for(; cit != cend; ++cit) /* For each callsite. */ { const UserFunctionCallsite::Ptr callsite(*cit); Q_ASSERT(callsite); const UserFunction::List::const_iterator end(parseInfo->userFunctions.constEnd()); UserFunction::List::const_iterator it(parseInfo->userFunctions.constBegin()); for(; it != end; ++it) /* For each UserFunction. */ { const FunctionSignature::Ptr sign((*it)->signature()); Q_ASSERT(sign); if(callsite->isSignatureValid(sign)) { callsite->setSource((*it), parseInfo->allocateCacheSlots((*it)->argumentDeclarations().count())); break; } } if(it == end) { parseInfo->staticContext->error(QtXmlPatterns::tr("No function with signature %1 is available") .arg(formatFunction(callsite)), ReportContext::XPST0017, fromYYLTYPE(@$, parseInfo)); } } } /* Mark callsites in UserFunction bodies as recursive, if they are. */ { const UserFunction::List::const_iterator fend(parseInfo->userFunctions.constEnd()); UserFunction::List::const_iterator fit(parseInfo->userFunctions.constBegin()); for(; fit != fend; ++fit) { CallTargetDescription::List signList; signList.append((*fit)->signature()); CallTargetDescription::checkCallsiteCircularity(signList, (*fit)->body()); } } /* Now, check all global variables for circularity. This is done * backwards because global variables are only in scope below them, * in XQuery. */ { const VariableDeclaration::List::const_iterator start(parseInfo->declaredVariables.constBegin()); VariableDeclaration::List::const_iterator it(parseInfo->declaredVariables.constEnd()); while(it != start) { --it; if((*it)->type != VariableDeclaration::ExpressionVariable && (*it)->type != VariableDeclaration::GlobalVariable) continue; /* We want to ignore 'external' variables. */ FunctionSignature::List signList; checkVariableCircularity(*it, (*it)->expression(), (*it)->type, signList, parseInfo); ExpressionFactory::registerLastPath((*it)->expression()); parseInfo->finalizePushedVariable(1, false); /* Warn if it's unused. */ } } /* Generate code for doing initial template name calling. One problem * is that we compilation in the initial template name, since we throw away the * code if we don't have the requested template. */ if(parseInfo->languageAccent == QXmlQuery::XSLT20 && !parseInfo->initialTemplateName.isNull() && parseInfo->namedTemplates.contains(parseInfo->initialTemplateName)) { parseInfo->queryBody = create(new CallTemplate(parseInfo->initialTemplateName, WithParam::Hash()), @$, parseInfo); parseInfo->templateCalls.append(parseInfo->queryBody); /* We just discard the template body that XSLTTokenizer generated. */ } else parseInfo->queryBody = $2; } LibraryModule: ModuleDecl Prolog /* [4] */ ModuleDecl: T_MODULE T_NAMESPACE T_NCNAME T_G_EQ URILiteral Separator /* [5] */ { // TODO add to namespace context parseInfo->moduleNamespace = parseInfo->staticContext->namePool()->allocateNamespace($3); } Prolog: /* Empty. */ /* [6] */ /* First part. */ | Prolog DefaultNamespaceDecl { allowedIn(QXmlQuery::XQuery10, parseInfo, @$); if(parseInfo->hasSecondPrologPart) parseInfo->staticContext->error(QtXmlPatterns::tr("A default namespace declaration must occur before function, " "variable, and option declarations."), ReportContext::XPST0003, fromYYLTYPE(@$, parseInfo)); } | Prolog Setter { if(parseInfo->hasSecondPrologPart) parseInfo->staticContext->error(QtXmlPatterns::tr("A default namespace declaration must occur before function, " "variable, and option declarations."), ReportContext::XPST0003, fromYYLTYPE(@$, parseInfo)); } | Prolog NamespaceDecl { if(parseInfo->hasSecondPrologPart) parseInfo->staticContext->error(QtXmlPatterns::tr("Namespace declarations must occur before function, " "variable, and option declarations."), ReportContext::XPST0003, fromYYLTYPE(@$, parseInfo)); } | Prolog Import { allowedIn(QXmlQuery::XQuery10, parseInfo, @$); if(parseInfo->hasSecondPrologPart) parseInfo->staticContext->error(QtXmlPatterns::tr("Module imports must occur before function, " "variable, and option declarations."), ReportContext::XPST0003, fromYYLTYPE(@$, parseInfo)); } | Prolog TemplateDecl /* Second part. */ | Prolog VarDecl { parseInfo->hasSecondPrologPart = true; } | Prolog FunctionDecl { parseInfo->hasSecondPrologPart = true; } | Prolog OptionDecl { allowedIn(QXmlQuery::XQuery10, parseInfo, @$); parseInfo->hasSecondPrologPart = true; } /* * declare template name theName * { * "expression" * }; * * or * * declare template name theName matches (pattern) mode modeName priority 123 * { * "expression" * }; * */ TemplateDecl: T_DECLARE T_TEMPLATE TemplateName OptionalTemplateParameters TypeDeclaration EnclosedOptionalExpr Separator /* [X] */ { Template::Ptr temp(create(new Template(parseInfo->currentImportPrecedence, $5), @$, parseInfo)); registerNamedTemplate($3, typeCheckTemplateBody($6, $5, parseInfo), parseInfo, @1, temp); temp->templateParameters = parseInfo->templateParameters; parseInfo->templateParametersHandled(); } | T_DECLARE T_TEMPLATE OptionalTemplateName T_MATCHES T_LPAREN { parseInfo->isParsingPattern = true; } Pattern { parseInfo->isParsingPattern = false; } T_RPAREN OptionalModes OptionalPriority OptionalTemplateParameters TypeDeclaration EnclosedOptionalExpr Separator /* [X] */ { /* In this grammar branch, we're guaranteed to be a template rule, but * may also be a named template. */ const ImportPrecedence ip = parseInfo->isFirstTemplate() ? 0 : parseInfo->currentImportPrecedence; Expression::Ptr pattern($7); const TemplatePattern::ID templateID = parseInfo->allocateTemplateID(); Template::Ptr templ(create(new Template(ip, $13), @$, parseInfo)); templ->body = typeCheckTemplateBody($14, $13, parseInfo); templ->templateParameters = parseInfo->templateParameters; parseInfo->templateParametersHandled(); TemplatePattern::Vector ourPatterns; /* We do it as per 6.4 Conflict Resolution for Template Rules: * * "If the pattern contains multiple alternatives separated by |, then * the template rule is treated equivalently to a set of template * rules, one for each alternative. However, it is not an error if a * node matches more than one of the alternatives." */ while(pattern->is(Expression::IDCombineNodes)) { const Expression::List operands(pattern->operands()); pattern = operands.first(); loadPattern(operands.at(1), ourPatterns, templateID, $11, templ); } loadPattern(pattern, ourPatterns, templateID, $11, templ); if(!$3.isNull()) registerNamedTemplate($3, $14, parseInfo, @1, templ); /* Now, let's add it to all the relevant templates. */ for(int i = 0; i < $10.count(); ++i) /* For each mode. */ { const QXmlName &modeName = $10.at(i); if(modeName == QXmlName(StandardNamespaces::InternalXSLT, StandardLocalNames::all) && $10.count() > 1) { parseInfo->staticContext->error(QtXmlPatterns::tr("The keyword %1 cannot occur with any other mode name.") .arg(formatKeyword(QLatin1String("#all"))), ReportContext::XTSE0530, fromYYLTYPE(@$, parseInfo)); } /* For each pattern the template use. */ const TemplateMode::Ptr mode(parseInfo->modeFor(modeName)); for(int t = 0; t < ourPatterns.count(); ++t) mode->templatePatterns.append(ourPatterns.at(t)); } } OptionalPriority: /* Empty. */ /* [X] */ { $$ = std::numeric_limits::quiet_NaN(); } | T_PRIORITY StringLiteral { const AtomicValue::Ptr val(Decimal::fromLexical($2)); if(val->hasError()) { parseInfo->staticContext->error(QtXmlPatterns::tr("The value of attribute %1 must be of type %2, which %3 isn't.") .arg(formatKeyword(QLatin1String("priority")), formatType(parseInfo->staticContext->namePool(), BuiltinTypes::xsDecimal), formatData($2)), ReportContext::XTSE0530, fromYYLTYPE(@$, parseInfo)); } else $$ = val->as()->toDouble(); } OptionalTemplateName: /* Empty. */ /* [X] */ { $$ = QXmlName(); } | TemplateName TemplateName: T_NAME ElementName { $$ = $2; } Setter: BoundarySpaceDecl /* [7] */ | DefaultCollationDecl { allowedIn(QXmlQuery::XQuery10, parseInfo, @$); } | BaseURIDecl | ConstructionDecl { allowedIn(QXmlQuery::XQuery10, parseInfo, @$); } | OrderingModeDecl { allowedIn(QXmlQuery::XQuery10, parseInfo, @$); } | EmptyOrderDecl { allowedIn(QXmlQuery::XQuery10, parseInfo, @$); } | CopyNamespacesDecl Import: SchemaImport /* [8] */ | ModuleImport Separator: T_SEMI_COLON /* [9] */ NamespaceDecl: T_DECLARE T_NAMESPACE T_NCNAME T_G_EQ URILiteral IsInternal Separator /* [10] */ { if(!$6) allowedIn(QXmlQuery::XQuery10, parseInfo, @$); if($3 == QLatin1String("xmlns")) { parseInfo->staticContext->error(QtXmlPatterns::tr("It is not possible to redeclare prefix %1.") .arg(formatKeyword(QLatin1String("xmlns"))), ReportContext::XQST0070, fromYYLTYPE(@$, parseInfo)); } else if ($5 == CommonNamespaces::XML || $3 == QLatin1String("xml")) { parseInfo->staticContext->error(QtXmlPatterns::tr( "The prefix %1 can not be bound. By default, it is already bound " "to the namespace %2.") .arg(formatKeyword("xml")) .arg(formatURI(CommonNamespaces::XML)), ReportContext::XQST0070, fromYYLTYPE(@$, parseInfo)); } else if(parseInfo->declaredPrefixes.contains($3)) { /* This includes the case where the user has bound a default prefix(such * as 'local') and now tries to do it again. */ parseInfo->staticContext->error(QtXmlPatterns::tr("Prefix %1 is already declared in the prolog.") .arg(formatKeyword($3)), ReportContext::XQST0033, fromYYLTYPE(@$, parseInfo)); } else { parseInfo->declaredPrefixes.append($3); if($5.isEmpty()) { parseInfo->staticContext->namespaceBindings()->addBinding(QXmlName(StandardNamespaces::UndeclarePrefix, StandardLocalNames::empty, parseInfo->staticContext->namePool()->allocatePrefix($3))); } else { parseInfo->staticContext->namespaceBindings()->addBinding(parseInfo->staticContext->namePool()->allocateBinding($3, $5)); } } } BoundarySpaceDecl: T_DECLARE T_BOUNDARY_SPACE BoundarySpacePolicy Separator /* [11] */ { if(parseInfo->hasDeclaration(ParserContext::BoundarySpaceDecl)) { parseInfo->staticContext->error(prologMessage("declare boundary-space"), ReportContext::XQST0068, fromYYLTYPE(@$, parseInfo)); } else { parseInfo->staticContext->setBoundarySpacePolicy($3); parseInfo->registerDeclaration(ParserContext::BoundarySpaceDecl); } } BoundarySpacePolicy: T_STRIP /* [X] */ { $$ = StaticContext::BSPStrip; } | T_PRESERVE { $$ = StaticContext::BSPPreserve; } DefaultNamespaceDecl: DeclareDefaultElementNamespace /* [12] */ | DeclareDefaultFunctionNamespace DeclareDefaultElementNamespace: T_DECLARE T_DEFAULT T_ELEMENT T_NAMESPACE URILiteral Separator /* [X] */ { if(parseInfo->hasDeclaration(ParserContext::DeclareDefaultElementNamespace)) { parseInfo->staticContext->error(prologMessage("declare default element namespace"), ReportContext::XQST0066, fromYYLTYPE(@$, parseInfo)); } else { parseInfo->staticContext->namespaceBindings()->addBinding(QXmlName(parseInfo->staticContext->namePool()->allocateNamespace($5), StandardLocalNames::empty)); parseInfo->registerDeclaration(ParserContext::DeclareDefaultElementNamespace); } } DeclareDefaultFunctionNamespace: T_DECLARE T_DEFAULT T_FUNCTION T_NAMESPACE URILiteral Separator /* [X] */ { if(parseInfo->hasDeclaration(ParserContext::DeclareDefaultFunctionNamespace)) { parseInfo->staticContext->error(prologMessage("declare default function namespace"), ReportContext::XQST0066, fromYYLTYPE(@$, parseInfo)); } else { parseInfo->staticContext->setDefaultFunctionNamespace($5); parseInfo->registerDeclaration(ParserContext::DeclareDefaultFunctionNamespace); } } OptionDecl: T_DECLARE T_OPTION ElementName StringLiteral Separator /* [13] */ { if($3.prefix() == StandardPrefixes::empty) { parseInfo->staticContext->error(QtXmlPatterns::tr("The name of an option must have a prefix. " "There is no default namespace for options."), ReportContext::XPST0081, fromYYLTYPE(@$, parseInfo)); } } OrderingModeDecl: T_DECLARE T_ORDERING OrderingMode Separator /* [14] */ { allowedIn(QXmlQuery::XQuery10, parseInfo, @$); if(parseInfo->hasDeclaration(ParserContext::OrderingModeDecl)) { parseInfo->staticContext->error(prologMessage("declare ordering"), ReportContext::XQST0065, fromYYLTYPE(@$, parseInfo)); } else { parseInfo->registerDeclaration(ParserContext::OrderingModeDecl); parseInfo->staticContext->setOrderingMode($3); } } OrderingMode: T_ORDERED { $$ = StaticContext::Ordered; } | T_UNORDERED { $$ = StaticContext::Unordered; } EmptyOrderDecl: T_DECLARE T_DEFAULT T_ORDER OrderingEmptySequence Separator /* [15] */ { if(parseInfo->hasDeclaration(ParserContext::EmptyOrderDecl)) { parseInfo->staticContext->error(prologMessage("declare default order"), ReportContext::XQST0069, fromYYLTYPE(@$, parseInfo)); } else { parseInfo->registerDeclaration(ParserContext::EmptyOrderDecl); parseInfo->staticContext->setOrderingEmptySequence($4); } } OrderingEmptySequence: T_EMPTY T_LEAST /* [X] */ { $$ = StaticContext::Least; } | T_EMPTY T_GREATEST { $$ = StaticContext::Greatest; } CopyNamespacesDecl: T_DECLARE T_COPY_NAMESPACES PreserveMode T_COMMA InheritMode Separator /* [16] */ { if(parseInfo->hasDeclaration(ParserContext::CopyNamespacesDecl)) { parseInfo->staticContext->error(prologMessage("declare copy-namespaces"), ReportContext::XQST0055, fromYYLTYPE(@$, parseInfo)); } else { parseInfo->registerDeclaration(ParserContext::CopyNamespacesDecl); } } PreserveMode: T_PRESERVE /* [17] */ { parseInfo->preserveNamespacesMode = true; } | T_NO_PRESERVE { parseInfo->preserveNamespacesMode = false; } InheritMode: T_INHERIT /* [18] */ { parseInfo->inheritNamespacesMode = true; } | T_NO_INHERIT { parseInfo->inheritNamespacesMode = false; } DefaultCollationDecl: T_DECLARE T_DEFAULT T_COLLATION StringLiteral Separator /* [19] */ { if(parseInfo->hasDeclaration(ParserContext::DefaultCollationDecl)) { parseInfo->staticContext->error(prologMessage("declare default collation"), ReportContext::XQST0038, fromYYLTYPE(@$, parseInfo)); } else { const QUrl coll(resolveAndCheckCollation($4, parseInfo, @$)); parseInfo->registerDeclaration(ParserContext::DefaultCollationDecl); parseInfo->staticContext->setDefaultCollation(coll); } } BaseURIDecl: T_DECLARE T_BASEURI IsInternal URILiteral Separator /* [20] */ { allowedIn(QueryLanguages(QXmlQuery::XQuery10 | QXmlQuery::XSLT20), parseInfo, @$, $3); if(parseInfo->hasDeclaration(ParserContext::BaseURIDecl)) { parseInfo->staticContext->error(prologMessage("declare base-uri"), ReportContext::XQST0032, fromYYLTYPE(@$, parseInfo)); } else { parseInfo->registerDeclaration(ParserContext::BaseURIDecl); const ReflectYYLTYPE ryy(@$, parseInfo); QUrl toBeBase(AnyURI::toQUrl($4, parseInfo->staticContext, &ryy)); /* Now we're guaranteed that base is a valid lexical representation, but it can still be relative. */ if(toBeBase.isRelative()) toBeBase = parseInfo->staticContext->baseURI().resolved(toBeBase); parseInfo->staticContext->setBaseURI(toBeBase); } } SchemaImport: T_IMPORT T_SCHEMA SchemaPrefix URILiteral FileLocations Separator /* [21] */ { parseInfo->staticContext->error(QtXmlPatterns::tr("The Schema Import feature is not supported, " "and therefore %1 declarations cannot occur.") .arg(formatKeyword("import schema")), ReportContext::XQST0009, fromYYLTYPE(@$, parseInfo)); } SchemaPrefix: /* empty */ /* [22] */ | T_DEFAULT T_ELEMENT T_NAMESPACE | T_NAMESPACE T_NCNAME T_G_EQ ModuleImport: T_IMPORT T_MODULE ModuleNamespaceDecl URILiteral FileLocations Separator /* [23] */ { if($4.isEmpty()) { parseInfo->staticContext->error(QtXmlPatterns::tr("The target namespace of a %1 cannot be empty.") .arg(formatKeyword("module import")), ReportContext::XQST0088, fromYYLTYPE(@$, parseInfo)); } else { /* This is temporary until we have implemented it. */ parseInfo->staticContext->error(QtXmlPatterns::tr("The module import feature is not supported"), ReportContext::XQST0016, fromYYLTYPE(@$, parseInfo)); } } ModuleNamespaceDecl: /* empty */ /* [X] */ | T_NAMESPACE T_NCNAME T_G_EQ FileLocations: /* empty */ /* [X] */ | T_AT FileLocation FileLocation: URILiteral /* [X] */ | FileLocation T_COMMA URILiteral VarDecl: T_DECLARE T_VARIABLE IsInternal T_DOLLAR VarName TypeDeclaration VariableValue OptionalDefaultValue Separator /* [24] */ { allowedIn(QXmlQuery::XQuery10, parseInfo, @$, $3); if(variableByName($5, parseInfo)) { parseInfo->staticContext->error(QtXmlPatterns::tr("A variable with name %1 has already " "been declared.") .arg(formatKeyword(parseInfo->staticContext->namePool()->toLexical($5))), parseInfo->isXSLT() ? ReportContext::XTSE0630 : ReportContext::XQST0049, fromYYLTYPE(@$, parseInfo)); } else { if($7) /* We got a value assigned. */ { const Expression::Ptr checked (TypeChecker::applyFunctionConversion($7, $6, parseInfo->staticContext, $3 ? ReportContext::XTTE0570 : ReportContext::XPTY0004, $3 ? TypeChecker::Options(TypeChecker::CheckFocus | TypeChecker::AutomaticallyConvert) : TypeChecker::CheckFocus)); pushVariable($5, $6, checked, VariableDeclaration::GlobalVariable, @$, parseInfo); parseInfo->declaredVariables.append(parseInfo->variables.last()); } else /* We got an 'external' declaration. */ { const SequenceType::Ptr varType(parseInfo->staticContext-> externalVariableLoader()->announceExternalVariable($5, $6)); if(varType) { /* We push the declaration such that we can see name clashes and so on, but we don't use it for tying * any references to it. */ pushVariable($5, varType, Expression::Ptr(), VariableDeclaration::ExternalVariable, @$, parseInfo); } else if($8) { /* Ok, the xsl:param got a default value, we make it * available as a regular variable declaration. */ // TODO turn into checked pushVariable($5, $6, $8, VariableDeclaration::GlobalVariable, @$, parseInfo); // TODO ensure that duplicates are trapped. } else { parseInfo->staticContext->error(QtXmlPatterns::tr("No value is available for the external " "variable with name %1.") .arg(formatKeyword(parseInfo->staticContext->namePool(), $5)), parseInfo->isXSLT() ? ReportContext::XTDE0050 : ReportContext::XPDY0002, fromYYLTYPE(@$, parseInfo)); } } } } VariableValue: T_EXTERNAL /* [X] */ { $$.reset(); } | T_ASSIGN ExprSingle { $$ = $2; } OptionalDefaultValue: /* Empty. */ /* [X] */ { $$.reset(); } | T_ASSIGN ExprSingle { $$ = $2; } ConstructionDecl: T_DECLARE T_CONSTRUCTION ConstructionMode Separator /* [25] */ { if(parseInfo->hasDeclaration(ParserContext::ConstructionDecl)) { parseInfo->staticContext->error(prologMessage("declare ordering"), ReportContext::XQST0067, fromYYLTYPE(@$, parseInfo)); } else { parseInfo->registerDeclaration(ParserContext::ConstructionDecl); parseInfo->staticContext->setConstructionMode($3); } } ConstructionMode: T_STRIP /* [X] */ { $$ = StaticContext::CMStrip; } | T_PRESERVE { $$ = StaticContext::CMPreserve; } FunctionDecl: T_DECLARE T_FUNCTION IsInternal FunctionName T_LPAREN ParamList T_RPAREN { $$ = parseInfo->currentExpressionSlot() - $6.count(); } TypeDeclaration FunctionBody Separator /* [26] */ { if(!$3) allowedIn(QXmlQuery::XQuery10, parseInfo, @$, $3); /* If FunctionBody is null, it is 'external', otherwise the value is the body. */ const QXmlName::NamespaceCode ns($4.namespaceURI()); if(parseInfo->isXSLT() && !$4.hasPrefix()) { parseInfo->staticContext->error(QtXmlPatterns::tr("A stylesheet function must have a prefixed name."), ReportContext::XTSE0740, fromYYLTYPE(@$, parseInfo)); } if($10) /* We got a function body. */ { if(ns == StandardNamespaces::empty) { parseInfo->staticContext->error(QtXmlPatterns::tr("The namespace for a user defined function " "cannot be empty (try the predefined " "prefix %1 which exists for cases " "like this)") .arg(formatKeyword("local")), ReportContext::XQST0060, fromYYLTYPE(@$, parseInfo)); } else if(XPathHelper::isReservedNamespace(ns)) { parseInfo->staticContext->error(QtXmlPatterns::tr( "The namespace %1 is reserved; therefore " "user defined functions may not use it. " "Try the predefined prefix %2, which " "exists for these cases.") .arg(formatURI(parseInfo->staticContext->namePool(), ns), formatKeyword("local")), parseInfo->isXSLT() ? ReportContext::XTSE0080 : ReportContext::XQST0045, fromYYLTYPE(@$, parseInfo)); } else if(parseInfo->moduleNamespace != StandardNamespaces::empty && ns != parseInfo->moduleNamespace) { parseInfo->staticContext->error(QtXmlPatterns::tr( "The namespace of a user defined " "function in a library module must be " "equivalent to the module namespace. " "In other words, it should be %1 instead " "of %2") .arg(formatURI(parseInfo->staticContext->namePool(), parseInfo->moduleNamespace), formatURI(parseInfo->staticContext->namePool(), ns)), ReportContext::XQST0048, fromYYLTYPE(@$, parseInfo)); } else { /* Apply function conversion such that the body matches the declared * return type. */ const Expression::Ptr checked(TypeChecker::applyFunctionConversion($10, $9, parseInfo->staticContext, ReportContext::XPTY0004, TypeChecker::Options(TypeChecker::AutomaticallyConvert | TypeChecker::CheckFocus | TypeChecker::GeneratePromotion))); const int argCount = $6.count(); const FunctionSignature::Ptr sign(new FunctionSignature($4 /* name */, argCount /* minArgs */, argCount /* maxArgs */, $9 /* returnType */)); sign->setArguments($6); const UserFunction::List::const_iterator end(parseInfo->userFunctions.constEnd()); UserFunction::List::const_iterator it(parseInfo->userFunctions.constBegin()); for(; it != end; ++it) { if(*(*it)->signature() == *sign) { parseInfo->staticContext->error(QtXmlPatterns::tr("A function already exists with " "the signature %1.") .arg(formatFunction(parseInfo->staticContext->namePool(), sign)), parseInfo->isXSLT() ? ReportContext::XTSE0770 : ReportContext::XQST0034, fromYYLTYPE(@$, parseInfo)); } } VariableDeclaration::List argDecls; for(int i = 0; i < argCount; ++i) argDecls.append(parseInfo->variables.at(i)); if($8 > -1) { /* We have allocated slots, so now push them out of scope. */ parseInfo->finalizePushedVariable(argCount); } parseInfo->userFunctions.append(UserFunction::Ptr(new UserFunction(sign, checked, $8, argDecls))); } } else /* We got an 'external' declaration. */ { parseInfo->staticContext->error(QtXmlPatterns::tr("No external functions are supported. " "All supported functions can be used directly, " "without first declaring them as external"), ReportContext::XPST0017, fromYYLTYPE(@$, parseInfo)); } } ParamList: /* empty */ /* [27] */ { $$ = FunctionArgument::List(); } | Param { FunctionArgument::List l; l.append($1); $$ = l; } | ParamList T_COMMA Param { FunctionArgument::List::const_iterator it($1.constBegin()); const FunctionArgument::List::const_iterator end($1.constEnd()); for(; it != end; ++it) { if((*it)->name() == $3->name()) { parseInfo->staticContext->error(QtXmlPatterns::tr("An argument with name %1 has already " "been declared. Every argument name " "must be unique.") .arg(formatKeyword(parseInfo->staticContext->namePool(), $3->name())), ReportContext::XQST0039, fromYYLTYPE(@$, parseInfo)); } } $1.append($3); $$ = $1; } Param: T_DOLLAR VarName TypeDeclaration /* [28] */ { pushVariable($2, $3, Expression::Ptr(), VariableDeclaration::FunctionArgument, @$, parseInfo); $$ = FunctionArgument::Ptr(new FunctionArgument($2, $3)); } FunctionBody: T_EXTERNAL /* [X] */ { $$.reset(); } | EnclosedExpr EnclosedExpr: T_CURLY_LBRACE Expr T_CURLY_RBRACE /* [29] */ { $$ = $2; } QueryBody: Expr /* [30] */ /** * A pattern as found in for instance xsl:template/@match. * * @note When using this pattern, remember to set ParserContext::isParsingPattern. * * @see XSL Transformations * (XSLT) Version 2.0, 5.5.2 Syntax of Patterns */ Pattern: PathPattern /* [XSLT20-1] */ | Pattern T_BAR PathPattern { $$ = create(new CombineNodes($1, CombineNodes::Union, $3), @$, parseInfo); } PathPattern: RelativePathPattern /* [XSLT20-2] */ | T_SLASH { /* We write this into a node test. The spec says, 5.5.3 The Meaning of a Pattern: * "Similarly, / matches a document node, and only a document node, * because the result of the expression root(.)//(/) returns the root * node of the tree containing the context node if and only if it is a * document node." */ $$ = create(new AxisStep(QXmlNodeModelIndex::AxisSelf, BuiltinTypes::document), @$, parseInfo); } | T_SLASH RelativePathPattern { /* /axis::node-test * => * axis::node-test[parent::document-node()] * * In practice it looks like this. $2 is: * * TruthPredicate * AxisStep self::element(c) * TruthPredicate * AxisStep parent::element(b) * AxisStep parent::element(a) * * and we want this: * * TruthPredicate * AxisStep self::element(c) * TruthPredicate * AxisStep self::element(b) * TruthPredicate * AxisStep parent::element(a) * AxisStep parent::document() * * So we want to rewrite the predicate deepest down into a * another TruthPredicate containing the AxisStep. * * The simplest case where $2 is only an axis step is special. When $2 is: * * AxisStep self::element(a) * * we want: * * TruthPredicate * AxisStep self::element(a) * AxisStep parent::document() */ /* First, find the target. */ Expression::Ptr target($2); while(isPredicate(target->id())) { const Expression::Ptr candidate(target->operands().at(1)); if(isPredicate(candidate->id())) target = candidate; else break; /* target is now the last predicate. */ } if(target->is(Expression::IDAxisStep)) { $$ = create(GenericPredicate::create($2, create(new AxisStep(QXmlNodeModelIndex::AxisParent, BuiltinTypes::document), @$, parseInfo), parseInfo->staticContext, fromYYLTYPE(@1, parseInfo)), @1, parseInfo); } else { const Expression::List targetOperands(target->operands()); Expression::List newOps; newOps.append(targetOperands.at(0)); newOps.append(create(GenericPredicate::create(targetOperands.at(1), create(new AxisStep(QXmlNodeModelIndex::AxisParent, BuiltinTypes::document), @$, parseInfo), parseInfo->staticContext, fromYYLTYPE(@1, parseInfo)), @1, parseInfo)); target->setOperands(newOps); $$ = $2; } } | T_SLASHSLASH RelativePathPattern { /* //axis::node-test * => * axis::node-test[parent::node()] * * Spec says: "//para matches any para element that has a parent node." */ $$ = create(GenericPredicate::create($2, create(new AxisStep(QXmlNodeModelIndex::AxisParent, BuiltinTypes::node), @$, parseInfo), parseInfo->staticContext, fromYYLTYPE(@1, parseInfo)), @1, parseInfo); } | IdKeyPattern | IdKeyPattern T_SLASH RelativePathPattern { createIdPatternPath($1, $3, QXmlNodeModelIndex::AxisParent, @2, parseInfo); } | IdKeyPattern T_SLASHSLASH RelativePathPattern { createIdPatternPath($1, $3, QXmlNodeModelIndex::AxisAncestor, @2, parseInfo); } IdKeyPattern: FunctionCallExpr { const Expression::List ands($1->operands()); const FunctionSignature::Ptr signature($1->as()->signature()); const QXmlName name(signature->name()); const QXmlName key(StandardNamespaces::fn, StandardLocalNames::key); const QXmlName id(StandardNamespaces::fn, StandardLocalNames::id); if(name == id) { const Expression::ID id = ands.first()->id(); if(!isVariableReference(id) && id != Expression::IDStringValue) { parseInfo->staticContext->error(QtXmlPatterns::tr("When function %1 is used for matching inside a pattern, " "the argument must be a variable reference or a string literal.") .arg(formatFunction(parseInfo->staticContext->namePool(), signature)), ReportContext::XPST0003, fromYYLTYPE(@$, parseInfo)); } } else if(name == key) { if(ands.first()->id() != Expression::IDStringValue) { parseInfo->staticContext->error(QtXmlPatterns::tr("In an XSL-T pattern, the first argument to function %1 " "must be a string literal, when used for matching.") .arg(formatFunction(parseInfo->staticContext->namePool(), signature)), ReportContext::XPST0003, fromYYLTYPE(@$, parseInfo)); } const Expression::ID id2 = ands.at(1)->id(); if(!isVariableReference(id2) && id2 != Expression::IDStringValue && id2 != Expression::IDIntegerValue && id2 != Expression::IDBooleanValue && id2 != Expression::IDFloat) { parseInfo->staticContext->error(QtXmlPatterns::tr("In an XSL-T pattern, the first argument to function %1 " "must be a literal or a variable reference, when used for matching.") .arg(formatFunction(parseInfo->staticContext->namePool(), signature)), ReportContext::XPST0003, fromYYLTYPE(@$, parseInfo)); } if(ands.count() == 3) { parseInfo->staticContext->error(QtXmlPatterns::tr("In an XSL-T pattern, function %1 cannot have a third argument.") .arg(formatFunction(parseInfo->staticContext->namePool(), signature)), ReportContext::XPST0003, fromYYLTYPE(@$, parseInfo)); } } else { const FunctionSignature::Hash signs(parseInfo->staticContext->functionSignatures()->functionSignatures()); parseInfo->staticContext->error(QtXmlPatterns::tr("In an XSL-T pattern, only function %1 " "and %2, not %3, can be used for matching.") .arg(formatFunction(parseInfo->staticContext->namePool(), signs.value(id)), formatFunction(parseInfo->staticContext->namePool(), signs.value(key)), formatFunction(parseInfo->staticContext->namePool(), signature)), ReportContext::XPST0003, fromYYLTYPE(@$, parseInfo)); } $$ = $1; } RelativePathPattern: PatternStep /* [XSLT20-3] */ | RelativePathPattern T_SLASH PatternStep { $$ = createPatternPath($1, $3, QXmlNodeModelIndex::AxisParent, @2, parseInfo); } | RelativePathPattern T_SLASHSLASH PatternStep { $$ = createPatternPath($1, $3, QXmlNodeModelIndex::AxisAncestor, @2, parseInfo); } PatternStep: FilteredAxisStep { const Expression::Ptr expr(findAxisStep($1)); const QXmlNodeModelIndex::Axis axis = expr->as()->axis(); AxisStep *const axisStep = expr->as(); /* Here we constrain the possible axes, and we rewrite the axes as according * to 5.5.3 The Meaning of a Pattern. * * However, we also rewrite axis child and attribute to axis self. The * reason for this is that if we don't, we will match the children of * the context node, instead of the context node itself. The formal * definition of a pattern, root(.)//EE is insensitive to context, * while the way we implement pattern, "the other way of seeing it", * e.g from right to left, are very much. */ if(axisStep->nodeTest() == BuiltinTypes::document || axis == QXmlNodeModelIndex::AxisChild) axisStep->setAxis(QXmlNodeModelIndex::AxisSelf); else if(axis == QXmlNodeModelIndex::AxisAttribute) { axisStep->setAxis(QXmlNodeModelIndex::AxisSelf); /* Consider that the user write attribute::node(). This is * semantically equivalent to attribute::attribute(), but since we have changed * the axis to axis self, we also need to change the node test, such that we * have self::attribute(). */ if(*axisStep->nodeTest() == *BuiltinTypes::node) axisStep->setNodeTest(BuiltinTypes::attribute); } else { parseInfo->staticContext->error(QtXmlPatterns::tr("In an XSL-T pattern, axis %1 cannot be used, " "only axis %2 or %3 can.") .arg(formatKeyword(AxisStep::axisName(axis)), formatKeyword(AxisStep::axisName(QXmlNodeModelIndex::AxisChild)), formatKeyword(AxisStep::axisName(QXmlNodeModelIndex::AxisAttribute))), ReportContext::XPST0003, fromYYLTYPE(@$, parseInfo)); } $$ = $1; } Expr: ExprSingle /* [31] */ | ExpressionSequence { $$ = create(new ExpressionSequence($1), @$, parseInfo); } ExpressionSequence: ExprSingle T_COMMA ExprSingle /* [X] */ { Expression::List l; l.append($1); l.append($3); $$ = l; } | ExpressionSequence T_COMMA ExprSingle { $1.append($3); $$ = $1; } ExprSingle: OrExpr /* [32] */ | FLWORExpr | QuantifiedExpr | TypeswitchExpr | IfExpr | T_AVT T_LPAREN AttrValueContent T_RPAREN { $$ = createDirAttributeValue($3, parseInfo, @$); } OptionalModes: /* Empty. */ /* [X] */ { QVector result; result.append(QXmlName(StandardNamespaces::InternalXSLT, StandardLocalNames::Default)); $$ = result; } | T_MODE Modes { $$ = $2; } OptionalMode: /* Empty. */ /* [X] */ { $$ = QXmlName(StandardNamespaces::InternalXSLT, StandardLocalNames::Default); } | T_MODE Mode { $$ = $2; } Modes: Mode { QVector result; result.append($1); $$ = result; } | Modes T_COMMA Mode { $1.append($3); $$ = $1; } Mode: QName /* [X] */ { $$ = $1; } | T_NCNAME { if($1 == QLatin1String("#current")) $$ = QXmlName(StandardNamespaces::InternalXSLT, StandardLocalNames::current); else if($1 == QLatin1String("#default")) $$ = QXmlName(StandardNamespaces::InternalXSLT, StandardLocalNames::Default); else if($1 == QLatin1String("#all")) $$ = QXmlName(StandardNamespaces::InternalXSLT, StandardLocalNames::all); else { const ReflectYYLTYPE ryy(@$, parseInfo); if(!QXmlUtils::isNCName($1)) { parseInfo->staticContext->error(QtXmlPatterns::tr("%1 is an invalid template mode name.") .arg(formatKeyword($1)), ReportContext::XTSE0550, fromYYLTYPE(@$, parseInfo)); } $$ = parseInfo->staticContext->namePool()->allocateQName(StandardNamespaces::empty, $1); } } FLWORExpr: ForClause /* [33] */ | LetClause ForClause: T_FOR T_DOLLAR VarName TypeDeclaration PositionalVar T_IN ExprSingle { /* We're pushing the range variable here, not the positional. */ $$ = pushVariable($3, quantificationType($4), $7, VariableDeclaration::RangeVariable, @$, parseInfo); } { /* It is ok this appears after PositionalVar, because currentRangeSlot() * uses a different "channel" than currentPositionSlot(), so they can't trash * each other. */ $$ = parseInfo->staticContext->currentRangeSlot(); } ForTail /* [34] */ { Q_ASSERT($7); Q_ASSERT($10); /* We want the next last pushed variable, since we push the range variable after the * positional variable. */ if($5 != -1 && parseInfo->variables.at(parseInfo->variables.count() -2)->name == $3) { /* Ok, a positional variable is used since its slot is not -1, and its name is equal * to our range variable. This is an error. */ parseInfo->staticContext->error(QtXmlPatterns::tr("The name of a variable bound in a for-expression must be different " "from the positional variable. Hence, the two variables named %1 collide.") .arg(formatKeyword(parseInfo->staticContext->namePool(), $3)), ReportContext::XQST0089, fromYYLTYPE(@$, parseInfo)); } const Expression::Ptr retBody(create(new ForClause($9, $8, $10, $5), @$, parseInfo)); ReturnOrderBy *const rob = locateReturnClause($10); if(rob) $$ = create(new OrderBy(rob->stability(), rob->orderSpecs(), retBody, rob), @$, parseInfo); else $$ = retBody; parseInfo->finalizePushedVariable(); if($5 != -1) /* We also have a positional variable to remove from the scope. */ parseInfo->finalizePushedVariable(); } ForTail: T_COMMA T_DOLLAR VarName TypeDeclaration PositionalVar T_IN ExprSingle { pushVariable($3, quantificationType($4), $7, VariableDeclaration::RangeVariable, @$, parseInfo); } { /* It is ok this appears after PositionalVar, because currentRangeSlot() * uses a different "channel" than currentPositionSlot(), so they can't trash * each other. */ $$ = parseInfo->staticContext->currentRangeSlot(); } ForTail /* [X] */ { $$ = create(new ForClause($9, $7, $10, $5), @$, parseInfo); parseInfo->finalizePushedVariable(); if($5 != -1) /* We also have a positional variable to remove from the scope. */ parseInfo->finalizePushedVariable(); } | WhereClause | ForClause | LetClause PositionalVar: /* empty */ /* [35] */ { $$ = -1; } | T_AT T_DOLLAR VarName { pushVariable($3, CommonSequenceTypes::ExactlyOneInteger, Expression::Ptr(), VariableDeclaration::PositionalVariable, @$, parseInfo); $$ = parseInfo->currentPositionSlot(); } LetClause: T_LET IsInternal T_DOLLAR VarName TypeDeclaration T_ASSIGN ExprSingle { $$ = pushVariable($4, quantificationType($5), $7, VariableDeclaration::ExpressionVariable, @$, parseInfo); } LetTail /* [36] */ { allowedIn(QXmlQuery::XQuery10, parseInfo, @$, $2); Q_ASSERT(parseInfo->variables.top()->name == $4); $$ = create(new LetClause($8, $9, parseInfo->variables.top()), @$, parseInfo); parseInfo->finalizePushedVariable(); } LetTail: T_COMMA T_DOLLAR VarName TypeDeclaration T_ASSIGN ExprSingle { $$ = pushVariable($3, quantificationType($4), $6, VariableDeclaration::ExpressionVariable, @$, parseInfo);} LetTail /* [X] */ { Q_ASSERT(parseInfo->variables.top()->name == $3); $$ = create(new LetClause($7, $8, parseInfo->variables.top()), @$, parseInfo); parseInfo->finalizePushedVariable(); } | WhereClause | ForClause | LetClause WhereClause: OrderByClause T_RETURN ExprSingle /* [37] */ { if($1.isEmpty()) $$ = $3; else $$ = createReturnOrderBy($1, $3, parseInfo->orderStability.pop(), @$, parseInfo); } | T_WHERE ExprSingle OrderByClause T_RETURN ExprSingle { if($3.isEmpty()) $$ = create(new IfThenClause($2, $5, create(new EmptySequence, @$, parseInfo)), @$, parseInfo); else $$ = create(new IfThenClause($2, createReturnOrderBy($3, $5, parseInfo->orderStability.pop(), @$, parseInfo), create(new EmptySequence, @$, parseInfo)), @$, parseInfo); } OrderByClause: /* Empty. */ /* [38] */ { $$ = OrderSpecTransfer::List(); } | MandatoryOrderByClause MandatoryOrderByClause: OrderByInputOrder OrderSpecList { $$ = $2; } OrderSpecList: OrderSpecList T_COMMA OrderSpec /* [39] */ { OrderSpecTransfer::List list; list += $1; list.append($3); $$ = list; } | OrderSpec { OrderSpecTransfer::List list; list.append($1); $$ = list; } OrderSpec: ExprSingle DirectionModifier EmptynessModifier CollationModifier /* [40] */ { $$ = OrderSpecTransfer($1, OrderBy::OrderSpec($2, $3)); } DirectionModifier: /* Empty. */ /* [X] */ { /* Where does the specification state the default value is ascending? * * It is implicit, in the first enumerated list in 3.8.3 Order By and Return Clauses: * * "If T1 and T2 are two tuples in the tuple stream, and V1 and V2 are the first pair * of values encountered when evaluating their orderspecs from left to right for * which one value is greater-than the other (as defined above), then: * * 1. If V1 is greater-than V2: If the orderspec specifies descending, * then T1 precedes T2 in the tuple stream; otherwise, T2 precedes T1 in the tuple stream. * 2. If V2 is greater-than V1: If the orderspec specifies descending, * then T2 precedes T1 in the tuple stream; otherwise, T1 precedes T2 in the tuple stream." * * which means that if you don't specify anything, or you * specify ascending, you get the same result. */ $$ = OrderBy::OrderSpec::Ascending; } | T_ASCENDING { $$ = OrderBy::OrderSpec::Ascending; } | T_DESCENDING { $$ = OrderBy::OrderSpec::Descending; } EmptynessModifier: /* Empty. */ /* [X] */ { $$ = parseInfo->staticContext->orderingEmptySequence(); } | OrderingEmptySequence CollationModifier: /* Empty. */ /* [X] */ | T_COLLATION URILiteral { if(parseInfo->isXSLT()) resolveAndCheckCollation($2, parseInfo, @$); else resolveAndCheckCollation($2, parseInfo, @$); } | T_INTERNAL T_COLLATION ExprSingle { /* We do nothing. We don't use collations, and we have this non-terminal * in order to accept expressions. */ } OrderByInputOrder: T_STABLE T_ORDER T_BY /* [X] */ { parseInfo->orderStability.push(OrderBy::StableOrder); } | T_ORDER T_BY { parseInfo->orderStability.push(OrderBy::UnstableOrder); } QuantifiedExpr: SomeQuantificationExpr /* [42] */ | EveryQuantificationExpr SomeQuantificationExpr: T_SOME T_DOLLAR VarName TypeDeclaration T_IN ExprSingle { pushVariable($3, quantificationType($4), $6, VariableDeclaration::RangeVariable, @$, parseInfo); } {$$ = parseInfo->staticContext->currentRangeSlot();} SomeQuantificationTail /* [X] */ { allowedIn(QueryLanguages(QXmlQuery::XQuery10 | QXmlQuery::XPath20), parseInfo, @$); $$ = create(new QuantifiedExpression($8, QuantifiedExpression::Some, $6, $9), @$, parseInfo); parseInfo->finalizePushedVariable(); } SomeQuantificationTail: T_COMMA T_DOLLAR VarName TypeDeclaration T_IN ExprSingle { $$ = pushVariable($3, quantificationType($4), $6, VariableDeclaration::RangeVariable, @$, parseInfo); } {$$ = parseInfo->staticContext->currentRangeSlot();} SomeQuantificationTail /* [X] */ { $$ = create(new QuantifiedExpression($8, QuantifiedExpression::Some, $7, $9), @$, parseInfo); parseInfo->finalizePushedVariable(); } | SatisfiesClause EveryQuantificationExpr: T_EVERY T_DOLLAR VarName TypeDeclaration T_IN ExprSingle { pushVariable($3, quantificationType($4), $6, VariableDeclaration::RangeVariable, @$, parseInfo); } {$$ = parseInfo->staticContext->currentRangeSlot();} EveryQuantificationTail /* [X] */ { allowedIn(QueryLanguages(QXmlQuery::XQuery10 | QXmlQuery::XPath20), parseInfo, @$); $$ = create(new QuantifiedExpression($8, QuantifiedExpression::Every, $6, $9), @$, parseInfo); parseInfo->finalizePushedVariable(); } EveryQuantificationTail: T_COMMA T_DOLLAR VarName TypeDeclaration T_IN ExprSingle { $$ = pushVariable($3, quantificationType($4), $6, VariableDeclaration::RangeVariable, @$, parseInfo); } {$$ = parseInfo->staticContext->currentRangeSlot();} EveryQuantificationTail /* [X] */ { $$ = create(new QuantifiedExpression($8, QuantifiedExpression::Every, $7, $9), @$, parseInfo); parseInfo->finalizePushedVariable(); } | SatisfiesClause SatisfiesClause: T_SATISFIES ExprSingle /* [X] */ { $$ = $2; } /* * Typeswitches are re-written to a combination between @c if clauses, instance of, and * @c let bindings. For example, the query: * * @code * typeswitch(input) * case element() return * case $i as attribute(name) return name($i) * default return "Didn't match" * @endcode * * becomes: * * @code * if(input instance of element()) * then * else if(input instance of attribute(name)) * then let $i as attribute(name) := input return name($i) * else "Didn't match" * @endcode */ TypeswitchExpr: T_TYPESWITCH T_LPAREN Expr T_RPAREN { parseInfo->typeswitchSource.push($3); } CaseClause /* [43] */ { allowedIn(QXmlQuery::XQuery10, parseInfo, @$); parseInfo->typeswitchSource.pop(); $$ = $6; } CaseClause: T_CASE CaseVariable SequenceType /* [44] */ { if(!$2.isNull()) { pushVariable($2, $3, parseInfo->typeswitchSource.top(), VariableDeclaration::ExpressionVariable, @$, parseInfo, false); } } T_RETURN ExprSingle { /* The variable shouldn't be in-scope for other case branches. */ if(!$2.isNull()) parseInfo->finalizePushedVariable(); } CaseTail { const Expression::Ptr instanceOf(create(new InstanceOf(parseInfo->typeswitchSource.top(), $3), @$, parseInfo)); $$ = create(new IfThenClause(instanceOf, $6, $8), @$, parseInfo); } CaseTail: CaseClause /* [X] */ | CaseDefault CaseVariable: /* Empty. */ /* [X] */ { $$ = QXmlName(); } | T_DOLLAR ElementName T_AS { $$ = $2; } CaseDefault: T_DEFAULT T_RETURN ExprSingle /* [X] */ { $$ = $3; } | T_DEFAULT T_DOLLAR ElementName { if(!$3.isNull()) { pushVariable($3, parseInfo->typeswitchSource.top()->staticType(), parseInfo->typeswitchSource.top(), VariableDeclaration::ExpressionVariable, @$, parseInfo, false); } } T_RETURN ExprSingle { if(!$3.isNull()) parseInfo->finalizePushedVariable(); $$ = $6; } IfExpr: T_IF T_LPAREN Expr T_RPAREN T_THEN ExprSingle T_ELSE ExprSingle /* [45] */ { allowedIn(QueryLanguages(QXmlQuery::XQuery10 | QXmlQuery::XPath20), parseInfo, @$); $$ = create(new IfThenClause($3, $6, $8), @$, parseInfo); } OrExpr: AndExpr /* [46] */ | OrExpr T_OR AndExpr { allowedIn(QueryLanguages(QXmlQuery::XQuery10 | QXmlQuery::XPath20), parseInfo, @$); $$ = create(new OrExpression($1, $3), @$, parseInfo); } AndExpr: ComparisonExpr /* [47] */ | AndExpr T_AND ComparisonExpr { allowedIn(QueryLanguages(QXmlQuery::XQuery10 | QXmlQuery::XPath20), parseInfo, @$); $$ = create(new AndExpression($1, $3), @$, parseInfo); } ComparisonExpr: RangeExpr /* [48] */ | ValueComp | GeneralComp | NodeComp RangeExpr: AdditiveExpr /* [49] */ | AdditiveExpr T_TO AdditiveExpr { allowedIn(QueryLanguages(QXmlQuery::XQuery10 | QXmlQuery::XPath20), parseInfo, @$); $$ = create(new RangeExpression($1, $3), @$, parseInfo); } AdditiveExpr: MultiplicativeExpr /* [50] */ | AdditiveExpr AdditiveOperator MultiplicativeExpr { allowedIn(QueryLanguages(QXmlQuery::XQuery10 | QXmlQuery::XPath20), parseInfo, @$); $$ = create(new ArithmeticExpression($1, $2, $3), @$, parseInfo); } AdditiveOperator: T_PLUS {$$ = AtomicMathematician::Add;} /* [X] */ | T_MINUS {$$ = AtomicMathematician::Substract;} MultiplicativeExpr: UnionExpr /* [51] */ | MultiplicativeExpr MultiplyOperator UnionExpr { allowedIn(QueryLanguages(QXmlQuery::XQuery10 | QXmlQuery::XPath20), parseInfo, @$); $$ = create(new ArithmeticExpression($1, $2, $3), @$, parseInfo); } MultiplyOperator: T_STAR {$$ = AtomicMathematician::Multiply;} /* [X] */ | T_DIV {$$ = AtomicMathematician::Div;} | T_IDIV {$$ = AtomicMathematician::IDiv;} | T_MOD {$$ = AtomicMathematician::Mod;} UnionExpr: IntersectExceptExpr /* [52] */ | UnionExpr UnionOperator IntersectExceptExpr { allowedIn(QueryLanguages(QXmlQuery::XQuery10 | QXmlQuery::XPath20 | QXmlQuery::XmlSchema11IdentityConstraintField | QXmlQuery::XmlSchema11IdentityConstraintSelector), parseInfo, @$); $$ = create(new CombineNodes($1, CombineNodes::Union, $3), @$, parseInfo); } IntersectExceptExpr: InstanceOfExpr /* [53] */ | IntersectExceptExpr IntersectOperator InstanceOfExpr { allowedIn(QueryLanguages(QXmlQuery::XQuery10 | QXmlQuery::XPath20), parseInfo, @$); $$ = create(new CombineNodes($1, $2, $3), @$, parseInfo); } UnionOperator: T_UNION /* [X] */ | T_BAR IntersectOperator: T_INTERSECT /* [X] */ { $$ = CombineNodes::Intersect; } | T_EXCEPT { $$ = CombineNodes::Except; } InstanceOfExpr: TreatExpr /* [54] */ | TreatExpr T_INSTANCE T_OF SequenceType { allowedIn(QueryLanguages(QXmlQuery::XQuery10 | QXmlQuery::XPath20), parseInfo, @$); $$ = create(new InstanceOf($1, SequenceType::Ptr($4)), @$, parseInfo); } TreatExpr: CastableExpr /* [55] */ | CastableExpr T_TREAT T_AS SequenceType { allowedIn(QueryLanguages(QXmlQuery::XQuery10 | QXmlQuery::XPath20), parseInfo, @$); $$ = create(new TreatAs($1, $4), @$, parseInfo); } CastableExpr: CastExpr /* [56] */ | CastExpr T_CASTABLE T_AS SingleType { allowedIn(QueryLanguages(QXmlQuery::XQuery10 | QXmlQuery::XPath20), parseInfo, @$); $$ = create(new CastableAs($1, $4), @$, parseInfo); } CastExpr: UnaryExpr /* [57] */ | UnaryExpr T_CAST T_AS SingleType { allowedIn(QueryLanguages(QXmlQuery::XQuery10 | QXmlQuery::XPath20), parseInfo, @$); $$ = create(new CastAs($1, $4), @$, parseInfo); } UnaryExpr: ValueExpr /* [58] */ | UnaryOperator UnaryExpr { allowedIn(QueryLanguages(QXmlQuery::XQuery10 | QXmlQuery::XPath20), parseInfo, @$); $$ = create(new UnaryExpression($1, $2, parseInfo->staticContext), @$, parseInfo); } UnaryOperator: T_PLUS /* [X] */ { $$ = AtomicMathematician::Add; } | T_MINUS { $$ = AtomicMathematician::Substract; } ValueExpr: ValidateExpr /* [59] */ | PathExpr | ExtensionExpr GeneralComp: RangeExpr GeneralComparisonOperator RangeExpr /* [60] */ { allowedIn(QueryLanguages(QXmlQuery::XQuery10 | QXmlQuery::XPath20), parseInfo, @$); $$ = create(new GeneralComparison($1, $2, $3, parseInfo->isBackwardsCompat.top()), @$, parseInfo); } GeneralComparisonOperator: T_G_EQ {$$ = AtomicComparator::OperatorEqual;} /* [X] */ | T_G_NE {$$ = AtomicComparator::OperatorNotEqual;} | T_G_GE {$$ = AtomicComparator::OperatorGreaterOrEqual;} | T_G_GT {$$ = AtomicComparator::OperatorGreaterThan;} | T_G_LE {$$ = AtomicComparator::OperatorLessOrEqual;} | T_G_LT {$$ = AtomicComparator::OperatorLessThan;} ValueComp: RangeExpr ValueComparisonOperator RangeExpr /* [61] */ { $$ = create(new ValueComparison($1, $2, $3), @$, parseInfo); } ValueComparisonOperator: T_EQ {$$ = AtomicComparator::OperatorEqual;} | T_NE {$$ = AtomicComparator::OperatorNotEqual;} | T_GE {$$ = AtomicComparator::OperatorGreaterOrEqual;} | T_GT {$$ = AtomicComparator::OperatorGreaterThan;} | T_LE {$$ = AtomicComparator::OperatorLessOrEqual;} | T_LT {$$ = AtomicComparator::OperatorLessThan;} NodeComp: RangeExpr NodeOperator RangeExpr /* [62] */ { $$ = create(new NodeComparison($1, $2, $3), @$, parseInfo); } NodeOperator: T_IS {$$ = QXmlNodeModelIndex::Is;} /* [X] */ | T_PRECEDES {$$ = QXmlNodeModelIndex::Precedes;} | T_FOLLOWS {$$ = QXmlNodeModelIndex::Follows;} ValidateExpr: ValidationMode EnclosedExpr /* [63] */ { allowedIn(QXmlQuery::XQuery10, parseInfo, @$); parseInfo->staticContext->error(QtXmlPatterns::tr("The Schema Validation Feature is not supported. " "Hence, %1-expressions may not be used.") .arg(formatKeyword("validate")), ReportContext::XQST0075, fromYYLTYPE(@$, parseInfo)); /* $$ = Validate::create($2, $1, parseInfo->staticContext); */ } /* "A validate expression may optionally specify a validation mode. The default validation mode is strict." */ ValidationMode: T_VALIDATE {$$ = Validate::Strict;} /* [64] */ | T_VALIDATE T_STRICT {$$ = Validate::Strict;} | T_VALIDATE T_LAX {$$ = Validate::Lax;} ExtensionExpr: Pragmas EnclosedOptionalExpr /* [65] */ { allowedIn(QXmlQuery::XQuery10, parseInfo, @$); /* We don't support any pragmas, so we only do the * necessary validation and use the fallback expression. */ if($2) $$ = $2; else { parseInfo->staticContext->error(QtXmlPatterns::tr("None of the pragma expressions are supported. " "Therefore, a fallback expression " "must be present"), ReportContext::XQST0079, fromYYLTYPE(@$, parseInfo)); } } EnclosedOptionalExpr: T_CURLY_LBRACE /* empty */ T_CURLY_RBRACE /* [X] */ { $$.reset(); } | T_CURLY_LBRACE Expr T_CURLY_RBRACE { $$ = $2; } Pragmas: Pragmas Pragma /* [X] */ | Pragma Pragma: T_PRAGMA_START PragmaName PragmaContents T_PRAGMA_END /* [66] */ { allowedIn(QXmlQuery::XQuery10, parseInfo, @$); } PragmaContents: /* empty */ /* [67] */ | StringLiteral PathExpr: T_SLASH RelativePathExpr /* [68] */ { /* This is "/step". That is, fn:root(self::node()) treat as document-node()/RelativePathExpr. */ $$ = create(new Path(createRootExpression(parseInfo, @$), $2), @$, parseInfo); } | T_SLASHSLASH RelativePathExpr { $$ = createSlashSlashPath(createRootExpression(parseInfo, @$), $2, @$, parseInfo); } | T_SLASH { /* This is "/". That is, fn:root(self::node()) treat as document-node(). */ $$ = createRootExpression(parseInfo, @$); } | RelativePathExpr /* This is "step", simply. We let bison generate "$$ = $1". */ RelativePathExpr: StepExpr /* [69] */ | RelativePathExpr MapOrSlash StepExpr { $$ = create(new Path($1, $3, $2), @$, parseInfo); } | RelativePathExpr MapOrSlash T_SORT MandatoryOrderByClause T_RETURN StepExpr T_END_SORT { const Expression::Ptr orderBy(createReturnOrderBy($4, $6, parseInfo->orderStability.pop(), @$, parseInfo)); ReturnOrderBy *const rob = orderBy->as(); const Expression::Ptr path(create(new Path($1, orderBy, $2), @$, parseInfo)); $$ = create(new OrderBy(rob->stability(), rob->orderSpecs(), path, rob), @$, parseInfo); } | RelativePathExpr T_SLASHSLASH StepExpr { $$ = createSlashSlashPath($1, $3, @$, parseInfo); } StepExpr: FilteredAxisStep /* [70] */ { $$ = NodeSortExpression::wrapAround($1, parseInfo->staticContext); } | FilterExpr | T_CURRENT EnclosedExpr { $$ = create(new CurrentItemStore($2), @$, parseInfo); } | T_XSLT_VERSION { const xsDouble version = $1.toDouble(); parseInfo->isBackwardsCompat.push(version != 2); $$ = version; } EnclosedExpr { if($2 < 2) $$ = createCompatStore($3, @$, parseInfo); else $$ = $3; } | T_BASEURI StringLiteral T_CURLY_LBRACE Expr T_CURLY_RBRACE /* [X] */ { allowedIn(QXmlQuery::XSLT20, parseInfo, @$); Q_ASSERT(!$2.isEmpty()); $$ = create(new StaticBaseURIStore($2, $4), @$, parseInfo); } | T_DECLARE T_NAMESPACE T_NCNAME T_G_EQ T_STRING_LITERAL T_CURLY_LBRACE /* [X] */ { allowedIn(QueryLanguages(QXmlQuery::XQuery10 | QXmlQuery::XSLT20), parseInfo, @$); parseInfo->resolvers.push(parseInfo->staticContext->namespaceBindings()); const NamespaceResolver::Ptr resolver(new DelegatingNamespaceResolver(parseInfo->staticContext->namespaceBindings())); resolver->addBinding(QXmlName(parseInfo->staticContext->namePool()->allocateNamespace($5), StandardLocalNames::empty, parseInfo->staticContext->namePool()->allocatePrefix($3))); parseInfo->staticContext->setNamespaceBindings(resolver); } Expr T_CURLY_RBRACE { parseInfo->staticContext->setNamespaceBindings(parseInfo->resolvers.pop()); $$ = $8; } | T_CALL_TEMPLATE ElementName T_LPAREN TemplateWithParameters T_RPAREN { $$ = create(new CallTemplate($2, parseInfo->templateWithParams), @$, parseInfo); parseInfo->templateWithParametersHandled(); parseInfo->templateCalls.append($$); } TemplateWithParameters: { parseInfo->startParsingWithParam(); } TemplateParameters { parseInfo->endParsingWithParam(); } TemplateParameters: /* Empty. */ /* [X] */ { } | TemplateParameter { } | TemplateParameters T_COMMA TemplateParameter { } OptionalTemplateParameters: /* Empty. */ /* [X] */ { } | T_LPAREN TemplateParameters T_RPAREN { } TemplateParameter: IsTunnel T_DOLLAR VarName TypeDeclaration OptionalAssign { /* Note, this grammar rule is invoked for @c xsl:param @em and @c * xsl:with-param. */ const bool isParsingWithParam = parseInfo->isParsingWithParam(); /** * @c xsl:param doesn't make life easy: * * If it only has @c name, it's default value is an empty * string(hence has type @c xs:string), but the value that * (maybe) is supplied can be anything, typically a node. * * Therefore, for that very common case we can't rely on * the Expression's type, but have to force it to item()*. * * So if we're supplied the type item()*, we pass a null * SequenceType. TemplateParameterReference recognizes this * and has item()* as its static type, regardless of if the * expression has a more specific type. */ SequenceType::Ptr type; if(!$4->is(CommonSequenceTypes::ZeroOrMoreItems)) type = $4; Expression::Ptr expr; /* The default value is an empty sequence. */ if(!$5 && ((type && $4->cardinality().allowsEmpty()) || isParsingWithParam)) expr = create(new EmptySequence, @$, parseInfo); else expr = $5; /* We ensure we have some type, so CallTemplate, Template and friends * are happy. */ if(!isParsingWithParam && !type) type = CommonSequenceTypes::ZeroOrMoreItems; if($1) /* TODO, handle tunnel parameters. */; else { if((!isParsingWithParam && VariableDeclaration::contains(parseInfo->templateParameters, $3)) || (isParsingWithParam && parseInfo->templateWithParams.contains($3))) { parseInfo->staticContext->error(QtXmlPatterns::tr("Each name of a template parameter must be unique; %1 is duplicated.") .arg(formatKeyword(parseInfo->staticContext->namePool(), $3)), isParsingWithParam ? ReportContext::XTSE0670 : ReportContext::XTSE0580, fromYYLTYPE(@$, parseInfo)); } else { if(isParsingWithParam) parseInfo->templateWithParams[$3] = WithParam::Ptr(new WithParam($3, $4, expr)); else { Q_ASSERT(type); pushVariable($3, type, expr, VariableDeclaration::TemplateParameter, @$, parseInfo); parseInfo->templateParameters.append(parseInfo->variables.top()); } } } } IsTunnel: /* Empty. */ { $$ = false; } | T_TUNNEL { $$ = true; } OptionalAssign: /* Empty. */ /* [X] */ { $$ = Expression::Ptr(); } | T_ASSIGN ExprSingle { $$ = $2; } /** * Controls whethers a path expression should sort its result. Used for * implementing XSL-T's for-each. */ MapOrSlash: T_SLASH /* [X] */ { $$ = Path::RegularPath; } | T_MAP { $$ = Path::XSLTForEach; } | T_FOR_APPLY_TEMPLATE { $$ = Path::ForApplyTemplate; } FilteredAxisStep: AxisStep /* [X] */ | FilteredAxisStep T_LBRACKET Expr T_RBRACKET { $$ = create(GenericPredicate::create($1, $3, parseInfo->staticContext, fromYYLTYPE(@$, parseInfo)), @$, parseInfo); } AxisStep: ForwardStep /* [71] */ | ReverseStep ForwardStep: Axis { if($1 == QXmlNodeModelIndex::AxisAttribute) parseInfo->nodeTestSource = BuiltinTypes::attribute; } NodeTestInAxisStep /* [72] */ { if($3) { /* A node test was explicitly specified. The un-abbreviated syntax was used. */ $$ = create(new AxisStep($1, $3), @$, parseInfo); } else { /* Quote from 3.2.1.1 Axes * * [Definition: Every axis has a principal node kind. If an axis * can contain elements, then the principal node kind is element; * otherwise, it is the kind of nodes that the axis can contain.] Thus: * - For the attribute axis, the principal node kind is attribute. * - For all other axes, the principal node kind is element. */ if($1 == QXmlNodeModelIndex::AxisAttribute) $$ = create(new AxisStep(QXmlNodeModelIndex::AxisAttribute, BuiltinTypes::attribute), @$, parseInfo); else $$ = create(new AxisStep($1, BuiltinTypes::element), @$, parseInfo); } parseInfo->restoreNodeTestSource(); } | AbbrevForwardStep NodeTestInAxisStep: NodeTest | AnyAttributeTest Axis: AxisToken T_COLONCOLON /* [73] */ { if($1 == QXmlNodeModelIndex::AxisNamespace) { /* We don't raise XPST0010 here because the namespace axis isn't an optional * axis. It simply is not part of the XQuery grammar. */ parseInfo->staticContext->error(QtXmlPatterns::tr("The %1-axis is unsupported in XQuery") .arg(formatKeyword("namespace")), ReportContext::XPST0003, fromYYLTYPE(@$, parseInfo)); } else $$ = $1; switch($1) { case QXmlNodeModelIndex::AxisAttribute: { allowedIn(QueryLanguages( QXmlQuery::XPath20 | QXmlQuery::XQuery10 | QXmlQuery::XmlSchema11IdentityConstraintField | QXmlQuery::XSLT20), parseInfo, @$); break; } case QXmlNodeModelIndex::AxisChild: { allowedIn(QueryLanguages( QXmlQuery::XPath20 | QXmlQuery::XQuery10 | QXmlQuery::XmlSchema11IdentityConstraintField | QXmlQuery::XmlSchema11IdentityConstraintSelector | QXmlQuery::XSLT20), parseInfo, @$); break; } default: { allowedIn(QueryLanguages( QXmlQuery::XPath20 | QXmlQuery::XQuery10 | QXmlQuery::XSLT20), parseInfo, @$); } } } AxisToken: T_ANCESTOR_OR_SELF {$$ = QXmlNodeModelIndex::AxisAncestorOrSelf ;} | T_ANCESTOR {$$ = QXmlNodeModelIndex::AxisAncestor ;} | T_ATTRIBUTE {$$ = QXmlNodeModelIndex::AxisAttribute ;} | T_CHILD {$$ = QXmlNodeModelIndex::AxisChild ;} | T_DESCENDANT_OR_SELF {$$ = QXmlNodeModelIndex::AxisDescendantOrSelf;} | T_DESCENDANT {$$ = QXmlNodeModelIndex::AxisDescendant ;} | T_FOLLOWING {$$ = QXmlNodeModelIndex::AxisFollowing ;} | T_PRECEDING {$$ = QXmlNodeModelIndex::AxisPreceding ;} | T_FOLLOWING_SIBLING {$$ = QXmlNodeModelIndex::AxisFollowingSibling;} | T_PRECEDING_SIBLING {$$ = QXmlNodeModelIndex::AxisPrecedingSibling;} | T_PARENT {$$ = QXmlNodeModelIndex::AxisParent ;} | T_SELF {$$ = QXmlNodeModelIndex::AxisSelf ;} AbbrevForwardStep: T_AT_SIGN { parseInfo->nodeTestSource = BuiltinTypes::attribute; } NodeTest /* [72] */ { allowedIn(QueryLanguages(QXmlQuery::XQuery10 | QXmlQuery::XSLT20 | QXmlQuery::XmlSchema11IdentityConstraintField), parseInfo, @$); $$ = create(new AxisStep(QXmlNodeModelIndex::AxisAttribute, $3), @$, parseInfo); parseInfo->restoreNodeTestSource(); } | NodeTest { ItemType::Ptr nodeTest; if(parseInfo->isParsingPattern && *$1 == *BuiltinTypes::node) nodeTest = BuiltinTypes::xsltNodeTest; else nodeTest = $1; $$ = create(new AxisStep(QXmlNodeModelIndex::AxisChild, nodeTest), @$, parseInfo); } | AnyAttributeTest { $$ = create(new AxisStep(QXmlNodeModelIndex::AxisAttribute, $1), @$, parseInfo); } ReverseStep: AbbrevReverseStep /* [75] */ AbbrevReverseStep: T_DOTDOT /* [77] */ { $$ = create(new AxisStep(QXmlNodeModelIndex::AxisParent, BuiltinTypes::node), @$, parseInfo); } NodeTest: NameTest /* [78] */ | KindTest { allowedIn(QueryLanguages(QXmlQuery::XQuery10 | QXmlQuery::XPath20), parseInfo, @$); } NameTest: ElementName /* [79] */ { $$ = QNameTest::create(parseInfo->nodeTestSource, $1); } | WildCard WildCard: T_STAR /* [80] */ { $$ = parseInfo->nodeTestSource; } | T_ANY_LOCAL_NAME { const NamePool::Ptr np(parseInfo->staticContext->namePool()); const ReflectYYLTYPE ryy(@$, parseInfo); const QXmlName::NamespaceCode ns(QNameConstructor::namespaceForPrefix(np->allocatePrefix($1), parseInfo->staticContext, &ryy)); $$ = NamespaceNameTest::create(parseInfo->nodeTestSource, ns); } | T_ANY_PREFIX { allowedIn(QueryLanguages(QXmlQuery::XQuery10 | QXmlQuery::XPath20), parseInfo, @$); const QXmlName::LocalNameCode c = parseInfo->staticContext->namePool()->allocateLocalName($1); $$ = LocalNameTest::create(parseInfo->nodeTestSource, c); } FilterExpr: PrimaryExpr /* [81] */ | FilterExpr T_LBRACKET Expr T_RBRACKET { allowedIn(QueryLanguages(QXmlQuery::XQuery10 | QXmlQuery::XPath20), parseInfo, @$); $$ = create(GenericPredicate::create($1, $3, parseInfo->staticContext, fromYYLTYPE(@4, parseInfo)), @$, parseInfo); } PrimaryExpr: Literal /* [84] */ | VarRef | ParenthesizedExpr | ContextItemExpr | FunctionCallExpr | OrderingExpr | Constructor | T_APPLY_TEMPLATE OptionalMode T_LPAREN TemplateWithParameters T_RPAREN { $$ = create(new ApplyTemplate(parseInfo->modeFor($2), parseInfo->templateWithParams, parseInfo->modeFor(QXmlName(StandardNamespaces::InternalXSLT, StandardLocalNames::Default))), @1, parseInfo); parseInfo->templateWithParametersHandled(); } Literal: NumericLiteral /* [85] */ | StringLiteral { $$ = create(new Literal(AtomicString::fromValue($1)), @$, parseInfo); } NumericLiteral: T_XPATH2_NUMBER /* [86] */ { allowedIn(QueryLanguages(QXmlQuery::XQuery10 | QXmlQuery::XPath20), parseInfo, @$); $$ = createNumericLiteral($1, @$, parseInfo); } | T_NUMBER { allowedIn(QueryLanguages(QXmlQuery::XQuery10 | QXmlQuery::XPath20), parseInfo, @$); $$ = createNumericLiteral($1, @$, parseInfo); } VarRef: T_DOLLAR VarName /* [87] */ { allowedIn(QueryLanguages(QXmlQuery::XQuery10 | QXmlQuery::XPath20), parseInfo, @$); $$ = resolveVariable($2, @$, parseInfo, false); } VarName: T_NCNAME /* [88] */ { /* See: http://www.w3.org/TR/xpath20/#id-variables */ $$ = parseInfo->staticContext->namePool()->allocateQName(QString(), $1); } | QName { $$ = $1; } ParenthesizedExpr: T_LPAREN Expr T_RPAREN /* [89] */ { allowedIn(QueryLanguages(QXmlQuery::XQuery10 | QXmlQuery::XPath20), parseInfo, @$); $$ = $2; } | T_LPAREN T_RPAREN { allowedIn(QueryLanguages(QXmlQuery::XQuery10 | QXmlQuery::XPath20), parseInfo, @$); $$ = create(new EmptySequence, @$, parseInfo); } ContextItemExpr: T_DOT /* [90] */ { $$ = create(new ContextItem(), @$, parseInfo); } OrderingExpr: OrderingMode EnclosedExpr /* [X] */ { $$ = $2; } FunctionCallExpr: FunctionName T_LPAREN FunctionArguments T_RPAREN /* [93] */ { allowedIn(QueryLanguages(QXmlQuery::XQuery10 | QXmlQuery::XPath20), parseInfo, @$); if(XPathHelper::isReservedNamespace($1.namespaceURI()) || $1.namespaceURI() == StandardNamespaces::InternalXSLT) { /* We got a call to a builtin function. */ const ReflectYYLTYPE ryy(@$, parseInfo); const Expression::Ptr func(parseInfo->staticContext-> functionSignatures()->createFunctionCall($1, $3, parseInfo->staticContext, &ryy)); if(func) $$ = create(func, @$, parseInfo); else { parseInfo->staticContext->error(QtXmlPatterns::tr("No function with name %1 is available.") .arg(formatKeyword(parseInfo->staticContext->namePool(), $1)), ReportContext::XPST0017, fromYYLTYPE(@$, parseInfo)); } } else /* It's a call to a function created with 'declare function'.*/ { $$ = create(new UserFunctionCallsite($1, $3.count()), @$, parseInfo); $$->setOperands($3); parseInfo->userFunctionCallsites.append($$); } } FunctionArguments: /* empty */ /* [X] */ { $$ = Expression::List(); } | ExprSingle { Expression::List list; list.append($1); $$ = list; } | ExpressionSequence Constructor: DirectConstructor /* [94] */ { allowedIn(QXmlQuery::XQuery10, parseInfo, @$); } | ComputedConstructor /* The reason we cannot call alloweIn() as the action for ComputedConstructor, * is that we use the computed constructors for XSL-T, and therefore generate * INTERNAL tokens. */ DirectConstructor: DirElemConstructor /* [95] */ | DirCommentConstructor | DirPIConstructor /* * Direct attribute constructors can contain embedded expressions, and for those namespace bindings * on the same element needs to be in scope. For example: * * @code * * @endcode * * Patternist is designed to do all name resolution at parse time so the subsequent code only has to * deal with expanded QNames(which the QName class represents), and this presents a problem since * the parser haven't even encountered the @c xmlns:prefix when resolving @c prefix in the name test. * * This is solved as follows: * *
    *
  1. Just before starting parsing the attributes, we call Tokenizer::commenceScanOnly(). * This switches the tokenizer to not tokenize embedded expressions in attributes, * but to return them as strings, token type STRING_LITERAL.
  2. *
  3. We parse all the attributes, and iterates over them, only caring about * namespace bindings, and validates and adds them to the context.
  4. *
  5. We call Tokenizer::resumeTokenizationFrom() from the previous position * returned from Tokenizer::commenceScanOnly() and parses the attributes once more, * but this time with tokenization of embedded expressions. Since we this time * have the namespace bindings in place, everything resolves.
  6. *
* * Saxon does this in a similar way. Study net.sf.saxon.expr.QueryParser::parseDirectElementConstructor(). * * @see XQueryTokenizer::attributeAsRaw() */ DirElemConstructor: T_G_LT LexicalName { $$ = parseInfo->tokenizer->commenceScanOnly(); parseInfo->scanOnlyStack.push(true); } /* This list contains name/string pairs. No embedded * expressions has been parsed. */ DirAttributeList { ++parseInfo->elementConstructorDepth; Expression::List constructors; parseInfo->resolvers.push(parseInfo->staticContext->namespaceBindings()); /* Fix up attributes and namespace declarations. */ const NamespaceResolver::Ptr resolver(new DelegatingNamespaceResolver(parseInfo->staticContext->namespaceBindings())); const NamePool::Ptr namePool(parseInfo->staticContext->namePool()); const int len = $4.size(); QSet usedDeclarations; /* Whether xmlns="" has been encountered. */ bool hasDefaultDeclaration = false; /* For each attribute & namespace declaration, do: */ for(int i = 0; i < len; ++i) { QString strLocalName; QString strPrefix; XPathHelper::splitQName($4.at(i).first, strPrefix, strLocalName); const QXmlName::PrefixCode prefix = namePool->allocatePrefix(strPrefix); /* This can seem a bit weird. However, this name is ending up in a QXmlName * which consider its prefix a... prefix. So, a namespace binding name can in some cases * be a local name, but that's just as the initial syntactical construct. */ const QXmlName::LocalNameCode localName = namePool->allocatePrefix(strLocalName); /* Not that localName is "foo" in "xmlns:foo" and that prefix is "xmlns". */ if(prefix == StandardPrefixes::xmlns || (prefix == StandardPrefixes::empty && localName == StandardPrefixes::xmlns)) { if(localName == StandardPrefixes::xmlns) hasDefaultDeclaration = true; /* We have a namespace declaration. */ const Expression::Ptr nsExpr($4.at(i).second); const QString strNamespace(nsExpr->is(Expression::IDEmptySequence) ? QString() : nsExpr->as()->item().stringValue()); const QXmlName::NamespaceCode ns = namePool->allocateNamespace(strNamespace); if(ns == StandardNamespaces::empty) { if(localName != StandardPrefixes::xmlns) { parseInfo->staticContext->error(QtXmlPatterns::tr("The namespace URI cannot be the empty string when binding to a prefix, %1.") .arg(formatURI(strPrefix)), ReportContext::XQST0085, fromYYLTYPE(@$, parseInfo)); } } else if(!AnyURI::isValid(strNamespace)) { parseInfo->staticContext->error(QtXmlPatterns::tr("%1 is an invalid namespace URI.").arg(formatURI(strNamespace)), ReportContext::XQST0022, fromYYLTYPE(@$, parseInfo)); } if(prefix == StandardPrefixes::xmlns && localName == StandardPrefixes::xmlns) { parseInfo->staticContext->error(QtXmlPatterns::tr("It is not possible to bind to the prefix %1") .arg(formatKeyword("xmlns")), ReportContext::XQST0070, fromYYLTYPE(@$, parseInfo)); } if(ns == StandardNamespaces::xml && localName != StandardPrefixes::xml) { parseInfo->staticContext->error(QtXmlPatterns::tr("Namespace %1 can only be bound to %2 (and it is, in either case, pre-declared).") .arg(formatURI(namePool->stringForNamespace(StandardNamespaces::xml))) .arg(formatKeyword("xml")), ReportContext::XQST0070, fromYYLTYPE(@$, parseInfo)); } if(localName == StandardPrefixes::xml && ns != StandardNamespaces::xml) { parseInfo->staticContext->error(QtXmlPatterns::tr("Prefix %1 can only be bound to %2 (and it is, in either case, pre-declared).") .arg(formatKeyword("xml")) .arg(formatURI(namePool->stringForNamespace(StandardNamespaces::xml))), ReportContext::XQST0070, fromYYLTYPE(@$, parseInfo)); } QXmlName nb; if(localName == StandardPrefixes::xmlns) nb = QXmlName(ns, StandardLocalNames::empty); else nb = QXmlName(ns, StandardLocalNames::empty, localName); if(usedDeclarations.contains(nb.prefix())) { parseInfo->staticContext->error(QtXmlPatterns::tr("Two namespace declaration attributes have the same name: %1.") .arg(formatKeyword(namePool->stringForPrefix(nb.prefix()))), ReportContext::XQST0071, fromYYLTYPE(@$, parseInfo)); } else usedDeclarations.insert(nb.prefix()); /* If the user has bound the XML namespace correctly, we in either * case don't want to output it. * * We only have to check the namespace parts since the above checks has ensured * consistency in the prefix parts. */ if(ns != StandardNamespaces::xml) { /* We don't want default namespace declarations when the * default namespace already is empty. */ if(!(ns == StandardNamespaces::empty && localName == StandardNamespaces::xmlns && resolver->lookupNamespaceURI(StandardPrefixes::empty) == StandardNamespaces::empty)) { constructors.append(create(new NamespaceConstructor(nb), @$, parseInfo)); resolver->addBinding(nb); } } } } if(parseInfo->elementConstructorDepth == 1 && !hasDefaultDeclaration) { /* TODO But mostly this isn't needed, since the default element * namespace is empty? How does this at all work? */ const QXmlName def(resolver->lookupNamespaceURI(StandardPrefixes::empty), StandardLocalNames::empty); constructors.append(create(new NamespaceConstructor(def), @$, parseInfo)); } parseInfo->staticContext->setNamespaceBindings(resolver); $$ = constructors; /* Resolve the name of the element, now that the namespace attributes are read. */ { const ReflectYYLTYPE ryy(@$, parseInfo); const QXmlName ele = QNameConstructor::expandQName($2, parseInfo->staticContext, resolver, &ryy); parseInfo->tagStack.push(ele); } parseInfo->tokenizer->resumeTokenizationFrom($3); } T_POSITION_SET DirAttributeList DirElemConstructorTail /* [96] */ { /* We add the content constructor after the attribute constructors. This might result * in nested ExpressionSequences, but it will be optimized away later on. */ Expression::List attributes($5); const NamePool::Ptr namePool(parseInfo->staticContext->namePool()); const int len = $7.size(); QSet declaredAttributes; declaredAttributes.reserve(len); /* For each namespace, resolve its name(now that we have resolved the namespace declarations) and * turn it into an attribute constructor. */ for(int i = 0; i < len; ++i) { QString strLocalName; QString strPrefix; XPathHelper::splitQName($7.at(i).first, strPrefix, strLocalName); const QXmlName::PrefixCode prefix = namePool->allocatePrefix(strPrefix); const QXmlName::LocalNameCode localName = namePool->allocateLocalName(strLocalName); if(prefix == StandardPrefixes::xmlns || (prefix == StandardPrefixes::empty && localName == StandardLocalNames::xmlns)) { const Expression::ID id = $7.at(i).second->id(); if(id == Expression::IDStringValue || id == Expression::IDEmptySequence) { /* It's a namespace declaration, and we've already handled those above. */ continue; } else { parseInfo->staticContext->error(QtXmlPatterns::tr("The namespace URI must be a constant and cannot " "use enclosed expressions."), ReportContext::XQST0022, fromYYLTYPE(@$, parseInfo)); } } else { const ReflectYYLTYPE ryy(@$, parseInfo); const QXmlName att = QNameConstructor::expandQName($7.at(i).first, parseInfo->staticContext, parseInfo->staticContext->namespaceBindings(), &ryy, true); if(declaredAttributes.contains(att)) { parseInfo->staticContext->error(QtXmlPatterns::tr("An attribute with name %1 has already appeared on this element.") .arg(formatKeyword(parseInfo->staticContext->namePool(), att)), ReportContext::XQST0040, fromYYLTYPE(@$, parseInfo)); } else declaredAttributes.insert(att); /* wrapLiteral() needs the SourceLocationReflection of the AttributeConstructor, but * it's unknown inside the arguments to its constructor. Hence we have to do this workaround of setting * it twice. * * The AttributeConstructor's arguments are just dummies. */ const Expression::Ptr ctor(create(new AttributeConstructor($7.at(i).second, $7.at(i).second), @$, parseInfo)); Expression::List ops; ops.append(wrapLiteral(toItem(QNameValue::fromValue(namePool, att)), parseInfo->staticContext, ctor.data())); ops.append($7.at(i).second); ctor->setOperands(ops); attributes.append(ctor); } } Expression::Ptr contentOp; if(attributes.isEmpty()) contentOp = $8; else { attributes.append($8); contentOp = create(new ExpressionSequence(attributes), @$, parseInfo); } const Expression::Ptr name(create(new Literal(toItem(QNameValue::fromValue(parseInfo->staticContext->namePool(), parseInfo->tagStack.top()))), @$, parseInfo)); $$ = create(new ElementConstructor(name, contentOp, parseInfo->isXSLT()), @$, parseInfo); /* Restore the old context. We don't want the namespaces * to be in-scope for expressions appearing after the * element they appeared on. */ parseInfo->staticContext->setNamespaceBindings(parseInfo->resolvers.pop()); parseInfo->tagStack.pop(); --parseInfo->elementConstructorDepth; } DirElemConstructorTail: T_QUICK_TAG_END { $$ = create(new EmptySequence(), @$, parseInfo); } | T_G_GT DirElemContent T_BEGIN_END_TAG ElementName T_G_GT { if(!$4.isLexicallyEqual(parseInfo->tagStack.top())) { parseInfo->staticContext->error(QtXmlPatterns::tr("A direct element constructor is not " "well-formed. %1 is ended with %2.") .arg(formatKeyword(parseInfo->staticContext->namePool()->toLexical(parseInfo->tagStack.top())), formatKeyword(parseInfo->staticContext->namePool()->toLexical($4))), ReportContext::XPST0003, fromYYLTYPE(@$, parseInfo)); } if($2.isEmpty()) $$ = create(new EmptySequence(), @$, parseInfo); else if($2.size() == 1) $$ = $2.first(); else $$ = create(new ExpressionSequence($2), @$, parseInfo); } DirAttributeList: /* empty */ /* [97] */ { $$ = AttributeHolderVector(); } | DirAttributeList Attribute { $1.append($2); $$ = $1; } Attribute: LexicalName T_G_EQ DirAttributeValue /* [X] */ { $$ = qMakePair($1, $3); } DirAttributeValue: T_QUOTE AttrValueContent T_QUOTE /* [98] */ { $$ = createDirAttributeValue($2, parseInfo, @$); } | T_APOS AttrValueContent T_APOS { $$ = createDirAttributeValue($2, parseInfo, @$); } AttrValueContent: /* Empty. */ /* [X] */ { $$ = Expression::List(); } | EnclosedExpr AttrValueContent { Expression::Ptr content($1); if(parseInfo->isBackwardsCompat.top()) content = create(GenericPredicate::createFirstItem(content), @$, parseInfo); $2.prepend(createSimpleContent(content, @$, parseInfo)); $$ = $2; } | StringLiteral AttrValueContent { $2.prepend(create(new Literal(AtomicString::fromValue($1)), @$, parseInfo)); $$ = $2; } DirElemContent: /* empty */ /* [101] */ { $$ = Expression::List(); parseInfo->isPreviousEnclosedExpr = false; } | DirElemContent DirectConstructor { $1.append($2); $$ = $1; parseInfo->isPreviousEnclosedExpr = false; } | DirElemContent StringLiteral { if(parseInfo->staticContext->boundarySpacePolicy() == StaticContext::BSPStrip && XPathHelper::isWhitespaceOnly($2)) { $$ = $1; } else { $1.append(create(new TextNodeConstructor(create(new Literal(AtomicString::fromValue($2)), @$, parseInfo)), @$, parseInfo)); $$ = $1; parseInfo->isPreviousEnclosedExpr = false; } } | DirElemContent T_NON_BOUNDARY_WS { $1.append(create(new TextNodeConstructor(create(new Literal(AtomicString::fromValue($2)), @$, parseInfo)), @$, parseInfo)); $$ = $1; parseInfo->isPreviousEnclosedExpr = false; } | DirElemContent EnclosedExpr { /* We insert a text node constructor that send an empty text node between * the two enclosed expressions, in order to ensure that no space is inserted. * * However, we only do it when we have no node constructors. */ if(parseInfo->isPreviousEnclosedExpr && BuiltinTypes::xsAnyAtomicType->xdtTypeMatches($2->staticType()->itemType()) && BuiltinTypes::xsAnyAtomicType->xdtTypeMatches($1.last()->staticType()->itemType())) $1.append(create(new TextNodeConstructor(create(new Literal(AtomicString::fromValue(QString())), @$, parseInfo)), @$, parseInfo)); else parseInfo->isPreviousEnclosedExpr = true; $1.append(createCopyOf($2, parseInfo, @$)); $$ = $1; } DirCommentConstructor: T_COMMENT_START T_COMMENT_CONTENT /* [103] */ { $$ = create(new CommentConstructor(create(new Literal(AtomicString::fromValue($2)), @$, parseInfo)), @$, parseInfo); } DirPIConstructor: T_PI_START T_PI_TARGET T_PI_CONTENT /* [105] */ { const ReflectYYLTYPE ryy(@$, parseInfo); NCNameConstructor::validateTargetName($2, parseInfo->staticContext, &ryy); $$ = create(new ProcessingInstructionConstructor( create(new Literal(AtomicString::fromValue($2)), @$, parseInfo), create(new Literal(AtomicString::fromValue($3)), @$, parseInfo)), @$, parseInfo); } ComputedConstructor: CompDocConstructor /* [109] */ | CompElemConstructor | CompAttrConstructor | CompTextConstructor | CompCommentConstructor | CompPIConstructor | CompNamespaceConstructor CompDocConstructor: T_DOCUMENT IsInternal EnclosedExpr /* [110] */ { allowedIn(QXmlQuery::XQuery10, parseInfo, @$, $2); $$ = create(new DocumentConstructor($3), @$, parseInfo); } CompElemConstructor: T_ELEMENT IsInternal CompElementName { /* This value is incremented before the action below is executed. */ ++parseInfo->elementConstructorDepth; } EnclosedOptionalExpr /* [111] */ { Q_ASSERT(5); allowedIn(QXmlQuery::XQuery10, parseInfo, @$, $2); Expression::Ptr effExpr; if($5) effExpr = createCopyOf($5, parseInfo, @$); else effExpr = create(new EmptySequence(), @$, parseInfo); const QXmlName::NamespaceCode ns = parseInfo->resolvers.top()->lookupNamespaceURI(StandardPrefixes::empty); /* Ensure the default namespace gets counted as an in-scope binding, if such a one exists. If we're * a child of another constructor, it has already been done. */ if(parseInfo->elementConstructorDepth == 1 && ns != StandardNamespaces::empty) { Expression::List exprList; /* We append the namespace constructor before the body, in order to * comply with QAbstractXmlPushHandler's contract. */ const QXmlName def(parseInfo->resolvers.top()->lookupNamespaceURI(StandardPrefixes::empty), StandardLocalNames::empty); exprList.append(create(new NamespaceConstructor(def), @$, parseInfo)); exprList.append(effExpr); effExpr = create(new ExpressionSequence(exprList), @$, parseInfo); } --parseInfo->elementConstructorDepth; $$ = create(new ElementConstructor($3, effExpr, parseInfo->isXSLT()), @$, parseInfo); } IsInternal: /* Empty. */ /* [X] */ { $$ = false; } | T_INTERNAL { $$ = true; } CompAttrConstructor: T_ATTRIBUTE IsInternal CompAttributeName EnclosedOptionalExpr /* [113] */ { allowedIn(QXmlQuery::XQuery10, parseInfo, @$, $2); const Expression::Ptr name(create(new AttributeNameValidator($3), @$, parseInfo)); if($4) $$ = create(new AttributeConstructor(name, createSimpleContent($4, @$, parseInfo)), @$, parseInfo); else $$ = create(new AttributeConstructor(name, create(new EmptySequence(), @$, parseInfo)), @$, parseInfo); } CompTextConstructor: T_TEXT IsInternal EnclosedExpr /* [114] */ { $$ = create(new TextNodeConstructor(createSimpleContent($3, @$, parseInfo)), @$, parseInfo); } CompCommentConstructor: T_COMMENT IsInternal EnclosedExpr /* [115] */ { allowedIn(QXmlQuery::XQuery10, parseInfo, @$, $2); $$ = create(new CommentConstructor(createSimpleContent($3, @$, parseInfo)), @$, parseInfo); } CompPIConstructor: T_PROCESSING_INSTRUCTION CompPIName EnclosedOptionalExpr /* [116] */ { allowedIn(QXmlQuery::XQuery10, parseInfo, @$, $2); if($3) { $$ = create(new ProcessingInstructionConstructor($2, createSimpleContent($3, @$, parseInfo)), @$, parseInfo); } else $$ = create(new ProcessingInstructionConstructor($2, create(new EmptySequence(), @$, parseInfo)), @$, parseInfo); } CompAttributeName: { parseInfo->nodeTestSource = BuiltinTypes::attribute; } ElementName { parseInfo->restoreNodeTestSource(); } /* [X] */ { $$ = create(new Literal(toItem(QNameValue::fromValue(parseInfo->staticContext->namePool(), $2))), @$, parseInfo); } | CompNameExpr CompElementName: ElementName /* [X] */ { $$ = create(new Literal(toItem(QNameValue::fromValue(parseInfo->staticContext->namePool(), $1))), @$, parseInfo); } | CompNameExpr CompNameExpr: EnclosedExpr { if(BuiltinTypes::xsQName->xdtTypeMatches($1->staticType()->itemType())) $$ = $1; else { $$ = create(new QNameConstructor($1, parseInfo->staticContext->namespaceBindings()), @$, parseInfo); } } /* * We always create an NCNameConstructor here. If will be rewritten away if not needed. */ CompPIName: T_NCNAME { $$ = create(new NCNameConstructor(create(new Literal(AtomicString::fromValue($1)), @$, parseInfo)), @$, parseInfo); } | EnclosedExpr { $$ = create(new NCNameConstructor($1), @$, parseInfo); } /* * This expression is used for implementing XSL-T 2.0's xsl:namespace * instruction. */ CompNamespaceConstructor: T_NAMESPACE EnclosedExpr EnclosedExpr /* [X] */ { $$ = create(new ComputedNamespaceConstructor($2, $3), @$, parseInfo); } SingleType: AtomicType /* [117] */ { $$ = makeGenericSequenceType($1, Cardinality::exactlyOne()); } | AtomicType T_QUESTION { $$ = makeGenericSequenceType($1, Cardinality::zeroOrOne()); } TypeDeclaration: /* empty */ /* [118] */ { $$ = CommonSequenceTypes::ZeroOrMoreItems; } | T_AS SequenceType { $$ = $2; } SequenceType: ItemType OccurrenceIndicator /* [119] */ { $$ = makeGenericSequenceType($1, $2); } | T_EMPTY_SEQUENCE EmptyParanteses { $$ = CommonSequenceTypes::Empty; } OccurrenceIndicator: /* empty */ {$$ = Cardinality::exactlyOne();} /* [120] */ | T_PLUS {$$ = Cardinality::oneOrMore();} | T_STAR {$$ = Cardinality::zeroOrMore();} | T_QUESTION {$$ = Cardinality::zeroOrOne();} ItemType: AtomicType /* [121] */ | KindTest | AnyAttributeTest | T_ITEM EmptyParanteses { $$ = BuiltinTypes::item; } AtomicType: ElementName /* [122] */ { const SchemaType::Ptr t(parseInfo->staticContext->schemaDefinitions()->createSchemaType($1)); if(!t) { parseInfo->staticContext->error(QtXmlPatterns::tr("The name %1 does not refer to any schema type.") .arg(formatKeyword(parseInfo->staticContext->namePool(), $1)), ReportContext::XPST0051, fromYYLTYPE(@$, parseInfo)); } else if(BuiltinTypes::xsAnyAtomicType->wxsTypeMatches(t)) $$ = AtomicType::Ptr(t); else { /* Try to give an intelligent message. */ if(t->isComplexType()) { parseInfo->staticContext->error(QtXmlPatterns::tr("%1 is an complex type. Casting to complex " "types is not possible. However, casting " "to atomic types such as %2 works.") .arg(formatType(parseInfo->staticContext->namePool(), t)) .arg(formatType(parseInfo->staticContext->namePool(), BuiltinTypes::xsInteger)), ReportContext::XPST0051, fromYYLTYPE(@$, parseInfo)); } else { parseInfo->staticContext->error(QtXmlPatterns::tr("%1 is not an atomic type. Casting " "is only possible to atomic types.") .arg(formatType(parseInfo->staticContext->namePool(), t)), ReportContext::XPST0051, fromYYLTYPE(@$, parseInfo)); } } } /* This non-terminal does not contain SchemaAttributeTest and AttributeTest. Those are in the AnyAttributeTest non-terminal. This is in order to get the axis right for attribute tests in the abbreviated syntax. */ KindTest: DocumentTest /* [123] */ | ElementTest | SchemaElementTest | PITest | CommentTest | TextTest | AnyKindTest AnyKindTest: T_NODE EmptyParanteses /* [124] */ { $$ = BuiltinTypes::node; } DocumentTest: T_DOCUMENT_NODE EmptyParanteses /* [125] */ { $$ = BuiltinTypes::document; } | T_DOCUMENT_NODE T_LPAREN AnyElementTest T_RPAREN { // TODO support for document element testing $$ = BuiltinTypes::document; } AnyElementTest: ElementTest /* [X] */ | SchemaElementTest TextTest: T_TEXT EmptyParanteses /* [126] */ { $$ = BuiltinTypes::text; } CommentTest: T_COMMENT EmptyParanteses /* [127] */ { $$ = BuiltinTypes::comment; } PITest: T_PROCESSING_INSTRUCTION EmptyParanteses /* [128] */ { $$ = BuiltinTypes::pi; } | T_PROCESSING_INSTRUCTION T_LPAREN T_NCNAME T_RPAREN { $$ = LocalNameTest::create(BuiltinTypes::pi, parseInfo->staticContext->namePool()->allocateLocalName($3)); } | T_PROCESSING_INSTRUCTION T_LPAREN StringLiteral T_RPAREN { if(QXmlUtils::isNCName($3)) { $$ = LocalNameTest::create(BuiltinTypes::pi, parseInfo->staticContext->namePool()->allocateLocalName($3)); } else { parseInfo->staticContext->error(QtXmlPatterns::tr("%1 is not a valid name for a " "processing-instruction.") .arg(formatKeyword($3)), ReportContext::XPTY0004, fromYYLTYPE(@$, parseInfo)); } } AnyAttributeTest: AttributeTest | SchemaAttributeTest AttributeTest: T_ATTRIBUTE EmptyParanteses /* [129] */ { $$ = BuiltinTypes::attribute; } | T_ATTRIBUTE T_LPAREN T_STAR T_RPAREN { $$ = BuiltinTypes::attribute; } | T_ATTRIBUTE T_LPAREN AttributeName T_RPAREN { $$ = QNameTest::create(BuiltinTypes::attribute, $3); } | T_ATTRIBUTE T_LPAREN AttributeName T_COMMA TypeName T_RPAREN { const SchemaType::Ptr t(parseInfo->staticContext->schemaDefinitions()->createSchemaType($5)); if(t) $$ = BuiltinTypes::attribute; else { parseInfo->staticContext->error(unknownType().arg(formatKeyword(parseInfo->staticContext->namePool(), $5)), ReportContext::XPST0008, fromYYLTYPE(@$, parseInfo)); } } | T_ATTRIBUTE T_LPAREN T_STAR T_COMMA TypeName T_RPAREN { const SchemaType::Ptr t(parseInfo->staticContext->schemaDefinitions()->createSchemaType($5)); if(t) $$ = BuiltinTypes::attribute; else { parseInfo->staticContext->error(unknownType().arg(formatKeyword(parseInfo->staticContext->namePool(), $5)), ReportContext::XPST0008, fromYYLTYPE(@$, parseInfo)); } } SchemaAttributeTest: T_SCHEMA_ATTRIBUTE T_LPAREN ElementName T_RPAREN /* [131] */ { parseInfo->staticContext->error(QtXmlPatterns::tr("%1 is not in the in-scope attribute " "declarations. Note that the schema import " "feature is not supported.") .arg(formatKeyword(parseInfo->staticContext->namePool(), $3)), ReportContext::XPST0008, fromYYLTYPE(@$, parseInfo)); $$.reset(); } ElementTest: T_ELEMENT EmptyParanteses /* [133] */ { $$ = BuiltinTypes::element; } | T_ELEMENT T_LPAREN T_STAR T_RPAREN { $$ = BuiltinTypes::element; } | T_ELEMENT T_LPAREN ElementName T_RPAREN { $$ = QNameTest::create(BuiltinTypes::element, $3); } | T_ELEMENT T_LPAREN ElementName T_COMMA TypeName OptionalQuestionMark T_RPAREN { const SchemaType::Ptr t(parseInfo->staticContext->schemaDefinitions()->createSchemaType($5)); if(t) $$ = BuiltinTypes::element; else { parseInfo->staticContext->error(unknownType() .arg(formatKeyword(parseInfo->staticContext->namePool(), $5)), ReportContext::XPST0008, fromYYLTYPE(@$, parseInfo)); } } | T_ELEMENT T_LPAREN T_STAR T_COMMA TypeName OptionalQuestionMark T_RPAREN { const SchemaType::Ptr t(parseInfo->staticContext->schemaDefinitions()->createSchemaType($5)); if(t) $$ = BuiltinTypes::element; else { parseInfo->staticContext->error(QtXmlPatterns::tr("%1 is an unknown schema type.") .arg(formatKeyword(parseInfo->staticContext->namePool(), $5)), ReportContext::XPST0008, fromYYLTYPE(@$, parseInfo)); } } OptionalQuestionMark: /* Empty. */ | T_QUESTION SchemaElementTest: T_SCHEMA_ELEMENT T_LPAREN ElementName T_RPAREN /* [135] */ { parseInfo->staticContext->error(QtXmlPatterns::tr("%1 is not in the in-scope attribute " "declarations. Note that the schema import " "feature is not supported.") .arg(formatKeyword(parseInfo->staticContext->namePool(), $3)), ReportContext::XPST0008, fromYYLTYPE(@$, parseInfo)); $$.reset(); } EmptyParanteses: T_LPAREN T_RPAREN /* [X] */ AttributeName: T_NCNAME /* [137] */ { $$ = parseInfo->staticContext->namePool()->allocateQName(StandardNamespaces::empty, $1); } | QName /* * When a QName appear with no prefix, it uses a certain default namespace * depending on where the QName occurs. These two rules, invoked in the appropriate * contexts, performs this distinction. */ ElementName: T_NCNAME /* [138] */ { if(parseInfo->nodeTestSource == BuiltinTypes::element) $$ = parseInfo->staticContext->namePool()->allocateQName(parseInfo->staticContext->namespaceBindings()->lookupNamespaceURI(StandardPrefixes::empty), $1); else $$ = parseInfo->staticContext->namePool()->allocateQName(StandardNamespaces::empty, $1); } | QName TypeName: ElementName /* [139] */ FunctionName: NCName /* [X] */ | QName NCName: T_NCNAME { $$ = parseInfo->staticContext->namePool()->allocateQName(parseInfo->staticContext->defaultFunctionNamespace(), $1); } | T_INTERNAL_NAME T_NCNAME { $$ = parseInfo->staticContext->namePool()->allocateQName(StandardNamespaces::InternalXSLT, $2); } LexicalName: T_NCNAME | T_QNAME PragmaName: T_NCNAME /* [X] */ { parseInfo->staticContext->error(QtXmlPatterns::tr("The name of an extension expression must be in " "a namespace."), ReportContext::XPST0081, fromYYLTYPE(@$, parseInfo)); } | QName URILiteral: StringLiteral /* [140] */ StringLiteral: T_STRING_LITERAL /* [144] */ { allowedIn(QueryLanguages(QXmlQuery::XQuery10 | QXmlQuery::XPath20), parseInfo, @$); } | T_XPATH2_STRING_LITERAL { allowedIn(QueryLanguages(QXmlQuery::XQuery10 | QXmlQuery::XPath20), parseInfo, @$); } QName: T_QNAME /* [154] */ { const ReflectYYLTYPE ryy(@$, parseInfo); $$ = QNameConstructor:: expandQName($1, parseInfo->staticContext, parseInfo->staticContext->namespaceBindings(), &ryy); } | T_CLARK_NAME { $$ = parseInfo->staticContext->namePool()->fromClarkName($1); } %% QString Tokenizer::tokenToString(const Token &token) { switch(token.type) { case T_NCNAME: /* Fallthrough. */ case T_QNAME: /* Fallthrough. */ case T_NUMBER: /* Fallthrough. */ case T_XPATH2_NUMBER: return token.value; case T_STRING_LITERAL: return QLatin1Char('"') + token.value + QLatin1Char('"'); default: { const QString raw(QString::fromLatin1(yytname[YYTRANSLATE(token.type)])); /* Remove the quotes. */ if(raw.at(0) == QLatin1Char('"') && raw.length() > 1) return raw.mid(1, raw.length() - 2); else return raw; } } } } /* namespace Patternist */ QT_END_NAMESPACE // vim: et:ts=4:sw=4:sts=4:syntax=yacc