/// <summary> /// Parses all shader field declarations, aggregated with their metadata directives. /// </summary> /// <param name="source"></param> /// <param name="ignoreRegions"></param> /// <param name="removeSchedule"></param> private void ParseFields(string source, List <IndexRange> ignoreRegions, List <IndexRange> removeSchedule) { // Read the source line by line and parse it along the way List <string> fieldMetadata = new List <string>(); using (StringReader reader = new StringReader(source)) { int lineIndex = 0; while (true) { string line = reader.ReadLine(); if (line == null) { break; } // Trim the current line, so ignored ranges are removed IndexRange lineRange = new IndexRange(lineIndex, line.Length); IndexRange trimmedLineRange = lineRange; foreach (IndexRange ignoreRange in ignoreRegions) { trimmedLineRange.Trim(ignoreRange); if (trimmedLineRange.Length == 0) { break; } } foreach (IndexRange ignoreRange in removeSchedule) { trimmedLineRange.Trim(ignoreRange); if (trimmedLineRange.Length == 0) { break; } } string trimmedLine = (trimmedLineRange.Length == 0) ? string.Empty : source.Substring(trimmedLineRange.Index, trimmedLineRange.Length); // Keep track of where we are in the source, and skip over lines that // fall within source regions that are flagged to be ignored. lineIndex += line.Length; lineIndex += Environment.NewLine.Length; // Cleanup remaining line to make it easier to parse trimmedLine = trimmedLine.Trim().TrimEnd(';'); if (string.IsNullOrEmpty(trimmedLine)) { continue; } // Scan for metadata directives and store them until we hit the next variable declaration Match metadataMatch = RegexMetadataDirective.Match(trimmedLine); if (metadataMatch != null && metadataMatch.Length > 0) { string metadataDirective = metadataMatch.Groups[1].Value; fieldMetadata.Add(metadataDirective); continue; } // Scan for field declarations and aggregate them with previously collected metadata directives ShaderFieldInfo field = this.ParseFieldDeclaration(trimmedLine, fieldMetadata); if (field != null) { this.fields.Add(field); fieldMetadata.Clear(); continue; } // Clear metadata directives when reading non-empty lines that don't match any of the above fieldMetadata.Clear(); } } }
/// <summary> /// Identify continuous blocks of variable metadata directives, mapped to the range of source code /// they're located in. /// </summary> /// <param name="source"></param> /// <param name="ignoreRegions"></param> private Dictionary <IndexRange, List <IndexRange> > IdentifyMetadataBlocks(string source, List <IndexRange> ignoreRegions) { Dictionary <IndexRange, List <IndexRange> > metadataBlocks = new Dictionary <IndexRange, List <IndexRange> >(); using (StringReader reader = new StringReader(source)) { List <IndexRange> currentBlock = new List <IndexRange>(); IndexRange currentBlockRange = new IndexRange(0, 0); int lineIndex = 0; while (true) { string line = reader.ReadLine(); if (line == null) { break; } // Trim the current line, so ignored ranges are removed IndexRange lineRange = new IndexRange(lineIndex, line.Length); IndexRange trimmedLineRange = lineRange; foreach (IndexRange ignoreRange in ignoreRegions) { trimmedLineRange.Trim(ignoreRange); if (trimmedLineRange.Length == 0) { break; } } string trimmedLine = (trimmedLineRange.Length == 0) ? string.Empty : source.Substring(trimmedLineRange.Index, trimmedLineRange.Length); // Process the current line bool isLineEmpty = string.IsNullOrWhiteSpace(trimmedLine); if (!isLineEmpty) { Match match = RegexMetadataDirective.Match(trimmedLine); if (match != null && match.Length > 0) { // Extend the current metadata block to include the detected directive IndexRange metadataRange = new IndexRange(trimmedLineRange.Index + match.Index, match.Length); metadataRange = this.ExpandToLine(source, metadataRange); currentBlock.Add(metadataRange); currentBlockRange.Length = (metadataRange.Index + metadataRange.Length) - currentBlockRange.Index; } else { // Close the current metadata block if (currentBlock.Count > 0) { metadataBlocks.Add(currentBlockRange, new List <IndexRange>(currentBlock)); } // Start a new metadata block currentBlock.Clear(); currentBlockRange.Index = lineRange.Index + lineRange.Length + Environment.NewLine.Length; currentBlockRange.Length = 0; } } else { // If we have a current block, incorporate comment and empty lines into it until it ends if (currentBlock.Count > 0) { currentBlockRange.Length = (lineRange.Index + lineRange.Length) - currentBlockRange.Index; } // Otherwise, move the start of the current block forward until we actually find a metadata line else { currentBlockRange.Index = lineRange.Index + lineRange.Length + Environment.NewLine.Length; currentBlockRange.Length = 0; } } lineIndex += line.Length; lineIndex += Environment.NewLine.Length; } } return(metadataBlocks); }