public QilNode Function(string prefix, string name, IList <QilNode> args) { Debug.Assert(prefix.Length == 0); QilIterator i = _f.For(_fixupNode); QilNode matches; if (name == "id") { Debug.Assert( args.Count == 1 && args[0].NodeType == QilNodeType.LiteralString, "Function id() must have one literal string argument" ); matches = _f.Id(i, args[0]); } else { Debug.Assert(name == "key", "Unexpected function"); Debug.Assert( args.Count == 2 && args[0].NodeType == QilNodeType.LiteralString && args[1].NodeType == QilNodeType.LiteralString, "Function key() must have two literal string arguments" ); matches = _environment.ResolveFunction(prefix, name, args, new XsltFunctionFocus(i)); } QilIterator j; QilLoop result = _f.BaseFactory.Filter(i, _f.Not(_f.IsEmpty(_f.Filter(j = _f.For(matches), _f.Is(j, i))))); SetPriority(result, 0.5); SetLastParent(result, result); return(result); }
// a/b/c -> self::c[parent::b[parent::a]] // a/b//c -> self::c[ancestor::b[parent::a]] // a/b -> self::b[parent::a] // -> JoinStep(Axis('a'), Axis('b')) // -> Filter('b' & Parent(Filter('a'))) // a//b // -> JoinStep(Axis('a'), JoingStep(Axis(DescendantOrSelf), Axis('b'))) // -> JoinStep(Filter('a'), JoingStep(Nop(null), Filter('b'))) // -> JoinStep(Filter('a'), Nop(Filter('b'))) // -> Filter('b' & Ancestor(Filter('a'))) public QilNode JoinStep(QilNode left, QilNode right) { Debug.Assert(left != null); Debug.Assert(right != null); if (left.NodeType == QilNodeType.Nop) { QilUnary nop = (QilUnary)left; Debug.Assert(nop.Child == _fixupNode); nop.Child = right; // We use Nop as a flag that DescendantOrSelf axis was used between steps. return(nop); } Debug.Assert(GetLastParent(left) == left, "Left is always single axis and never the step"); Debug.Assert(left.NodeType == QilNodeType.Filter); CleanAnnotation(left); QilLoop parentFilter = (QilLoop)left; bool ancestor = false; { if (right.NodeType == QilNodeType.Nop) { ancestor = true; QilUnary nop = (QilUnary)right; Debug.Assert(nop.Child != null); right = nop.Child; } } Debug.Assert(right.NodeType == QilNodeType.Filter); QilLoop lastParent = GetLastParent(right); FixupFilterBinding(parentFilter, ancestor ? _f.Ancestor(lastParent.Variable) : _f.Parent(lastParent.Variable)); lastParent.Body = _f.And(lastParent.Body, _f.Not(_f.IsEmpty(parentFilter))); SetPriority(right, 0.5); SetLastParent(right, parentFilter); return(right); }
/// <summary> /// Recursively analyze the definition of a function. /// </summary> private static void AnalyzeDefinition(QilNode nd) { Debug.Assert(XmlILConstructInfo.Read(nd).PushToWriterLast, "Only need to analyze expressions which will be compiled in push mode."); switch (nd.NodeType) { case QilNodeType.Invoke: // Invoke node can either be compiled as IteratorThenWriter, or Writer. // Since IteratorThenWriter involves caching the results of the function call // and iterating over them, .tailcall cannot be used if (XmlILConstructInfo.Read(nd).ConstructMethod == XmlILConstructMethod.Writer) { OptimizerPatterns.Write(nd).AddPattern(OptimizerPatternName.TailCall); } break; case QilNodeType.Loop: { // Recursively analyze Loop return value QilLoop ndLoop = (QilLoop)nd; if (ndLoop.Variable.NodeType == QilNodeType.Let || !ndLoop.Variable.Binding.XmlType.MaybeMany) { AnalyzeDefinition(ndLoop.Body); } break; } case QilNodeType.Sequence: { // Recursively analyze last expression in Sequence QilList ndSeq = (QilList)nd; if (ndSeq.Count > 0) { AnalyzeDefinition(ndSeq[ndSeq.Count - 1]); } break; } case QilNodeType.Choice: { // Recursively analyze Choice branches QilChoice ndChoice = (QilChoice)nd; for (int i = 0; i < ndChoice.Branches.Count; i++) { AnalyzeDefinition(ndChoice.Branches[i]); } break; } case QilNodeType.Conditional: { // Recursively analyze Conditional branches QilTernary ndCond = (QilTernary)nd; AnalyzeDefinition(ndCond.Center); AnalyzeDefinition(ndCond.Right); break; } case QilNodeType.Nop: AnalyzeDefinition(((QilUnary)nd).Child); break; } }
public void AssertFilter(QilLoop filter) { Debug.Assert(filter.NodeType == QilNodeType.Filter, "XPathPatternBuilder expected to generate list of Filters on top level"); Debug.Assert(filter.Variable.XmlType.IsSubtypeOf(T.NodeNotRtf)); Debug.Assert(filter.Variable.Binding.NodeType == QilNodeType.Unknown); // fixupNode Debug.Assert(filter.Body.XmlType.IsSubtypeOf(T.Boolean)); }
private static void SetLastParent(QilNode node, QilLoop parent) { Debug.Assert(parent.NodeType == QilNodeType.Filter); Annotation ann = (Annotation)node.Annotation ?? new Annotation(); ann.Parent = parent; node.Annotation = ann; }
public QilNode GetLast() { QilLoop clone = (QilLoop)cloner.Clone(baseContext); QilIterator i = f.For(f.Parent(GetCurrent())); clone.Variable.Binding = f.Content(i); return(f.XsltConvert(f.Length(f.Loop(i, clone)), T.DoubleX)); }
// -------------------------------------- GetPredicateBuilder() --------------------------------------- public IXPathBuilder <QilNode> GetPredicateBuilder(QilNode ctx) { QilLoop context = (QilLoop)ctx; Debug.Assert(context != null, "Predicate always has step so it can't have context == null"); Debug.Assert(context.Variable.NodeType == QilNodeType.For, "It shouldn't be Let, becaus predicates in PatternBuilder don't produce cached tuples."); return(_predicateBuilder); }
/// <summary> /// Analyze loop. /// </summary> protected override void AnalyzeLoop(QilLoop ndLoop, XmlILConstructInfo info) { // Constructing attributes/namespaces in a loop can cause duplicates, namespaces after attributes, etc. if (ndLoop.XmlType.MaybeMany) { CheckAttributeNamespaceConstruct(ndLoop.XmlType); } base.AnalyzeLoop(ndLoop, info); }
public void AddTemplateMatch(Template template, QilLoop filter) { List <TemplateMatch> matchesForMode; if (!TemplateMatches.TryGetValue(template.Mode, out matchesForMode)) { matchesForMode = TemplateMatches[template.Mode] = new List <TemplateMatch>(); } matchesForMode.Add(new TemplateMatch(template, filter)); }
//The structure of result is a Filter, variable is current node, body is the match condition. //Previous predicate build logic in XPathPatternBuilder is match from right to left, which have 2^n complexiy when have lots of position predicates. TFS #368771 //Now change the logic to: If predicates contains position/last predicates, given the current node, filter out all the nodes that match the predicates, //and then check if current node is in the result set. public QilNode BuildPredicates(QilNode nodeset, List <QilNode> predicates) { //convert predicates to boolean type List <QilNode> convertedPredicates = new List <QilNode>(predicates.Count); foreach (var predicate in predicates) { convertedPredicates.Add(XPathBuilder.PredicateToBoolean(predicate, _f, _predicateEnvironment)); } QilLoop nodeFilter = (QilLoop)nodeset; QilIterator current = nodeFilter.Variable; //If no last() and position() in predicates, use nodeFilter.Variable to fixup current //because all the predicates only based on the input variable, no matter what other predicates are. if (_predicateEnvironment.numFixupLast == 0 && _predicateEnvironment.numFixupPosition == 0) { foreach (var predicate in convertedPredicates) { nodeFilter.Body = _f.And(nodeFilter.Body, predicate); } nodeFilter.Body = _predicateEnvironment.fixupVisitor.Fixup(nodeFilter.Body, current, null); } //If any preidcate contains last() or position() node, then the current node is based on previous predicates, //for instance, a[...][2] is match second node after filter 'a[...]' instead of second 'a'. else { //filter out the siblings QilIterator parentIter = _f.For(_f.Parent(current)); QilNode sibling = _f.Content(parentIter); //generate filter based on input filter QilLoop siblingFilter = (QilLoop)nodeset.DeepClone(_f.BaseFactory); siblingFilter.Variable.Binding = sibling; siblingFilter = (QilLoop)_f.Loop(parentIter, siblingFilter); //build predicates from left to right to get all the matching nodes QilNode matchingSet = siblingFilter; foreach (var predicate in convertedPredicates) { matchingSet = XPathBuilder.BuildOnePredicate(matchingSet, predicate, /*isReverseStep*/ false, _f, _predicateEnvironment.fixupVisitor, ref _predicateEnvironment.numFixupCurrent, ref _predicateEnvironment.numFixupPosition, ref _predicateEnvironment.numFixupLast); } //check if the matching nodes contains the current node QilIterator matchNodeIter = _f.For(matchingSet); QilNode filterCurrent = _f.Filter(matchNodeIter, _f.Is(matchNodeIter, current)); nodeFilter.Body = _f.Not(_f.IsEmpty(filterCurrent)); //for passing type check, explicit say the result is target type nodeFilter.Body = _f.And(_f.IsType(current, nodeFilter.XmlType), nodeFilter.Body); } SetPriority(nodeset, 0.5); return(nodeset); }
QilNode BuildAxisFilter(QilNode qilAxis, XPathAxis xpathAxis, XPathNodeType nodeType, string name, string nsUri) { XmlNodeKindFlags original = qilAxis.XmlType.NodeKinds; XmlNodeKindFlags required = AxisTypeMask(original, nodeType, xpathAxis); QilIterator itr; if (required == 0) { return(f.Sequence()); } else if (required == original) { } else { qilAxis = f.Filter(itr = f.For(qilAxis), f.IsType(itr, T.NodeChoice(required))); qilAxis.XmlType = T.PrimeProduct(T.NodeChoice(required), qilAxis.XmlType.Cardinality); // Without code bellow IlGeneragion gives stack overflow exception for the following passage. //<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> // <xsl:template match="/"> // <xsl:value-of select="descendant::author/@id | comment()" /> // </xsl:template> //</xsl:stylesheet> // ToDo: remove this code when IlGen bug will be fixed. if (qilAxis.NodeType == QilNodeType.Filter) { QilLoop filter = (QilLoop)qilAxis; filter.Body = f.And(filter.Body, name != null && nsUri != null ? f.Eq(f.NameOf(itr), f.QName(name, nsUri)) : // ns:bar || bar nsUri != null ? f.Eq(f.NamespaceUriOf(itr), f.String(nsUri)) : // ns:* name != null ? f.Eq(f.LocalNameOf(itr), f.String(name)) : // *:foo /*name == nsUri == null*/ f.True() // * ); return(filter); } } return(f.Filter(itr = f.For(qilAxis), name != null && nsUri != null ? f.Eq(f.NameOf(itr), f.QName(name, nsUri)) : // ns:bar || bar nsUri != null ? f.Eq(f.NamespaceUriOf(itr), f.String(nsUri)) : // ns:* name != null ? f.Eq(f.LocalNameOf(itr), f.String(name)) : // *:foo /*name == nsUri == null*/ f.True() // * )); }
public TemplateMatch(Template template, QilLoop filter) { _template = template; _priority = double.IsNaN(template.Priority) ? XPathPatternBuilder.GetPriority(filter) : template.Priority; _iterator = filter.Variable; _condition = filter.Body; XPathPatternBuilder.CleanAnnotation(filter); NipOffTypeNameCheck(); Debug.Assert( _qname == null || _nodeKind == XmlNodeKindFlags.Element || _nodeKind == XmlNodeKindFlags.Attribute || _nodeKind == XmlNodeKindFlags.PI, "qname may be not null only for element, attribute, or PI patterns" ); }
public QilNode Predicate(QilNode node, QilNode condition, bool isReverseStep) { Debug.Assert(isReverseStep == false, "patterns can't have reverse axe"); QilLoop nodeFilter = (QilLoop)node; if (condition.XmlType.TypeCode == XmlTypeCode.Double) { predicateEnvironment.SetContext(nodeFilter); condition = f.Eq(condition, predicateEnvironment.GetPosition()); } else { condition = f.ConvertToBoolean(condition); } nodeFilter.Body = f.And(nodeFilter.Body, condition); SetPriority(node, 0.5); return(node); }
/// <summary> /// Analyze loop. /// </summary> protected virtual void AnalyzeLoop(QilLoop ndLoop, XmlILConstructInfo info) { XmlQueryType typ = ndLoop.XmlType; // Ensure that construct method is Writer info.ConstructMethod = XmlILConstructMethod.Writer; if (!typ.IsSingleton) { StartLoop(typ, info); } // Body constructs content ndLoop.Body = AnalyzeContent(ndLoop.Body); if (!typ.IsSingleton) { EndLoop(typ, info); } }
public QilNode GetPosition() { QilLoop clone = (QilLoop)cloner.Clone(baseContext); XmlNodeKindFlags nodeKinds = baseContext.XmlType.NodeKinds; // baseContext either always returns attributes (attribute::), or never returns attributes or namespaces (child::) if (nodeKinds == XmlNodeKindFlags.Attribute) { QilIterator i = f.For(f.Parent(GetCurrent())); clone.Variable.Binding = f.Content(i); clone.Body = f.And(clone.Body, f.Before(clone.Variable, GetCurrent())); clone = f.BaseFactory.Loop(i, clone); } else { Debug.Assert((nodeKinds & (XmlNodeKindFlags.Attribute | XmlNodeKindFlags.Namespace)) == XmlNodeKindFlags.None); clone.Variable.Binding = f.PrecedingSibling(GetCurrent()); } return(f.Add(f.Double(1), f.XsltConvert(f.Length(clone), T.DoubleX))); }
// Filers that travers Content being converted to global travers: // Filter($j= ... Filter($i = Content(fixup), ...)) -> Filter($j= ... Filter($i = Loop($j = DesendentOrSelf(Root(fixup)), Content($j), ...))) protected override QilNode VisitLoop(QilLoop n) { if (n.Variable.Binding.NodeType == QilNodeType.Root || n.Variable.Binding.NodeType == QilNodeType.Deref) { // This is absolute path already. We shouldn't touch it return(n); } if (n.Variable.Binding.NodeType == QilNodeType.Content) { // This is "begin" of reletive path. Let's rewrite it as absolute: QilUnary content = (QilUnary)n.Variable.Binding; Debug.Assert(content.Child == this.fixup, "Unexpected content node"); QilIterator it = f.For(f.DescendantOrSelf(f.Root(this.fixup))); content.Child = it; n.Variable.Binding = f.Loop(it, content); return(n); } n.Variable.Binding = Visit(n.Variable.Binding); return(n); }
QilNode BuildAxisFilter(QilNode qilAxis, XPathAxis xpathAxis, XPathNodeType nodeType, string name, string nsUri) { XmlNodeKindFlags original = qilAxis.XmlType.NodeKinds; XmlNodeKindFlags required = AxisTypeMask(original, nodeType, xpathAxis); QilIterator itr; if (required == 0) { return(f.Sequence()); } else if (required == original) { } else { qilAxis = f.Filter(itr = f.For(qilAxis), f.IsType(itr, T.NodeChoice(required))); qilAxis.XmlType = T.PrimeProduct(T.NodeChoice(required), qilAxis.XmlType.Cardinality); if (qilAxis.NodeType == QilNodeType.Filter) { QilLoop filter = (QilLoop)qilAxis; filter.Body = f.And(filter.Body, name != null && nsUri != null ? f.Eq(f.NameOf(itr), f.QName(name, nsUri)) : // ns:bar || bar nsUri != null ? f.Eq(f.NamespaceUriOf(itr), f.String(nsUri)) : // ns:* name != null ? f.Eq(f.LocalNameOf(itr), f.String(name)) : // *:foo /*name == nsUri == null*/ f.True() // * ); return(filter); } } return(f.Filter(itr = f.For(qilAxis), name != null && nsUri != null ? f.Eq(f.NameOf(itr), f.QName(name, nsUri)) : // ns:bar || bar nsUri != null ? f.Eq(f.NamespaceUriOf(itr), f.String(nsUri)) : // ns:* name != null ? f.Eq(f.LocalNameOf(itr), f.String(name)) : // *:foo /*name == nsUri == null*/ f.True() // * )); }
private static QilLoop BuildAxisFilter(QilPatternFactory f, QilIterator itr, XPathAxis xpathAxis, XPathNodeType nodeType, string name, string nsUri) { QilNode nameTest = ( name != null && nsUri != null ? f.Eq(f.NameOf(itr), f.QName(name, nsUri)) : // ns:bar || bar nsUri != null ? f.Eq(f.NamespaceUriOf(itr), f.String(nsUri)) : // ns:* name != null ? f.Eq(f.LocalNameOf(itr), f.String(name)) : // *:foo /*name == nsUri == null*/ f.True() // * ); XmlNodeKindFlags intersection = XPathBuilder.AxisTypeMask(itr.XmlType.NodeKinds, nodeType, xpathAxis); QilNode typeTest = ( intersection == 0 ? f.False() : // input & required doesn't intersect intersection == itr.XmlType.NodeKinds ? f.True() : // input is subset of required /*else*/ f.IsType(itr, T.NodeChoice(intersection)) ); QilLoop filter = f.BaseFactory.Filter(itr, f.And(typeTest, nameTest)); filter.XmlType = T.PrimeProduct(T.NodeChoice(intersection), filter.XmlType.Cardinality); return(filter); }
protected override QilNode VisitFilter(QilLoop n) { return(VisitLoop(n)); }
private void FixupFilterBinding(QilLoop filter, QilNode newBinding) { AssertFilter(filter); filter.Variable.Binding = newBinding; }
public void SetContext(QilLoop filter) { this.baseContext = filter; }