private bool ParseDollar(ProjectItem projectItem, string sourcePath, Stream stream, object context, ref string output) { if (stream.Current == '$') { var identifier = stream.PeekWord(1); if (TryGetIdentifier(projectItem, sourcePath, identifier, context, out var value)) { stream.Advance(identifier.Length); if (value is IEnumerable <Item> collection) { var filter = ParseBlock(stream, '(', ')'); var block = ParseBlock(stream, '[', ']'); var separator = ParseBlock(stream, '[', ']'); if (filter == null && block == null && separator == null) { var stringValue = value.ToString(); if (stringValue != value.GetType().FullName) { output += stringValue; } else { output += "$" + identifier; } } else { IEnumerable <Item> items; if (filter != null && filter.StartsWith("$", StringComparison.OrdinalIgnoreCase)) { var predicate = filter.Remove(0, 1); if (extensions != null) { // Lambda filters are always defined in the first extension type var c = extensions.FirstOrDefault()?.GetMethod(predicate); if (c != null) { try { items = collection.Where(x => (bool)c.Invoke(null, new object[] { x })).ToList(); matchFound = matchFound || items.Any(); } catch (Exception e) { items = Array.Empty <Item>(); hasError = true; var message = $"Error rendering template. Cannot apply filter to identifier '{identifier}'."; LogException(e, message, projectItem, sourcePath); } } else { items = Array.Empty <Item>(); } } else { items = Array.Empty <Item>(); } } else { items = ItemFilter.Apply(collection, filter, ref matchFound); } output += string.Join(ParseTemplate(projectItem, sourcePath, separator, context), items.Select(item => ParseTemplate(projectItem, sourcePath, block, item))); } } else if (value is bool) { var trueBlock = ParseBlock(stream, '[', ']'); var falseBlock = ParseBlock(stream, '[', ']'); output += ParseTemplate(projectItem, sourcePath, (bool)value ? trueBlock : falseBlock, context); } else { var block = ParseBlock(stream, '[', ']'); if (value != null) { if (block != null) { output += ParseTemplate(projectItem, sourcePath, block, value); } else { output += value.ToString(); } } } return(true); } } return(false); }