Item AbstractFloatMathematician<isDouble>::calculate(const Item &o1, const Operator op, const Item &o2, const QExplicitlySharedDataPointer<DynamicContext> &context) const { const Numeric *const num1 = o1.template as<Numeric>(); const Numeric *const num2 = o2.template as<Numeric>(); switch(op) { case Div: return toItem(AbstractFloat<isDouble>::fromValue(num1->toDouble() / num2->toDouble())); case IDiv: { if(num1->isNaN() || num2->isNaN()) { context->error(QtXmlPatterns::tr("No operand in an integer division, %1, can be %2.") .arg(formatKeyword("idiv")) .arg(formatData("NaN")), ReportContext::FOAR0002, this); } else if(num1->isInf()) { context->error(QtXmlPatterns::tr("The first operand in an integer division, %1, cannot be infinity (%2).") .arg(formatKeyword("idiv")) .arg(formatData("INF")), ReportContext::FOAR0002, this); } else if(num2->toInteger() == 0) context->error(QtXmlPatterns::tr("The second operand in a division, %1, cannot be zero (%2).") .arg(formatKeyword("idiv")) .arg(formatData("0")), ReportContext::FOAR0001, this); return Integer::fromValue(static_cast<xsInteger>(num1->toDouble() / num2->toDouble())); } case Substract: return toItem(AbstractFloat<isDouble>::fromValue(num1->toDouble() - num2->toDouble())); case Mod: return toItem(AbstractFloat<isDouble>::fromValue(::fmod(num1->toDouble(), num2->toDouble()))); case Multiply: return toItem(AbstractFloat<isDouble>::fromValue(num1->toDouble() * num2->toDouble())); case Add: return toItem(AbstractFloat<isDouble>::fromValue(num1->toDouble() + num2->toDouble())); } Q_ASSERT(false); return Item(); /* GCC unbarfer. */ }
AtomicComparator::Ptr ComparisonPlatform<TSubClass, issueError, comparisonType, errorCode>:: fetchComparator(const ItemType::Ptr &t1, const ItemType::Ptr &t2, const ReportContext::Ptr &context) const { Q_ASSERT(t1); Q_ASSERT(t2); if(*BuiltinTypes::xsAnyAtomicType == *t1 || *BuiltinTypes::xsAnyAtomicType == *t2 || *BuiltinTypes::item == *t1 || *BuiltinTypes::item == *t2 || *BuiltinTypes::numeric == *t1 || *BuiltinTypes::numeric == *t2 || *CommonSequenceTypes::Empty == *t1 || *CommonSequenceTypes::Empty == *t2) { /* The static type of(at least) one of the operands could not * be narrowed further, so we do the operator * lookup at runtime. */ return AtomicComparator::Ptr(); } const AtomicComparatorLocator::Ptr locator (static_cast<const AtomicType *>(t1.data())->comparatorLocator()); if(!locator) { if(issueError) { context->error(QtXmlPatterns::tr("No comparisons can be done involving the type %1.") .arg(formatType(context->namePool(), t1)), errorCode, static_cast<const TSubClass *>(this)->actualReflection()); } return AtomicComparator::Ptr(); } const AtomicComparator::Ptr comp(static_cast<const AtomicType *>(t2.data())->accept(locator, operatorID(), static_cast<const TSubClass *>(this)->actualReflection())); if(comp) return comp; else if(issueError) { context->error(QtXmlPatterns::tr("Operator %1 is not available between atomic values of type %2 and %3.") .arg(formatKeyword(AtomicComparator::displayName(operatorID(), comparisonType)), formatType(context->namePool(), t1), formatType(context->namePool(), t2)), errorCode, static_cast<const TSubClass *>(this)->actualReflection()); } return AtomicComparator::Ptr(); }
Expression::Ptr ExpressionFactory::createExpression(const Tokenizer::Ptr &tokenizer, const StaticContext::Ptr &context, const QXmlQuery::QueryLanguage lang, const SequenceType::Ptr &requiredType, const QUrl &queryURI, const QXmlName &initialTemplateName) { Q_ASSERT(context); Q_ASSERT(requiredType); Q_ASSERT(queryURI.isValid()); Tokenizer::Ptr effectiveTokenizer(tokenizer); #ifdef Patternist_DEBUG effectiveTokenizer = Tokenizer::Ptr(new TokenRevealer(queryURI, tokenizer)); #endif OptimizationPasses::Coordinator::init(); const ParserContext::Ptr info(new ParserContext(context, lang, effectiveTokenizer.data())); info->initialTemplateName = initialTemplateName; effectiveTokenizer->setParserContext(info); const int bisonRetval = QPatternist::XPathparse(info.data()); Q_ASSERT_X(bisonRetval == 0, Q_FUNC_INFO, "We shouldn't be able to get an error, because we throw exceptions."); Q_UNUSED(bisonRetval); /* Needed when not compiled in debug mode, since bisonRetval won't * be used in the Q_ASSERT_X above. */ Expression::Ptr result(info->queryBody); if(!result) { context->error(QtXmlPatterns::tr("A library module cannot be evaluated " "directly. It must be imported from a " "main module."), ReportContext::XPST0003, QSourceLocation(queryURI, 1, 1)); } /* Optimization: I think many things are done in the wrong order below. We * probably want everything typechecked before compressing, since we can * have references all over the place(variable references, template * invocations, function callsites). This could even be a source to bugs. */ /* Here, we type check user declared functions and global variables. This * means that variables and functions that are not used are type * checked(which they otherwise wouldn't have been), and those which are * used, are type-checked twice, unfortunately. */ const bool hasExternalFocus = context->contextItemType(); if(lang == QXmlQuery::XSLT20) { /* Bind xsl:call-template instructions to their template bodies. * * We do this before type checking and compressing them, because a * CallTemplate obviously needs its template before being compressed. * * Also, we do this before type checking and compressing user * functions, since they can contain template call sites. */ for(int i = 0; i < info->templateCalls.count(); ++i) { CallTemplate *const site = info->templateCalls.at(i)->as<CallTemplate>(); const QXmlName targetName(site->name()); const Template::Ptr t(info->namedTemplates.value(targetName)); if(t) site->setTemplate(t); else { context->error(QtXmlPatterns::tr("No template by name %1 exists.").arg(formatKeyword(context->namePool(), targetName)), ReportContext::XTSE0650, site); } } } /* Type check and compress user functions. */ { const UserFunction::List::const_iterator end(info->userFunctions.constEnd()); UserFunction::List::const_iterator it(info->userFunctions.constBegin()); /* If the query has a focus(which is common, in the case of a * stylesheet), we must ensure that the focus isn't visible in the * function body. */ StaticContext::Ptr effectiveContext; if(hasExternalFocus) { effectiveContext = StaticContext::Ptr(new StaticFocusContext(ItemType::Ptr(), context)); } else effectiveContext = context; for(; it != end; ++it) { pDebug() << "----- User Function Typecheck -----"; registerLastPath((*it)->body()); /* We will most likely call body()->typeCheck() again, once for * each callsite. That is, it will be called from * UserFunctionCallsite::typeCheck(), which will be called * indirectly when we check the query body. */ const Expression::Ptr typeCheck((*it)->body()->typeCheck(effectiveContext, (*it)->signature()->returnType())); /* We don't have to call (*it)->setBody(typeCheck) here since it's * only used directly below. */ processTreePass(typeCheck, UserFunctionTypeCheck); pDebug() << "------------------------------"; pDebug() << "----- User Function Compress -----"; const Expression::Ptr comp(typeCheck->compress(effectiveContext)); (*it)->setBody(comp); processTreePass(comp, UserFunctionCompression); pDebug() << "------------------------------"; } } /* Type check and compress global variables. */ { const VariableDeclaration::Stack::const_iterator vend(info->variables.constEnd()); VariableDeclaration::Stack::const_iterator vit(info->variables.constBegin()); for(; vit != vend; ++vit) { Q_ASSERT(*vit); /* This is a bit murky, the global variable will have it * Expression::typeCheck() function called from all its references, * but we also want to check it here globally, so we do * typechecking using a proper focus. */ if((*vit)->type == VariableDeclaration::ExternalVariable) continue; pDebug() << "----- Global Variable Typecheck -----"; Q_ASSERT((*vit)->expression()); /* We supply ZeroOrMoreItems, meaning the variable can evaluate to anything. */ // FIXME which is a source to bugs // TODO What about compressing variables? const Expression::Ptr nev((*vit)->expression()->typeCheck(context, CommonSequenceTypes::ZeroOrMoreItems)); processTreePass(nev, GlobalVariableTypeCheck); pDebug() << "------------------------------"; } } /* Do all tests specific to XSL-T. */ if(lang == QXmlQuery::XSLT20) { /* Type check and compress named templates. */ { pDebug() << "Have " << info->namedTemplates.count() << "named templates"; QMutableHashIterator<QXmlName, Template::Ptr> it(info->namedTemplates); while(it.hasNext()) { it.next(); processNamedTemplate(it.key(), it.value()->body, TemplateInitial); it.value()->body = it.value()->body->typeCheck(context, CommonSequenceTypes::ZeroOrMoreItems); processNamedTemplate(it.key(), it.value()->body, TemplateTypeCheck); it.value()->body = it.value()->body->compress(context); processNamedTemplate(it.key(), it.value()->body, TemplateCompress); it.value()->compileParameters(context); } } /* Type check and compress template rules. */ { QHashIterator<QXmlName, TemplateMode::Ptr> it(info->templateRules); /* Since a pattern can exist of AxisStep, its typeCheck() stage * requires a focus. In the case that we're invoked with a name but * no focus, this will yield a compile error, unless we declare a * focus manually. This only needs to be done for the pattern * expression, since the static type of the pattern is used as the * static type for the focus of the template body. */ StaticContext::Ptr patternContext; if(hasExternalFocus) patternContext = context; else patternContext = StaticContext::Ptr(new StaticFocusContext(BuiltinTypes::node, context)); /* For each template pattern. */ while(it.hasNext()) { it.next(); const TemplateMode::Ptr &mode = it.value(); const int len = mode->templatePatterns.count(); TemplatePattern::ID currentTemplateID = -1; bool hasDoneItOnce = false; /* For each template pattern. */ for(int i = 0; i < len; ++i) { /* We can't use references for these two members, since we * assign to them. */ const TemplatePattern::Ptr &pattern = mode->templatePatterns.at(i); Expression::Ptr matchPattern(pattern->matchPattern()); processTemplateRule(pattern->templateTarget()->body, pattern, mode->name(), TemplateInitial); matchPattern = matchPattern->typeCheck(patternContext, CommonSequenceTypes::ZeroOrMoreItems); matchPattern = matchPattern->compress(patternContext); pattern->setMatchPattern(matchPattern); if(currentTemplateID == -1 && hasDoneItOnce) { currentTemplateID = pattern->id(); continue; } else if(currentTemplateID == pattern->id() && hasDoneItOnce) { hasDoneItOnce = false; continue; } hasDoneItOnce = true; currentTemplateID = pattern->id(); Expression::Ptr body(pattern->templateTarget()->body); /* Patterns for a new template has started, we must * deal with the body & parameters. */ { /* TODO type is wrong, it has to be the union of all * patterns. */ const StaticContext::Ptr focusContext(new StaticFocusContext(matchPattern->staticType()->itemType(), context)); body = body->typeCheck(focusContext, CommonSequenceTypes::ZeroOrMoreItems); pattern->templateTarget()->compileParameters(focusContext); } processTemplateRule(body, pattern, mode->name(), TemplateTypeCheck); body = body->compress(context); pattern->templateTarget()->body = body; processTemplateRule(body, pattern, mode->name(), TemplateCompress); } mode->finalize(); } } /* Add templates in mode #all to all other modes. * * We do this after the templates has been typechecked and compressed, * since otherwise it will be done N times for the built-in templates, * where N is the count of different templates, instead of once. */ { const QXmlName nameModeAll(QXmlName(StandardNamespaces::InternalXSLT, StandardLocalNames::all)); const TemplateMode::Ptr &modeAll = info->templateRules[nameModeAll]; Q_ASSERT_X(modeAll, Q_FUNC_INFO, "We should at least have the builtin templates."); QHashIterator<QXmlName, TemplateMode::Ptr> it(info->templateRules); while(it.hasNext()) { it.next(); /* Don't add mode #all to mode #all. */ if(it.key() == nameModeAll) continue; it.value()->addMode(modeAll); } } } /* Type check and compress the query body. */ { pDebug() << "----- Initial AST build. -----"; processTreePass(result, QueryBodyInitial); pDebug() << "------------------------------"; pDebug() << "----- Type Check -----"; registerLastPath(result); result->rewrite(result, result->typeCheck(context, requiredType), context); processTreePass(result, QueryBodyTypeCheck); pDebug() << "------------------------------"; pDebug() << "----- Compress -----"; result->rewrite(result, result->compress(context), context); processTreePass(result, QueryBodyCompression); pDebug() << "------------------------------"; } return result; }
void MaintainingReader<TokenLookupClass, LookupKey>::validateElement(const LookupKey elementName) const { Q_ASSERT(tokenType() == QXmlStreamReader::StartElement); if(m_elementDescriptions.contains(elementName)) { const ElementDescription<TokenLookupClass, LookupKey> &desc = m_elementDescriptions.value(elementName); const int attCount = m_currentAttributes.count(); QSet<typename TokenLookupClass::NodeName> encounteredXSLTAtts; for(int i = 0; i < attCount; ++i) { const QXmlStreamAttribute &attr = m_currentAttributes.at(i); if(attr.namespaceUri().isEmpty()) { const typename TokenLookupClass::NodeName attrName(TokenLookupClass::toToken(attr.name())); encounteredXSLTAtts.insert(attrName); if(!desc.requiredAttributes.contains(attrName) && !desc.optionalAttributes.contains(attrName) && !m_standardAttributes.contains(attrName) && !isAnyAttributeAllowed()) { QString translationString; QList<typename TokenLookupClass::NodeName> all(desc.requiredAttributes.toList() + desc.optionalAttributes.toList()); const int totalCount = all.count(); QStringList allowed; for(int i = 0; i < totalCount; ++i) allowed.append(formatKeyword(toString(all.at(i)))); /* Note, we can't run toString() on attrName, because we're in this branch, * the token lookup doesn't have the string(!).*/ const QString stringedName(attr.name().toString()); if(totalCount == 0) { translationString = QtXmlPatterns::tr("Attribute %1 cannot appear on the element %2. Only the standard attributes can appear.") .arg(formatKeyword(stringedName), formatKeyword(name())); } else if(totalCount == 1) { translationString = QtXmlPatterns::tr("Attribute %1 cannot appear on the element %2. Only %3 is allowed, and the standard attributes.") .arg(formatKeyword(stringedName), formatKeyword(name()), allowed.first()); } else if(totalCount == 1) { /* Note, allowed has already had formatKeyword() applied. */ translationString = QtXmlPatterns::tr("Attribute %1 cannot appear on the element %2. Allowed is %3, %4, and the standard attributes.") .arg(formatKeyword(stringedName), formatKeyword(name()), allowed.first(), allowed.last()); } else { /* Note, allowed has already had formatKeyword() applied. */ translationString = QtXmlPatterns::tr("Attribute %1 cannot appear on the element %2. Allowed is %3, and the standard attributes.") .arg(formatKeyword(stringedName), formatKeyword(name()), allowed.join(QLatin1String(", "))); } m_context->error(translationString, ReportContext::XTSE0090, currentLocation()); } } else if(attr.namespaceUri() == namespaceUri()) { m_context->error(QtXmlPatterns::tr("XSL-T attributes on XSL-T elements must be in the null namespace, not in the XSL-T namespace which %1 is.") .arg(formatKeyword(attr.name())), ReportContext::XTSE0090, currentLocation()); } /* Else, attributes in other namespaces are allowed, continue. */ } const QSet<typename TokenLookupClass::NodeName> requiredButMissing(QSet<typename TokenLookupClass::NodeName>(desc.requiredAttributes).subtract(encounteredXSLTAtts)); if(!requiredButMissing.isEmpty()) { error(QtXmlPatterns::tr("The attribute %1 must appear on element %2.") .arg(formatKeyword(toString(*requiredButMissing.constBegin())), formatKeyword(name())), ReportContext::XTSE0010); } } else { error(QtXmlPatterns::tr("The element with local name %1 does not exist in XSL-T.").arg(formatKeyword(name())), ReportContext::XTSE0010); } }
void AccelTreeBuilder<FromDocument>::attribute(const QXmlName &name, const QStringRef &value) { /* Attributes adds a namespace binding, so lets synthesize one. * * We optimize by checking whether we have a namespace for which a binding would * be generated. Happens relatively rarely. */ if(name.hasPrefix()) namespaceBinding(QXmlName(name.namespaceURI(), 0, name.prefix())); m_document->basicData.append(AccelTree::BasicNodeData(currentDepth(), currentParent(), QXmlNodeModelIndex::Attribute, 0, name)); ++m_preNumber; ++m_size.top(); m_isPreviousAtomic = false; if(name.namespaceURI() == StandardNamespaces::xml && name.localName() == StandardLocalNames::id) { const QString normalized(value.toString().simplified()); if(QXmlUtils::isNCName(normalized)) { const QXmlName::LocalNameCode id = m_namePool->allocateLocalName(normalized); const int oldSize = m_document->m_IDs.count(); m_document->m_IDs.insert(id, currentParent()); /* We don't run the value through m_attributeCompress here, because * the likelyhood of it deing identical to another attribute is * very small. */ m_document->data.insert(m_preNumber, normalized); /** * In the case that we're called for doc-available(), m_context is * null, and we need to flag somehow that we failed to load this * document. */ if(oldSize == m_document->m_IDs.count() && m_context) // TODO { Q_ASSERT(m_context); m_context->error(QtXmlPatterns::tr("An %1-attribute with value %2 has already been declared.") .arg(formatKeyword("xml:id"), formatData(normalized)), FromDocument ? ReportContext::FODC0002 : ReportContext::XQDY0091, this); } } else if(m_context) // TODO { Q_ASSERT(m_context); /* If we're building from an XML Document(e.g, we're fed from QXmlStreamReader, we raise FODC0002, * otherwise XQDY0091. */ m_context->error(QtXmlPatterns::tr("An %1-attribute must have a " "valid %2 as value, which %3 isn't.").arg(formatKeyword("xml:id"), formatType(m_namePool, BuiltinTypes::xsNCName), formatData(value.toString())), FromDocument ? ReportContext::FODC0002 : ReportContext::XQDY0091, this); } } else m_document->data.insert(m_preNumber, *m_attributeCompress.insert(value.toString())); }