private QilNode InvokeApplyFunction(StylesheetLevel sheet, QilName mode, IList<XslNode> actualArgs) { // Here we create function that has one argument for each with-param in apply-templates // We have actualArgs -- list of xsl:with-param(name, value) // From it we create: // invokeArgs -- values to use with QilInvoke // formalArgs -- list of iterators to use with QilFunction // actualArgs -- modify it to hold iterators (formalArgs) instead of values to ise in invoke generator inside function budy XslFlags flags; { if (!sheet.ModeFlags.TryGetValue(mode, out flags)) { flags = 0; } flags |= XslFlags.Current; // Due to recursive nature of Apply(Templates/Imports) we will need current node any way } actualArgs = AddRemoveImplicitArgs(actualArgs, flags); QilList invokeArgs = _f.ActualParameterList(); QilFunction applyFunction = null; // Look at the list of all functions that have been already built. If a suitable one is found, reuse it. List<QilFunction> functionsForMode; if (!sheet.ApplyFunctions.TryGetValue(mode, out functionsForMode)) { functionsForMode = sheet.ApplyFunctions[mode] = new List<QilFunction>(); } foreach (QilFunction func in functionsForMode) { if (FillupInvokeArgs(func.Arguments, actualArgs, /*ref*/invokeArgs)) { applyFunction = func; break; } } // If a suitable function has not been found, create it if (applyFunction == null) { invokeArgs.Clear(); // We wasn't able to find suitable function. Let's build new: // 1. Function arguments QilList formalArgs = _f.FormalParameterList(); for (int i = 0; i < actualArgs.Count; i++) { Debug.Assert(actualArgs[i].NodeType == XslNodeType.WithParam, "All Sorts was removed in CompileSorts()"); VarPar withParam = (VarPar)actualArgs[i]; // Add actual arg to 'invokeArgs' array. No need to clone it since it must be // a literal or a reference. invokeArgs.Add(withParam.Value); // Create correspondent formal arg QilParameter formalArg = _f.Parameter(i == 0 ? T.NodeNotRtf : withParam.Value.XmlType); formalArg.Name = CloneName(withParam.Name); formalArgs.Add(formalArg); // Change actual arg value to formalArg for reuse in calling built-in templates rules withParam.Value = formalArg; } // 2. Function header applyFunction = _f.Function(formalArgs, _f.Boolean((flags & XslFlags.SideEffects) != 0), T.NodeNotRtfS ); string attMode = (mode.LocalName.Length == 0) ? string.Empty : " mode=\"" + mode.QualifiedName + '"'; applyFunction.DebugName = (sheet is RootLevel ? "<xsl:apply-templates" : "<xsl:apply-imports") + attMode + '>'; functionsForMode.Add(applyFunction); _functions.Add(applyFunction); // 3. Function body Debug.Assert(actualArgs[0].Name == _nameCurrent, "Caller should always pass $current as a first argument to apply-* calls."); QilIterator current = (QilIterator)formalArgs[0]; // 3.1 Built-in templates: // 3.1.1 loop over content of current element QilLoop loopOnContent; { QilIterator iChild = _f.For(_f.Content(current)); QilNode filter = _f.Filter(iChild, _f.IsType(iChild, T.Content)); filter.XmlType = T.ContentS; // not attribute LoopFocus curLoopSaved = _curLoop; _curLoop.SetFocus(_f.For(filter)); /* Prepare actual arguments */ // At XSLT 1.0, if a built-in template rule is invoked with parameters, the parameters are not // passed on to any templates invoked by the built-in rule. At XSLT 2.0, these parameters are // passed through the built-in template rule unchanged. // we can't just modify current/position/last of actualArgs in XSLT 2.0 as we tried before, // becuase flags for apply-import amy now be different then flags for apply-templates, so // we may need to add some space for additional position/last arguments QilNode body = InvokeApplyFunction(_compiler.Root, mode, /*actualArgs:*/null); if (IsDebug) { body = _f.Sequence(InvokeOnCurrentNodeChanged(), body); } loopOnContent = _curLoop.ConstructLoop(body); _curLoop = curLoopSaved; } // 3.1.2 switch on type of current node QilTernary builtinTemplates = _f.BaseFactory.Conditional(_f.IsType(current, _elementOrDocumentType), loopOnContent, _f.Conditional(_f.IsType(current, _textOrAttributeType), _f.TextCtor(_f.XPathNodeValue(current)), _f.Sequence() ) ); // 3.2 Stylesheet templates _matcherBuilder.CollectPatterns(sheet, mode); applyFunction.Definition = _matcherBuilder.BuildMatcher(current, actualArgs, /*otherwise:*/builtinTemplates); } return _f.Invoke(applyFunction, invokeArgs); }
private QilGenerator(bool debug) { _scope = new CompilerScopeManager<QilIterator>(); _outputScope = new OutputScopeManager(); _prefixesInUse = new HybridDictionary(); _f = new XsltQilFactory(new QilFactory(), debug); _xpathBuilder = new XPathBuilder((IXPathEnvironment)this); _xpathParser = new XPathParser<QilNode>(); _ptrnBuilder = new XPathPatternBuilder((IXPathEnvironment)this); _ptrnParser = new XPathPatternParser(); _refReplacer = new ReferenceReplacer(_f.BaseFactory); _invkGen = new InvokeGenerator(_f, debug); _matcherBuilder = new MatcherBuilder(_f, _refReplacer, _invkGen); _singlFocus = new SingletonFocus(_f); _funcFocus = new FunctionFocus(); _curLoop = new LoopFocus(_f); _strConcat = new QilStrConcatenator(_f); _varHelper = new VariableHelper(_f); _elementOrDocumentType = T.DocumentOrElement; _textOrAttributeType = T.NodeChoice(XmlNodeKindFlags.Text | XmlNodeKindFlags.Attribute); _nameCurrent = _f.QName("current", XmlReservedNs.NsXslDebug); _namePosition = _f.QName("position", XmlReservedNs.NsXslDebug); _nameLast = _f.QName("last", XmlReservedNs.NsXslDebug); _nameNamespaces = _f.QName("namespaces", XmlReservedNs.NsXslDebug); _nameInit = _f.QName("init", XmlReservedNs.NsXslDebug); _formatterCnt = 0; }
private void CompileSort(Sort sort, QilList keyList, ref LoopFocus parentLoop) { Debug.Assert(sort.NodeType == XslNodeType.Sort); QilNode select, select2, lang, order, caseOrder; bool fwdCompat; EnterScope(sort); fwdCompat = sort.ForwardsCompatible; select = CompileXPathExpression(sort.Select); if (sort.Lang != null || sort.DataType != null || sort.Order != null || sort.CaseOrder != null) { // Calculate these attributes in the context of the parent loop LoopFocus curLoopSaved = _curLoop; _curLoop = parentLoop; lang = CompileLangAttribute(sort.Lang, fwdCompat); CompileDataTypeAttribute(sort.DataType, fwdCompat, ref select, out select2); order = CompileOrderAttribute( /*attName: */ "order", /*attValue: */ sort.Order, /*value0: */ "ascending", /*value1: */ "descending", /*fwdCompat:*/ fwdCompat ); caseOrder = CompileOrderAttribute( /*attName: */ "case-order", /*attValue: */ sort.CaseOrder, /*value0: */ "lower-first", /*value1: */ "upper-first", /*fwdCompat:*/ fwdCompat ); // Restore loop context _curLoop = curLoopSaved; } else { select = _f.ConvertToString(select); select2 = lang = order = caseOrder = null; } _strConcat.Reset(); _strConcat.Append(XmlReservedNs.NsCollationBase); _strConcat.Append('/'); _strConcat.Append(lang); char separator = '?'; if (order != null) { _strConcat.Append(separator); _strConcat.Append("descendingOrder="); _strConcat.Append(order); separator = '&'; } if (caseOrder != null) { _strConcat.Append(separator); _strConcat.Append("upperFirst="); _strConcat.Append(caseOrder); separator = '&'; } QilNode collation = _strConcat.ToQil(); QilSortKey result = _f.SortKey(select, collation); // Line information is not copied keyList.Add(result); if (select2 != null) { result = _f.SortKey(select2, collation.DeepClone(_f.BaseFactory)); // Line information is not copied keyList.Add(result); } ExitScope(); }
// REVIEW: Can we handle both sort's and with-param's in the document order? // CompileSorts() creates helper variables in varHelper private QilNode CompileSorts(IList<XslNode> content, ref LoopFocus parentLoop) { QilList keyList = _f.BaseFactory.SortKeyList(); int i = 0; while (i < content.Count) { Sort sort = content[i] as Sort; if (sort != null) { CompileSort(sort, keyList, ref parentLoop); content.RemoveAt(i); } else { i++; } } if (keyList.Count == 0) return null; return keyList; }
private QilNode CompileApplyTemplates(XslNodeEx node) { QilNode result; IList<XslNode> content = node.Content; // Calculate select expression int varScope = _varHelper.StartVariables(); QilIterator select = _f.Let(CompileNodeSetExpression(node.Select)); _varHelper.AddVariable(select); // Compile with-param's, they must be calculated outside the loop and // if they are neither constant nor reference we need to cache them in Let's for (int i = 0; i < content.Count; i++) { VarPar withParam = content[i] as VarPar; if (withParam != null) { Debug.Assert(withParam.NodeType == XslNodeType.WithParam); CompileWithParam(withParam); QilNode val = withParam.Value; if (IsDebug || !(val is QilIterator || val is QilLiteral)) { QilIterator let = _f.Let(val); let.DebugName = _f.QName("with-param " + withParam.Name.QualifiedName, XmlReservedNs.NsXslDebug).ToString(); _varHelper.AddVariable(let); withParam.Value = let; } } } // Push new loop frame on the stack LoopFocus curLoopSaved = _curLoop; QilIterator it = _f.For(select); _curLoop.SetFocus(it); // Compile sort keys and body _curLoop.Sort(CompileSorts(content, ref curLoopSaved)); result = GenerateApply(_compiler.Root, node); result = WrapLoopBody(node.ElemNameLi, result, node.EndTagLi); result = AddCurrentPositionLast(result); result = _curLoop.ConstructLoop(result); // Pop loop frame _curLoop = curLoopSaved; result = _varHelper.FinishVariables(result, varScope); return result; }
private QilNode CompileForEach(XslNodeEx node) { QilNode result; IList<XslNode> content = node.Content; // Push new loop frame on the stack LoopFocus curLoopSaved = _curLoop; QilIterator it = _f.For(CompileNodeSetExpression(node.Select)); _curLoop.SetFocus(it); // Compile sort keys and body int varScope = _varHelper.StartVariables(); _curLoop.Sort(CompileSorts(content, ref curLoopSaved)); result = CompileInstructions(content); result = WrapLoopBody(node.ElemNameLi, result, node.EndTagLi); result = AddCurrentPositionLast(result); result = _curLoop.ConstructLoop(result); result = _varHelper.FinishVariables(result, varScope); // Pop loop frame _curLoop = curLoopSaved; return result; }
private QilNode InvokeApplyFunction(Stylesheet sheet, QilName mode, IList<XslNode> actualArgs) { // Here we create function that has one argument for each with-param in apply-templates // We have actualArgs -- list of xsl:with-param(name, value) // From it we create: // invokeArgs -- values to use with QilInvoke // formalArgs -- list of iterators to use with QilFunction // actualArgs -- modify it to hold iterators (formalArgs) instead of values to ise in invoke generator inside function budy // Special treatment for current/position/last XslFlags flags; if (! compiler.ModeFlags.TryGetValue(mode, out flags)) { flags = 0; } if (IsDebug) { flags = XslFlags.FullFocus; } flags |= XslFlags.Current; // Due to recursive nature of Apply(Templates/Imports) we will need current node any way Debug.Assert(actualArgs[0].Name == nameCurrent, "Caller should always pass $current as a first argument to apply-* calls."); QilList invokeArgs = f.ActualParameterList(); QilFunction applyFunction = null; // Look at the list of all functions that have been already built. If a suitable one is found, reuse it. Dictionary<QilName, List<QilFunction>> funcTable = ( sheet == null ? compiler.ApplyTemplatesFunctions : sheet.ApplyImportsFunctions ); List<QilFunction> functionsForMode; if (!funcTable.TryGetValue(mode, out functionsForMode)) { functionsForMode = funcTable[mode] = new List<QilFunction>(); } foreach (QilFunction func in functionsForMode) { if (FillupInvokeArgs(func.Arguments, actualArgs, /*ref*/invokeArgs)) { applyFunction = func; break; } } // If a suitable function has not been found, create it if (applyFunction == null) { invokeArgs.Clear(); // We wasn't able to find suitable function. Let's build new: // 1. Function arguments QilList formalArgs = f.FormalParameterList(); for (int i = 0; i < actualArgs.Count; i++) { Debug.Assert(actualArgs[i].NodeType == XslNodeType.WithParam, "All Sorts was removed in CompileSorts()"); VarPar withParam = (VarPar)actualArgs[i] ; // Add actual arg to 'invokeArgs' array. No need to clone it since it must be // a literal or a reference. invokeArgs.Add(withParam.Value); // Create correspondent formal arg QilParameter formalArg = f.Parameter(i == 0 ? T.NodeNotRtf : withParam.Value.XmlType); formalArg.Name = CloneName(withParam.Name); formalArgs.Add(formalArg); // Change actual arg value to formalArg for reuse in calling built-in templates rules withParam.Value = formalArg; } // 2. Function header applyFunction = f.Function(formalArgs, f.False(), T.NodeNotRtfS); string attMode = (mode.LocalName.Length == 0) ? string.Empty : " mode=\"" + mode.QualifiedName + '"'; applyFunction.DebugName = (sheet == null ? "<xsl:apply-templates" : "<xsl:apply-imports") + attMode + '>'; functionsForMode.Add(applyFunction); this.functions.Add(applyFunction); // 3. Function body QilIterator current = (QilIterator)formalArgs[0]; // 3.1 Built-in templates QilTernary builtinTemplates = f.BaseFactory.Conditional(f.IsType(current, elementOrDocumentType), // This will be fixed up later f.BaseFactory.Nop(f.BaseFactory.Unknown(T.NodeNotRtfS)), f.Conditional(f.IsType(current, textOrAttributeType), f.TextCtor(f.XPathNodeValue(current)), f.Sequence() ) ); // 3.2 Stylesheet templates matcherBuilder.CollectPatterns(sheet ?? compiler.PrincipalStylesheet, mode, /*applyImports:*/sheet != null); applyFunction.Definition = matcherBuilder.BuildMatcher(current, actualArgs, /*otherwise:*/builtinTemplates); // 3.3 Fix up the loop in built-in templates QilLoop loopOnContent; { QilIterator iChild = f.For(f.Content(current)); QilNode filter = f.Filter(iChild, f.IsType(iChild, T.Content)); filter.XmlType = T.ContentS; // not attribute LoopFocus curLoopSaved = curLoop; curLoop.SetFocus(f.For(filter)); if ((flags & XslFlags.Last) != 0) { // Mark that we need last argument curLoop.GetLast(); } /* Prepare actual arguments */ { // At XSLT 1.0, if a built-in template rule is invoked with parameters, the parameters are not // passed on to any templates invoked by the built-in rule. At XSLT 2.0, these parameters are // passed through the built-in template rule unchanged. const bool Xslt10Rules = true; if (Xslt10Rules) { List<XslNode> newActualArgs = new List<XslNode>(3); int argNum = 0; if ((flags & XslFlags.Current ) != 0) { newActualArgs.Add(actualArgs[argNum ++]); } if ((flags & XslFlags.Position) != 0) { newActualArgs.Add(actualArgs[argNum ++]); } if ((flags & XslFlags.Last ) != 0) { newActualArgs.Add(actualArgs[argNum ++]); } actualArgs = newActualArgs; } // Fix values of current, position, and last { int argNum = 0; if ((flags & XslFlags.Current ) != 0) { ((VarPar)actualArgs[argNum ++]).Value = GetCurrentNode (); } if ((flags & XslFlags.Position) != 0) { ((VarPar)actualArgs[argNum ++]).Value = GetCurrentPosition(); } if ((flags & XslFlags.Last ) != 0) { ((VarPar)actualArgs[argNum ++]).Value = GetLastPosition (); } } } loopOnContent = curLoop.ConstructLoop(InvokeApplyFunction(/*sheet:*/null, mode, actualArgs)); curLoop = curLoopSaved; } Debug.Assert(builtinTemplates.Center.NodeType == QilNodeType.Nop); ((QilUnary)builtinTemplates.Center).Child = loopOnContent; } return f.Invoke(applyFunction, invokeArgs); }