private IReadOnlyList <object> GenerateValues(Item endItem, IProduction production) { // Small shortcircuit: If this production has one symbol, and that symbol is a // terminal, just return those derivation results directly without any buffering. if (production.Symbols.Count == 1 && production.Symbols[0] is IParser) { _statistics.DerivationSingleSymbolShortcircuits++; return(endItem.Derivations ! .Select(d => production.Apply(new[] { d })) .Where(o => o.Success) .Select(o => o.Value) .ToList()); } Item current = endItem; var count = production.Symbols.Count; var values = ArrayPool <IReadOnlyList <object> > .Shared.Rent(count); if (!GetValuesArray(values, current)) { return(Array.Empty <object>()); } // Small shortcircuit: If there is only one item in the production, and that one item // has only one possible value after recursing, just return that without buffering. if (count == 1 && values[0].Count == 1) { var result = production.Apply(new[] { values[0][0] }); _statistics.ItemsWithSingleDerivation++; return(result.Success ? new object[] { result.Value } : Array.Empty <object>()); } // Allocate a buffer and fill with initial values var buffer = ArrayPool <object> .Shared.Rent(count); InitializeBuffer(count, values, buffer); // Get an array to hold indices and initialize all values (they might not be 0 // coming out of the array pool) var indices = ArrayPool <int> .Shared.Rent(count); Array.Clear(indices, 0, count); // We traverse from Item to Item.ParentItem, which forms a unique chain from the end // back to the beginning. Even if Item.ParentItem has multiple children of different // lengths we can traverse those branches separately and there is no ambiguity in // derivation of results. // Start getting values. On each loop we call the production callback with what we // have in the buffer already, then we update values to the next index. When position // 1 gets to the last value, we reset it to 0 and increment position 2, etc. When // we've incremented the last position past the number of items in the last column, we // break from the loop and are done. var results = new List <object>(); do { _statistics.ProductionRuleAttempts++; var result = production.Apply(buffer); if (result.Success) { results.Add(result.Value); _statistics.ProductionRuleSuccesses++; } }while (IncrementBufferItems(count, indices, values, buffer)); ArrayPool <IReadOnlyList <object> > .Shared.Return(values); ArrayPool <object> .Shared.Return(buffer); ArrayPool <int> .Shared.Return(indices); return(results); }