Expression* InterpreterEmulator::InterpreterExpressionVisitor::VisitBind(Expression::List const& rExprList) { Expression::List SmplExprList; for (Expression* pExpr : rExprList) SmplExprList.push_back(pExpr->Visit(this)); return new BindExpression(SmplExprList); }
Expression::Ptr ElementConstructor::typeCheck(const StaticContext::Ptr &context, const SequenceType::Ptr &reqType) { /* What does this code do? When type checking our children, our namespace * bindings, which are also children of the form of NamespaceConstructor * instances, must be statically in-scope for them, so find them and * shuffle their bindings into the StaticContext. */ m_staticBaseURI = context->baseURI(); /* Namespace declarations changes the in-scope bindings, so let's * first lookup our child NamespaceConstructors. */ const ID operandID = m_operand2->id(); NamespaceResolver::Bindings overrides; if(operandID == IDExpressionSequence) { const Expression::List operands(m_operand2->operands()); const int len = operands.count(); for(int i = 0; i < len; ++i) { if(operands.at(i)->is(IDNamespaceConstructor)) { const QXmlName &nb = operands.at(i)->as<NamespaceConstructor>()->namespaceBinding(); overrides.insert(nb.prefix(), nb.namespaceURI()); } } } const NamespaceResolver::Ptr newResolver(new DelegatingNamespaceResolver(context->namespaceBindings(), overrides)); const StaticContext::Ptr augmented(new StaticNamespaceContext(newResolver, context)); return PairContainer::typeCheck(augmented, reqType); }
Expression::Ptr ElementConstructor::typeCheck(const StaticContext::Ptr &context, const SequenceType::Ptr &reqType) { m_staticBaseURI = context->baseURI(); /* Namespace declarations changes the in-scope bindings, so let's * first lookup our child NamespaceConstructors. */ const ID operandID = m_operand2->id(); NamespaceResolver::Bindings overrides; if(operandID == IDExpressionSequence) { const Expression::List operands(m_operand2->operands()); const int len = operands.count(); for(int i = 0; i < len; ++i) { if(operands.at(i)->is(IDNamespaceConstructor)) { const QXmlName &nb = operands.at(i)->as<NamespaceConstructor>()->namespaceBinding(); overrides.insert(nb.prefix(), nb.namespaceURI()); } } } const NamespaceResolver::Ptr newResolver(new DelegatingNamespaceResolver(context->namespaceBindings(), overrides)); const StaticContext::Ptr augmented(new StaticNamespaceContext(newResolver, context)); return PairContainer::typeCheck(augmented, reqType); }
void TripleContainer::setOperands(const Expression::List &ops) { Q_ASSERT(ops.count() == 3); m_operand1 = ops.first(); m_operand2 = ops.at(1); m_operand3 = ops.at(2); }
Expression* InterpreterEmulator::InterpreterExpressionVisitor::VisitBind(Expression::List const& rExprList) { Expression::List SmplExprList; for (auto itExpr = std::begin(rExprList); itExpr != std::end(rExprList); ++itExpr) SmplExprList.push_back((*itExpr)->Visit(this)); return new BindExpression(SmplExprList); }
Expression::Ptr OrderBy::typeCheck(const StaticContext::Ptr &context, const SequenceType::Ptr &reqType) { m_returnOrderBy->setStay(true); /* It's important we do the typeCheck() before calling OrderSpec::prepare(), since * atomizers must first be inserted. */ const Expression::Ptr me(SingleContainer::typeCheck(context, reqType)); const Expression::List ops(m_returnOrderBy->operands()); const int len = ops.count(); Q_ASSERT(ops.count() > 1); Q_ASSERT(m_orderSpecs.count() == ops.count() - 1); for(int i = 1; i < len; ++i) m_orderSpecs[i - 1].prepare(ops.at(i), context); return me; /* It's not meaningful to sort a single item or less, so rewrite ourselves * away if that is the case. This is an optimization. */ /* TODO: How do we remove ReturnOrderBy? if(Cardinality::zeroOrOne().isMatch(m_operand->staticType()->cardinality())) return m_operand->typeCheck(context, reqType); else return SingleContainer::typeCheck(context, reqType); */ }
Expression::Ptr ParseNonAmbiguousExpression(const string& code) { auto item = make_shared<GrammarStackItem>(); item->FillPredefinedSymbols(); auto stack = make_shared<GrammarStack>(); stack->Push(item); CodeToken::List tokens; GrammarStack::ResultList result; Tokenize(code, tokens); stack->ParseExpression(tokens.begin(), tokens.end(), result); Expression::List fullExpressions; for (auto r : result) { if (r.first == tokens.end()) { fullExpressions.push_back(r.second); } } TEST_ASSERT(fullExpressions.size() == 1); return fullExpressions[0]; }
Expression::List PairContainer::operands() const { Expression::List list; list.append(m_operand1); list.append(m_operand2); return list; }
Expression::List TripleContainer::operands() const { Expression::List result; result.append(m_operand1); result.append(m_operand2); result.append(m_operand3); return result; }
void PairContainer::setOperands(const Expression::List &ops) { Q_ASSERT(ops.count() == 2); m_operand1 = ops.first(); m_operand2 = ops.last(); Q_ASSERT(m_operand1); Q_ASSERT(m_operand2); }
Expression *BindExpression::Clone(void) const { Expression::List ExprListCloned; std::for_each(std::begin(m_Expressions), std::end(m_Expressions), [&](Expression *pExpr) { ExprListCloned.push_back(pExpr->Clone()); }); return new BindExpression(ExprListCloned); }
void CallTargetDescription::checkArgumentsCircularity(CallTargetDescription::List &signList, const Expression::Ptr callsite) { /* Check the arguments. */ const Expression::List ops(callsite->operands()); const Expression::List::const_iterator end(ops.constEnd()); Expression::List::const_iterator it(ops.constBegin()); for(; it != end; ++it) checkCallsiteCircularity(signList, *it); }
void Expression::typeCheckOperands(const StaticContext::Ptr &context) { const Expression::List ops(operands()); /* Check if this expression has any operands at all. */ if(ops.isEmpty()) return; /* We're done, early exit. */ const SequenceType::List opTypes(expectedOperandTypes()); Expression::List result; /* If we create a focus, we handle the last one specially, so avoid it in the loop. */ const bool createsFocus = has(CreatesFocusForLast); const SequenceType::List::const_iterator typeEnd(createsFocus ? --opTypes.constEnd() : opTypes.constEnd()); const Expression::List::const_iterator end(createsFocus ? --ops.constEnd() : ops.constEnd()); SequenceType::List::const_iterator reqType(opTypes.constBegin()); SequenceType::Ptr t(*reqType); // TODO we assign twice to t here(also below in loop) when ops.size() > 1 Expression::List::const_iterator it(ops.constBegin()); for(; it != end; ++it) { /* This ensures that the last expectedOperandType stays, and is * used for all other operands. This is used for expressions that * have an infinite amount of operands, such as the concat() function. */ if(reqType != typeEnd) { t = *reqType; ++reqType; } /* Let the child & its children typecheck. */ result.append((*it)->typeCheck(context, t)); } if(createsFocus) { const StaticContext::Ptr newContext(finalizeStaticContext(context)); result.append(ops.last()->typeCheck(newContext, opTypes.last())); } setOperands(result); }
ReturnOrderBy::ReturnOrderBy(const OrderBy::Stability aStability, const OrderBy::OrderSpec::Vector &oSpecs, const Expression::List &ops) : UnlimitedContainer(ops) , m_stability(aStability) , m_orderSpecs(oSpecs) , m_flyAway(true) { Q_ASSERT_X(m_operands.size() >= 2, Q_FUNC_INFO, "ReturnOrderBy must have the return expression, and at least one sort key."); Q_ASSERT(m_orderSpecs.size() == ops.size() - 1); }
Expression::Ptr ConstructorFunctionsFactory::retrieveExpression(const QXmlName name, const Expression::List &args, const FunctionSignature::Ptr &sign) const { Q_UNUSED(sign); /* This function is only called if the callsite is valid, so createSchemaType() will always * return an AtomicType. */ const AtomicType::Ptr at(static_cast<AtomicType *>(m_typeFactory->createSchemaType(name).data())); return Expression::Ptr(new CastAs(args.first(), makeGenericSequenceType(at, Cardinality::zeroOrOne()))); }
Expression::Ptr AbstractFunctionFactory::createFunctionCall(const QXmlName name, const Expression::List &args, const StaticContext::Ptr &context, const SourceLocationReflection *const r) { const FunctionSignature::Ptr sign(retrieveFunctionSignature(context->namePool(), name)); if(!sign) /* The function doesn't exist(at least not in this factory). */ return Expression::Ptr(); /* May throw. */ verifyArity(sign, context, args.count(), r); /* Ok, the function does exist and the arity is correct. */ return retrieveExpression(name, args, sign); }
Expression::Ptr ExpressionSequence::compress(const StaticContext::Ptr &context) { const Expression::Ptr me(UnlimitedContainer::compress(context)); if(me != this) return me; Expression::List::const_iterator it(m_operands.constBegin()); const Expression::List::const_iterator end(m_operands.constEnd()); Expression::List result; for(; it != end; ++it) { const ID Id = (*it)->id(); /* Remove empty sequences. This is rather important because we have some steps in the parser that * intentionally, unconditionally and for temporary reasons create expressions like (expr, ()). Of course, * empty sequences also occur as part of optimizations. * * User function call sites that are of type empty-sequence() must be avoided since * they may contain calls to fn:error(), which we would rewrite away otherwise. */ if(Id != IDUserFunctionCallsite && (*it)->staticType()->cardinality().isEmpty()) { /* Rewrite "(1, (), 2)" into "(1, 2)" by not * adding (*it) to result. */ continue; } else if(Id == IDExpressionSequence) { /* Rewrite "(1, (2, 3), 4)" into "(1, 2, 3, 4)" */ Expression::List::const_iterator seqIt((*it)->operands().constBegin()); const Expression::List::const_iterator seqEnd((*it)->operands().constEnd()); for(; seqIt != seqEnd; ++seqIt) result.append(*seqIt); } else result.append(*it); } if(result.isEmpty()) return EmptySequence::create(this, context); else if(result.count() == 1) return result.first(); else { m_operands = result; return me; } }
GrammarStack::Ptr ParseAmbiguousStatement(const string& code, Expression::List& expressionResult, GrammarStack::Ptr stack = nullptr) { if (!stack) { auto item = make_shared<GrammarStackItem>(); item->FillPredefinedSymbols(); stack = make_shared<GrammarStack>(); stack->Push(item); } CodeToken::List tokens; GrammarStack::ResultList result; Tokenize(code, tokens); stack->ParseStatement(tokens.begin(), tokens.end(), result); for (auto r : result) { expressionResult.push_back(r.second); } return stack; }
void SingleContainer::setOperands(const Expression::List &ops) { Q_ASSERT(ops.count() == 1); m_operand = ops.first(); }
Expression::List SingleContainer::operands() const { Expression::List list; list.append(m_operand); return list; }
Expression::Ptr TypeChecker::verifyType(const Expression::Ptr &operand, const SequenceType::Ptr &reqSeqType, const StaticContext::Ptr &context, const ReportContext::ErrorCode code, const Options options) { const ItemType::Ptr reqType(reqSeqType->itemType()); const Expression::Properties props(operand->properties()); /* If operand requires a focus, do the necessary type checking for that. */ if(props.testFlag(Expression::RequiresFocus) && options.testFlag(CheckFocus)) { const ItemType::Ptr contextType(context->contextItemType()); if(contextType) { if(props.testFlag(Expression::RequiresContextItem)) { Q_ASSERT_X(operand->expectedContextItemType(), Q_FUNC_INFO, "When the Expression sets the RequiresContextItem property, it must " "return a type in expectedContextItemType()"); const ItemType::Ptr expectedContextType(operand->expectedContextItemType()); /* Allow the empty sequence. We don't want to trigger XPTY0020 on ()/... . */ if(!expectedContextType->xdtTypeMatches(contextType) && contextType != CommonSequenceTypes::Empty) { context->error(wrongType(context->namePool(), operand->expectedContextItemType(), contextType), ReportContext::XPTY0020, operand.data()); return operand; } } } else { context->error(QtXmlPatterns::tr("The focus is undefined."), ReportContext::XPDY0002, operand.data()); return operand; } } SequenceType::Ptr operandSeqType(operand->staticType()); ItemType::Ptr operandType(operandSeqType->itemType()); /* This returns the operand if the types are identical or if operandType * is a subtype of reqType. */ if(reqType->xdtTypeMatches(operandType) || *operandType == *CommonSequenceTypes::Empty) return operand; /* Since we haven't exited yet, it means that the operandType is a super type * of reqType, and that there hence is a path down to it through the * type hierachy -- but that doesn't necessarily mean that a up-cast(down the * hierarchy) would succeed. */ Expression::Ptr result(operand); if(reqType->isAtomicType()) { const Expression::ID opID = operand->id(); if((opID == Expression::IDArgumentReference || (opID == Expression::IDCardinalityVerifier && operand->operands().first()->is(Expression::IDArgumentReference))) && *BuiltinTypes::item == *operandType) return Expression::Ptr(new ArgumentConverter(result, reqType)); if(!operandType->isAtomicType()) { result = Expression::Ptr(new Atomizer(result)); /* The atomizer might know more about the type. */ operandType = result->staticType()->itemType(); } if(reqType->xdtTypeMatches(operandType)) { /* Atomization was sufficient. Either the expected type is xs:anyAtomicType * or the type the Atomizer knows it returns, matches the required type. */ return result; } const bool compatModeEnabled = context->compatModeEnabled(); if((options.testFlag(AutomaticallyConvert) && BuiltinTypes::xsUntypedAtomic->xdtTypeMatches(operandType)) || (compatModeEnabled && BuiltinTypes::xsString->xdtTypeMatches(reqType))) { if(*reqType == *BuiltinTypes::numeric) { result = typeCheck(new UntypedAtomicConverter(result, BuiltinTypes::xsDouble, code), context, reqSeqType); } else result = typeCheck(new UntypedAtomicConverter(result, reqType, code), context, reqSeqType); /* The UntypedAtomicConverter might know more about the type, so reload. */ operandType = result->staticType()->itemType(); } else if(compatModeEnabled && *reqType == *BuiltinTypes::xsDouble) { const FunctionFactory::Ptr functions(context->functionSignatures()); Expression::List numberArgs; numberArgs.append(operand); result = functions->createFunctionCall(QXmlName(StandardNamespaces::fn, StandardLocalNames::number), numberArgs, context, operand.data())->typeCheck(context, reqSeqType); operandType = result->staticType()->itemType(); context->wrapExpressionWith(operand.data(), result); } if(reqType->xdtTypeMatches(operandType)) return result; /* Test if promotion will solve it; the xdtTypeMatches didn't * do that. */ if(options.testFlag(AutomaticallyConvert) && promotionPossible(operandType, reqType, context)) { if(options.testFlag(GeneratePromotion)) return Expression::Ptr(new UntypedAtomicConverter(result, reqType)); else return result; } if(operandType->xdtTypeMatches(reqType)) { /* For example, operandType is numeric, and reqType is xs:integer. */ return Expression::Ptr(new ItemVerifier(result, reqType, code)); } else { context->error(wrongType(context->namePool(), reqType, operandType), code, operand.data()); return result; } } else if(reqType->isNodeType()) { ReportContext::ErrorCode myCode; if(*reqType == *CommonSequenceTypes::EBV->itemType()) myCode = ReportContext::FORG0006; else myCode = code; /* empty-sequence() is considered valid because it's ok to do * for example nilled( () ). That is, to pass an empty sequence to a * function requiring for example node()?. */ if(*operandType == *CommonSequenceTypes::Empty) return result; else if(!operandType->xdtTypeMatches(reqType)) { context->error(wrongType(context->namePool(), reqType, operandType), myCode, operand.data()); return result; } /* Operand must be an item. Thus, the sequence can contain both * nodes and atomic values: we have to verify. */ return Expression::Ptr(new ItemVerifier(result, reqType, myCode)); } else { Q_ASSERT(*reqType == *CommonSequenceTypes::Empty); /* element() doesn't match empty-sequence(), but element()* does. */ if(!reqType->xdtTypeMatches(operandType) && !operandSeqType->cardinality().allowsEmpty()) { context->error(wrongType(context->namePool(), reqType, operandType), code, operand.data()); return result; } } /* This line should be reached if required type is * EBVType, and the operand is compatible. */ return result; }
Expression::Ptr Expression::invokeOptimizers(const Expression::Ptr &expr, const StaticContext::Ptr &context) { Q_ASSERT(expr); const OptimizationPass::List opts(expr->optimizationPasses()); if(opts.isEmpty()) /* Early exit. */ { return expr; } const OptimizationPass::List::const_iterator passEnd(opts.constEnd()); const OptimizationPass::List::const_iterator end(opts.constEnd()); OptimizationPass::List::const_iterator passIt(opts.constBegin()); for(; passIt != passEnd; ++passIt) /* Invoke each optimization pass. */ { const OptimizationPass::Ptr pass(*passIt); /* Alias, for readability. */ OptimizationPass::ExpressionMarker sourceMarker(pass->sourceExpression); if(pass->startIdentifier && !pass->startIdentifier->matches(expr)) { /* This pass specified a start identifier and it did * not match -- let's try the next OptimizationPass. */ continue; } const ExpressionIdentifier::List::const_iterator idEnd(pass->operandIdentifiers.constEnd()); ExpressionIdentifier::List::const_iterator idIt(pass->operandIdentifiers.constBegin()); const Expression::List ops(expr->operands()); const Expression::List::const_iterator opEnd(ops.constEnd()); Expression::List::const_iterator opIt(ops.constBegin()); switch(pass->operandsMatchMethod) { case OptimizationPass::Sequential: { for(; opIt != opEnd; ++opIt) { const Expression::Ptr operand(*opIt); /* Alias, for readability. */ const ExpressionIdentifier::Ptr opIdentifier(*idIt); /* Alias, for readability. */ if(opIdentifier && !opIdentifier->matches(operand)) { break; } ++idIt; } if(opIt == opEnd) break; /* All operands matched, so this pass matched. */ else { /* The loop above did not finish which means all operands did not match. Therefore, this OptimizationPass did not match -- let's try the next one. */ continue; } } case OptimizationPass::AnyOrder: { Q_ASSERT_X(ops.count() == 2, Q_FUNC_INFO, "AnyOrder is currently only supported for Expressions with two operands."); if(pass->operandIdentifiers.first()->matches(ops.first()) && pass->operandIdentifiers.last()->matches(ops.last())) { break; } else if(pass->operandIdentifiers.first()->matches(ops.last()) && pass->operandIdentifiers.last()->matches(ops.first())) { sourceMarker.first() = 1; sourceMarker[1] = 0; break; /* This pass matched. */ } else continue; /* This pass didn't match, let's loop through the next pass. */ } } /* Figure out the source Expression, if any. */ Expression::List operands; Expression::Ptr sourceExpr; if(!sourceMarker.isEmpty()) { const OptimizationPass::ExpressionMarker::const_iterator mEnd(sourceMarker.constEnd()); OptimizationPass::ExpressionMarker::const_iterator mIt(sourceMarker.constBegin()); sourceExpr = expr; for(; mIt != mEnd; ++mIt) { Q_ASSERT(*mIt >= 0); sourceExpr = sourceExpr->operands().at(*mIt); } operands.append(sourceExpr); } if(operands.isEmpty()) { Q_ASSERT(pass->resultCreator); return pass->resultCreator->create(Expression::List(), context, expr.data())->compress(context); } else if(pass->resultCreator) return pass->resultCreator->create(operands, context, expr.data())->compress(context); else { return sourceExpr; } } return expr; }
Expression::Ptr DocumentFN::typeCheck(const StaticContext::Ptr &context, const SequenceType::Ptr &reqType) { /* See the class documentation for the rewrite that we're doing here. */ /* Generate type checking code for our operands such that they match. */ typeCheckOperands(context); const QSourceLocation myLocation(context->locationFor(this)); const FunctionFactory::Ptr functions(context->functionSignatures()); Expression::Ptr uriSource; { Expression::List distinctValuesArgs; distinctValuesArgs.append(m_operands.first()); uriSource = functions->createFunctionCall(QXmlName(StandardNamespaces::fn, StandardLocalNames::distinct_values), distinctValuesArgs, context, this); context->addLocation(uriSource.data(), myLocation); } const VariableSlotID rangeSlot = context->allocateRangeSlot(); const Expression::Ptr uriReference(new RangeVariableReference(uriSource, rangeSlot)); context->addLocation(uriReference.data(), myLocation); Expression::List docArgs; if(m_operands.count() == 2) { Expression::List baseUriArgs; baseUriArgs.append(uriReference); baseUriArgs.append(m_operands.at(1)); const Expression::Ptr fnBaseUri(functions->createFunctionCall(QXmlName(StandardNamespaces::fn, StandardLocalNames::resolve_uri), baseUriArgs, context, this)); context->addLocation(fnBaseUri.data(), myLocation); docArgs.append(fnBaseUri); } else docArgs.append(uriReference); const Expression::Ptr fnDoc(functions->createFunctionCall(QXmlName(StandardNamespaces::fn, StandardLocalNames::doc), docArgs, context, this)); context->addLocation(fnDoc.data(), myLocation); Expression::Ptr newMe(new ForClause(rangeSlot, uriSource, fnDoc, -1 /* We have no position variable. */)); Expression::Ptr oldMe(this); rewrite(oldMe, newMe, context); return newMe->typeCheck(context, reqType); }
ExpressionSequence::ExpressionSequence(const Expression::List &ops) : UnlimitedContainer(ops) { Q_ASSERT_X(1 < ops.count(), Q_FUNC_INFO, "It makes no sense to have an ExpressionSequence containing less than two expressions."); }