/// <summary> /// Parse declarations from the reader /// </summary> /// <param name="File">The file containing the declarations</param> /// <param name="MarkupIdx">The markup to parse declarations from</param> /// <param name="Scope">The scope depth; the number of unmatched opening brace tokens</param> void ParseDeclarations(SourceFile File, SourceFragment Fragment, PreprocessorMarkup Markup, ref int Scope) { TokenReader Reader = new TokenReader(File.Text, Markup.Location, Markup.EndLocation); if (Reader.MoveNext()) { for (;;) { if (Scope > 0 || (!ReadEnumClassHeader(Reader, Fragment) && !ReadClassOrStructHeader(Reader, Fragment) && !ReadTypedefHeader(Reader, Fragment) && !ReadTemplateClassOrStructHeader(Reader, Fragment) && !SkipTemplateHeader(Reader))) { // Update the scope depth if (Reader.Current.Text == "{") { Scope++; } else if (Reader.Current.Text == "}" && --Scope < 0) { throw new Exception(String.Format("Unbalanced '}}' while parsing '{0}'", File.Location)); } // Move to the next token if (!Reader.MoveNext()) { break; } } } } }
/// <summary> /// Override to allow a block of markup to be be different between two derivations /// </summary> /// <param name="File">File being preprocessed</param> /// <param name="MarkupIdx">Index of the markup object to consider</param> /// <returns>True to ignore the different derivations</returns> public static bool AllowDifferentMarkup(SourceFile File, int MarkupIdx) { PreprocessorMarkup Markup = File.Markup[MarkupIdx]; if (Markup.Type == PreprocessorMarkupType.Text && Markup.EndLocation.LineIdx == Markup.Location.LineIdx + 1 && File.Text.Lines[Markup.Location.LineIdx].Contains("friend") && File.Text.Lines[Markup.Location.LineIdx].Contains("Z_Construct_")) { return(true); } if (Markup.Type == PreprocessorMarkupType.Define && Markup.Tokens[0].Text == "ONLINE_LOG_PREFIX") { return(true); } if (Markup.Type == PreprocessorMarkupType.Define && Markup.Tokens[0].Text == "DEPRECATED_FORGAME") { return(true); } if (Markup.Type == PreprocessorMarkupType.Undef && Markup.Tokens[0].Text == "TEXT") { return(true); } if (Markup.Type == PreprocessorMarkupType.Else || Markup.Type == PreprocessorMarkupType.Endif) { return(true); } return(false); }
/// <summary> /// Checks whether the markup matches a #pragma once directive /// </summary> /// <param name="Markup">The parsed markup for this file</param> /// <param name="BodyMinIdx">The current minimum index of non-header guard markup in this file</param> /// <returns>True if the markup represents a #pragma once directive</returns> static bool SkipPragmaOnce(PreprocessorMarkup[] Markup, ref int BodyMinIdx) { PreprocessorMarkup FirstMarkup = Markup[BodyMinIdx]; if (FirstMarkup.Type == PreprocessorMarkupType.Pragma && FirstMarkup.Tokens.Count == 1 && FirstMarkup.Tokens[0].Text == "once") { BodyMinIdx++; return(true); } return(false); }
/// <summary> /// Adds a known dependency to the given task /// </summary> /// <param name="Worker">The task to add the dependency to</param> /// <param name="Fragments">The array of fragments</param> /// <param name="FragmentIdx">Index of the dependency</param> static void AddDependency(SequenceWorker Worker, SourceFragment[] Fragments, int FragmentIdx) { // Add the dependency Worker.KnownDependencies.Add(FragmentIdx); // Check for any macro definitions that the given fragment depends on SourceFragment Fragment = Fragments[FragmentIdx]; foreach (Symbol ReferencedSymbol in Fragment.ReferencedSymbols.Keys) { if (ReferencedSymbol.Type == SymbolType.Macro) { int ReferencedFragmentIdx = Array.IndexOf(Fragments, ReferencedSymbol.Fragment); if (ReferencedFragmentIdx != -1 && ReferencedFragmentIdx < FragmentIdx) { Worker.KnownDependencies.Add(ReferencedFragmentIdx); } } } // Force dependencies onto pinned fragments. This can make a difference when templated functions are implemented in pinned headers // but declared in another (eg. LegacyText.h). The file will compile fine without the implementations being present, expecting them to be linked // into another object file, but fail when they are included due to being pinned later because MSVC only parses the token stream then. for (int MarkupIdx = Fragment.MarkupMin; MarkupIdx < Fragment.MarkupMax; MarkupIdx++) { PreprocessorMarkup Markup = Fragment.File.Markup[MarkupIdx]; if (Markup.Type == PreprocessorMarkupType.Include && Markup.IncludedFile != null && (Markup.IncludedFile.Flags & SourceFileFlags.Pinned) != 0 && Markup.IncludedFile.Counterpart == null) { foreach (SourceFragment PinnedFragment in Markup.IncludedFile.Fragments) { int PinnedFragmentIdx = Array.IndexOf(Fragments, PinnedFragment); if (PinnedFragmentIdx != -1 && PinnedFragmentIdx < FragmentIdx) { AddDependency(Worker, Fragments, PinnedFragmentIdx); } } } } }
/// <summary> /// Find all the fragments included by this fragment, including the current one /// </summary> /// <param name="IncludedFragments">List to receive the included fragments</param> /// <param name="IncludeStackChanges">Optional list to store changes to the include stack, and the count of fragments at that point. Records with a file set to null indicate that the end of the topmost file.</param> /// <param name="UniqueIncludedFiles">Set of files with header guards that have already been included</param> public void FindIncludedFragments(List <SourceFragment> IncludedFragments, List <Tuple <int, SourceFile> > IncludeStackChanges, HashSet <SourceFile> UniqueIncludedFiles) { if ((File.Flags & SourceFileFlags.Inline) == 0) { // Add all the included files into the tree, and save the depth at each step for (int MarkupIdx = MarkupMin; MarkupIdx < MarkupMax; MarkupIdx++) { PreprocessorMarkup Item = File.Markup[MarkupIdx]; if (Item.Type == PreprocessorMarkupType.Include && Item.IsActive && Item.IncludedFile != null) { SourceFile IncludedFile = Item.IncludedFile; if (!IncludedFile.HasHeaderGuard || UniqueIncludedFiles.Add(IncludedFile)) { IncludedFile.FindIncludedFragments(IncludedFragments, IncludeStackChanges, UniqueIncludedFiles); } } } // Add the current fragment IncludedFragments.Add(this); } }
/// <summary> /// Writes this fragment to a file /// </summary> /// <param name="Writer">Writer to receive output text</param> public void Write(TextWriter Writer) { TextBuffer Text = File.Text; // Write all the relevant bits from this file TextLocation Location = TextLocation.Origin; for (int Idx = File.BodyMinIdx; Idx < File.BodyMaxIdx; Idx++) { PreprocessorMarkup Markup = File.Markup[Idx]; if ((Idx >= MarkupMin && Idx < MarkupMax) || Markup.IsConditionalPreprocessorDirective()) { if (Markup.Type != PreprocessorMarkupType.Include) { TextLocation EndLocation = (Idx + 1 < File.Markup.Length)? File.Markup[Idx + 1].Location : File.Text.End; WriteLinesCommented(Writer, Text, Location, Markup.Location); WriteLines(Writer, Text, Markup.Location, EndLocation); Location = EndLocation; } else if (!Markup.IsActive) { WriteLinesCommented(Writer, Text, Location, Markup.Location); Writer.WriteLine("#error Unexpected include: {0}", Token.Format(Markup.Tokens)); Location = new TextLocation(Markup.Location.LineIdx + 1, 0); } else if (Markup.IncludedFile != null && (Markup.IncludedFile.Flags & SourceFileFlags.Inline) != 0) { TextLocation EndLocation = (Idx + 1 < File.Markup.Length)? File.Markup[Idx + 1].Location : File.Text.End; WriteLinesCommented(Writer, Text, Location, Markup.Location); Writer.Write("#include \"{0}\"", Markup.IncludedFile.Location.FullName); WriteLinesCommented(Writer, Text, Markup.Location, EndLocation); Location = EndLocation; } } } // Comment out the rest of the file WriteLinesCommented(Writer, Text, Location, Text.End); }
/// <summary> /// Construct a SourceFile from the given arguments /// </summary> /// <param name="Location">Location of the file</param> /// <param name="Text">Contents of the file</param> /// <param name="Flags">Properties of the file</param> public SourceFile(FileReference Location, TextBuffer Text, SourceFileFlags Flags) { this.Location = Location; this.Text = Text; this.Flags = Flags; // Read the preprocsesor markup if (Text == null) { Markup = new PreprocessorMarkup[0]; } else { Markup = PreprocessorMarkup.ParseArray(Text); } // Find the markup range which excludes header guards BodyMinIdx = 0; BodyMaxIdx = Markup.Length; while (BodyMaxIdx > BodyMinIdx) { if (!SkipPragmaOnce(Markup, ref BodyMinIdx) && !SkipHeaderGuard(Markup, ref BodyMinIdx, ref BodyMaxIdx)) { break; } } // Inline files must not have a header guard (because it would result in a different derivation), and must not include any other files (because we include them // from their original location) if ((Flags & SourceFileFlags.Inline) != 0) { if (HasHeaderGuard) { throw new Exception("Files marked as 'inline' may not have a header guard, since they will be included directly."); } } }
/// <summary> /// Find or create an output file for a corresponding input file /// </summary> /// <param name="InputFile">The input file</param> /// <param name="IncludeStack">The current include stack</param> /// <param name="OutputFiles">List of output files</param> /// <param name="OutputFileLookup">Mapping from source file to output file</param> /// <returns>The new or existing output file</returns> static OutputFile FindOrCreateOutputFile(List <SourceFile> InputFileStack, Dictionary <SourceFile, SourceFile> CppFileToHeaderFile, HashList <OutputFile> PreviousFiles, Dictionary <SourceFile, OutputFile> OutputFileLookup, Dictionary <Symbol, OutputFile> FwdSymbolToHeader, bool bMakeStandalone, bool bUseOriginalIncludes, LineBasedTextWriter Log) { // Get the file at the top of the stack SourceFile InputFile = InputFileStack[InputFileStack.Count - 1]; // Try to find an existing file OutputFile OutputFile; if (OutputFileLookup.TryGetValue(InputFile, out OutputFile)) { if (OutputFile == null) { throw new Exception("Circular include dependencies are not allowed."); } foreach (OutputFile IncludedFile in OutputFile.OriginalIncludedFiles.Where(x => !PreviousFiles.Contains(x))) { PreviousFiles.Add(IncludedFile); } } else { // Add a placeholder entry in the output file lookup, so we can detect circular include dependencies OutputFileLookup[InputFile] = null; // Duplicate the list of previously included files, so we can construct the List <OutputFile> PreviousFilesCopy = new List <OutputFile>(PreviousFiles); // Build a list of includes for this file. First include is a placeholder for any missing includes that need to be inserted. List <OutputFileInclude> Includes = new List <OutputFileInclude>(); if ((InputFile.Flags & SourceFileFlags.External) == 0) { for (int MarkupIdx = 0; MarkupIdx < InputFile.Markup.Length; MarkupIdx++) { PreprocessorMarkup Markup = InputFile.Markup[MarkupIdx]; if (Markup.IsActive && Markup.IncludedFile != null && (Markup.IncludedFile.Flags & SourceFileFlags.Inline) == 0 && Markup.IncludedFile.Counterpart == null) { InputFileStack.Add(Markup.IncludedFile); OutputFile IncludeFile = FindOrCreateOutputFile(InputFileStack, CppFileToHeaderFile, PreviousFiles, OutputFileLookup, FwdSymbolToHeader, bMakeStandalone, bUseOriginalIncludes, Log); InputFileStack.RemoveAt(InputFileStack.Count - 1); Includes.Add(new OutputFileInclude(MarkupIdx, IncludeFile)); } } } // Find the matching header file OutputFile HeaderFile = null; if ((InputFile.Flags & SourceFileFlags.TranslationUnit) != 0) { SourceFile CandidateHeaderFile; if (CppFileToHeaderFile.TryGetValue(InputFile, out CandidateHeaderFile) && (CandidateHeaderFile.Flags & SourceFileFlags.Standalone) != 0) { OutputFileLookup.TryGetValue(CandidateHeaderFile, out HeaderFile); } } // Create the output file. if ((InputFile.Flags & SourceFileFlags.Output) != 0 && !bUseOriginalIncludes) { OutputFile = CreateOptimizedOutputFile(InputFile, HeaderFile, PreviousFilesCopy, Includes, InputFileStack, FwdSymbolToHeader, bMakeStandalone, Log); } else { OutputFile = CreatePassthroughOutputFile(InputFile, Includes, Log); } // Replace the null entry in the output file lookup that we added earlier OutputFileLookup[InputFile] = OutputFile; // Add this file to the list of included files PreviousFiles.Add(OutputFile); // If the output file dependends on something on the stack, make sure it's marked as pinned if ((InputFile.Flags & SourceFileFlags.Pinned) == 0) { SourceFragment Dependency = OutputFile.Dependencies.FirstOrDefault(x => InputFileStack.Contains(x.File) && x.File != InputFile); if (Dependency != null) { throw new Exception(String.Format("'{0}' is not marked as pinned, but depends on '{1}' which includes it", InputFile.Location.GetFileName(), Dependency.UniqueName)); } } } return(OutputFile); }
/// <summary> /// Write out an optimized file to the given location /// </summary> /// <param name="IncludePaths">Base directories for relative include paths</param> /// <param name="SystemIncludePaths">Base directories for system include paths</param> /// <param name="Writer">Writer for the output text</param> public void Write(IEnumerable <DirectoryReference> IncludePaths, IEnumerable <DirectoryReference> SystemIncludePaths, TextWriter Writer, bool bRemoveForwardDeclarations, TextWriter Log) { // Write the file header TextLocation LastLocation = Text.Start; // Write the standalone includes if (MissingIncludes.Count > 0) { TextLocation BoilerplateLocation = (BodyMinIdx == Markup.Length || (Markup[BodyMinIdx].Type != PreprocessorMarkupType.Include && BodyMinIdx > 0))? Markup[BodyMinIdx - 1].EndLocation : Markup[BodyMinIdx].Location; WriteLines(Text, LastLocation, BoilerplateLocation, Writer); LastLocation = BoilerplateLocation; if (LastLocation.LineIdx > 0 && Text.Lines[LastLocation.LineIdx - 1].TrimEnd().Length > 0) { if (LastLocation.LineIdx + 1 < Text.Lines.Length && Text.Lines[LastLocation.LineIdx].TrimEnd().Length == 0 && Text.Lines[LastLocation.LineIdx + 1].TrimEnd().Length == 0) { LastLocation.LineIdx++; } Writer.WriteLine(); } foreach (SourceFile MissingInclude in MissingIncludes) { string IncludeText = FormatInclude(Location.Directory, MissingInclude.Location, IncludePaths, SystemIncludePaths, Log); Writer.WriteLine("#include {0}", IncludeText); } } // Figure out before which markup object to write forward declarations, skipping over all the includes at the start of the file int ForwardDeclarationsBeforeMarkupIdx = -1; if ((Flags & SourceFileFlags.TranslationUnit) == 0) { int ConditionDepth = 0; for (int MarkupIdx = BodyMinIdx; MarkupIdx < Markup.Length; MarkupIdx++) { if (ConditionDepth == 0) { ForwardDeclarationsBeforeMarkupIdx = MarkupIdx; } if (Markup[MarkupIdx].Type == PreprocessorMarkupType.Text) { break; } ConditionDepth += Markup[MarkupIdx].GetConditionDepthDelta(); } } // Write all the other markup for (int MarkupIdx = BodyMinIdx; MarkupIdx < Markup.Length; MarkupIdx++) { PreprocessorMarkup ThisMarkup = Markup[MarkupIdx]; // Write the forward declarations if (MarkupIdx == ForwardDeclarationsBeforeMarkupIdx) { // Write out at least up to the end of the last markup if (MarkupIdx > 0 && LastLocation <= Markup[MarkupIdx - 1].EndLocation) { WriteLines(Text, LastLocation, Markup[MarkupIdx - 1].EndLocation, Writer); LastLocation = Markup[MarkupIdx - 1].EndLocation; } // Skip a blank line in the existing text. TextLocation NewLastLocation = LastLocation; if (LastLocation.LineIdx < Text.Lines.Length && String.IsNullOrWhiteSpace(Text.Lines[LastLocation.LineIdx])) { NewLastLocation = new TextLocation(LastLocation.LineIdx + 1, 0); } // Merge all the existing forward declarations with the new set. HashSet <string> PreviousForwardDeclarations = new HashSet <string>(); while (NewLastLocation.LineIdx < Text.Lines.Length) { string TrimLine = Text.Lines[NewLastLocation.LineIdx].Trim(); if (TrimLine.Length > 0 && !TrimLine.Equals("// Forward declarations", StringComparison.InvariantCultureIgnoreCase) && !TrimLine.Equals("// Forward declarations.", StringComparison.InvariantCultureIgnoreCase)) { // Create a token reader for the current line TokenReader Reader = new TokenReader(Text, new TextLocation(NewLastLocation.LineIdx, 0), new TextLocation(NewLastLocation.LineIdx, Text.Lines[NewLastLocation.LineIdx].Length)); // Read it into a buffer List <Token> Tokens = new List <Token>(); while (Reader.MoveNext()) { Tokens.Add(Reader.Current); } // Check it matches the syntax for a forward declaration, and add it to the list if it does if (Tokens.Count == 3 && (Tokens[0].Text == "struct" || Tokens[0].Text == "class") && Tokens[1].Type == TokenType.Identifier && Tokens[2].Text == ";") { PreviousForwardDeclarations.Add(String.Format("{0} {1};", Tokens[0].Text, Tokens[1].Text)); } else if (Tokens.Count == 4 && Tokens[0].Text == "enum" && Tokens[1].Text == "class" && Tokens[2].Type == TokenType.Identifier && Tokens[3].Text == ";") { PreviousForwardDeclarations.Add(String.Format("enum class {0};", Tokens[2].Text)); } else if (Tokens.Count == 6 && Tokens[0].Text == "enum" && Tokens[1].Text == "class" && Tokens[2].Type == TokenType.Identifier && Tokens[3].Text == ":" && Tokens[4].Type == TokenType.Identifier && Tokens[5].Text == ";") { PreviousForwardDeclarations.Add(String.Format("enum class {0} : {1};", Tokens[2].Text, Tokens[4].Text)); } else if (ForwardDeclarations.Contains(Text.Lines[NewLastLocation.LineIdx])) { PreviousForwardDeclarations.Add(Text.Lines[NewLastLocation.LineIdx]); } else { break; } } NewLastLocation = new TextLocation(NewLastLocation.LineIdx + 1, 0); } // Create a full list of new forward declarations, combining with the ones that are already there. Normally we optimize with the forward declarations present, // so we shouldn't remove any unless running a specific pass designed to do so. HashSet <string> MergedForwardDeclarations = new HashSet <string>(ForwardDeclarations); if (!bRemoveForwardDeclarations) { MergedForwardDeclarations.UnionWith(PreviousForwardDeclarations); } // Write them out if (MergedForwardDeclarations.Count > 0) { Writer.WriteLine(); foreach (string ForwardDeclaration in MergedForwardDeclarations.Distinct().OrderBy(x => GetForwardDeclarationSortKey(x)).ThenBy(x => x)) { Writer.WriteLine("{0}{1}", GetIndent(MarkupIdx), ForwardDeclaration); } Writer.WriteLine(); LastLocation = NewLastLocation; } else if (PreviousForwardDeclarations.Count > 0) { Writer.WriteLine(); LastLocation = NewLastLocation; } } // Write the includes if (ThisMarkup.Type == PreprocessorMarkupType.Include && ThisMarkup.IsActive && !ThisMarkup.IsInlineInclude()) { // Write up to the start of this include WriteLines(Text, LastLocation, ThisMarkup.Location, Writer); // Get the original include text. Some modules - particularly editor modules - include headers from other modules based from Engine/Source which are not listed as dependencies. If // the original include is based from a shallower directory than the one we would include otherwise, we'll use that instead. string OriginalIncludeText = null; if (ThisMarkup.Tokens.Count == 1) { OriginalIncludeText = ThisMarkup.Tokens[0].Text.Replace('\\', '/'); } // Write the replacement includes foreach (SourceFile OutputIncludedFile in ThisMarkup.OutputIncludedFiles) { string IncludeText = FormatInclude(Location.Directory, OutputIncludedFile.Location, IncludePaths, SystemIncludePaths, Log); if (OutputIncludedFile == ThisMarkup.IncludedFile && Rules.IsExternalIncludeMacro(ThisMarkup.Tokens)) { IncludeText = Token.Format(ThisMarkup.Tokens); } else if (OutputIncludedFile == ThisMarkup.IncludedFile && (OutputIncludedFile.Flags & SourceFileFlags.External) != 0) { IncludeText = OriginalIncludeText; } else if (OriginalIncludeText != null && (Flags & SourceFileFlags.TranslationUnit) == 0 && OriginalIncludeText.EndsWith(IncludeText.TrimStart('\"'), StringComparison.InvariantCultureIgnoreCase) && (OriginalIncludeText.StartsWith("\"Runtime/", StringComparison.InvariantCultureIgnoreCase) || OriginalIncludeText.StartsWith("\"Developer/", StringComparison.InvariantCultureIgnoreCase) || OriginalIncludeText.StartsWith("\"Editor/", StringComparison.InvariantCultureIgnoreCase))) { IncludeText = OriginalIncludeText; } Writer.WriteLine("{0}#include {1}", GetIndent(MarkupIdx), IncludeText); } // Copy any formatting if (ThisMarkup.EndLocation.LineIdx > ThisMarkup.Location.LineIdx + 1) { WriteLines(Text, new TextLocation(ThisMarkup.Location.LineIdx + 1, 0), ThisMarkup.EndLocation, Writer); } // Update the location to the start of the next line LastLocation = new TextLocation(ThisMarkup.Location.LineIdx + 1, 0); } } // Write to the end of the file WriteLines(Text, LastLocation, Text.End, Writer); }
/// <summary> /// Read all the exported symbol definitions from a file /// </summary> /// <param name="File">The file to parse</param> /// <param name="MarkupIdx">The current index into the markup array of this file</param> /// <param name="Scope">The scoping depth, ie. number of unmatched '{' tokens encountered in source code</param> void ReadExportedSymbols(SourceFile File, ref int MarkupIdx, ref int Scope) { // Ignore files which only have a header guard; there are no fragments that we can link to if (File.BodyMinIdx == File.BodyMaxIdx) { return; } // Read markup from the file for (; MarkupIdx < File.Markup.Length; MarkupIdx++) { PreprocessorMarkup Markup = File.Markup[MarkupIdx]; if (Markup.Type == PreprocessorMarkupType.Define) { // If the macro is not already defined, add it if (Markup.Tokens[0].Text != "LOCTEXT_NAMESPACE") { SourceFragment Fragment = FindFragment(File, MarkupIdx); AddSymbol(Markup.Tokens[0].Text, SymbolType.Macro, null, Fragment, Markup.Location); } } else if (Markup.Type == PreprocessorMarkupType.If && Markup.Tokens.Count == 2 && Markup.Tokens[0].Text == "!" && Markup.Tokens[1].Text == "CPP") { // Completely ignore these blocks; they sometimes differ in class/struct usage, and exist solely for noexport types to be parsed by UHT int Depth = 1; while (Depth > 0) { Markup = File.Markup[++MarkupIdx]; if (Markup.Type == PreprocessorMarkupType.If || Markup.Type == PreprocessorMarkupType.Ifdef || Markup.Type == PreprocessorMarkupType.Ifndef) { Depth++; } else if (Markup.Type == PreprocessorMarkupType.Endif) { Depth--; } } } else if (Markup.Type == PreprocessorMarkupType.If || Markup.Type == PreprocessorMarkupType.Ifdef || Markup.Type == PreprocessorMarkupType.Ifndef) { // Loop through all the branches of this conditional block, saving the resulting scope of each derivation List <int> NewScopes = new List <int>(); while (Markup.Type != PreprocessorMarkupType.Endif) { // Skip over the conditional directive MarkupIdx++; // Read any exported symbols from this conditional block int NewScope = Scope; ReadExportedSymbols(File, ref MarkupIdx, ref NewScope); NewScopes.Add(NewScope); // Get the new preprocessor markup Markup = File.Markup[MarkupIdx]; } // Make sure they were all consistent if (NewScopes.Any(x => x != NewScopes[0])) { throw new Exception(String.Format("Unbalanced scope depth between conditional block derivations while parsing {0}", File.Location)); } // Update the scope Scope = NewScopes[0]; } else if (Markup.Type == PreprocessorMarkupType.Else || Markup.Type == PreprocessorMarkupType.Elif || Markup.Type == PreprocessorMarkupType.Endif) { // We've reached the end of this block; return success break; } else if (Markup.Type == PreprocessorMarkupType.Text) { // Read the declarations in this block SourceFragment Fragment = FindFragment(File, MarkupIdx); ParseDeclarations(File, Fragment, File.Markup[MarkupIdx], ref Scope); } } }
/// <summary> /// Construct a SourceFile from the given arguments /// </summary> /// <param name="Location">Location of the file</param> /// <param name="Text">Contents of the file</param> /// <param name="Flags">Properties of the file</param> public SourceFile(FileReference Location, TextBuffer Text, SourceFileFlags Flags) { // Check for directives specifying additional flags for this file in the source text if (Text != null) { foreach (string Line in Text.Lines) { Match Match = OptionsPattern.Match(Line); if (Match.Success) { foreach (string FlagText in Match.Groups[1].Value.Split(',').Select(x => x.Trim()).Where(x => x.Length > 0)) { SourceFileFlags Flag; if (Enum.TryParse(FlagText, true, out Flag)) { Flags |= Flag; } else { throw new Exception(String.Format("{0}: Invalid source file flag '{1}'", Location, FlagText)); } } } } } // Inline files cannot be standalone if ((Flags & SourceFileFlags.Inline) != 0) { Flags &= ~SourceFileFlags.Standalone; } // Save the parameters this.Location = Location; this.Text = Text; this.Flags = Flags; // Read the preprocsesor markup if (Text == null) { Markup = new PreprocessorMarkup[0]; } else { Markup = PreprocessorMarkup.ParseArray(Text); } // Find the markup range which excludes header guards BodyMinIdx = 0; BodyMaxIdx = Markup.Length; while (BodyMaxIdx > BodyMinIdx) { if (!SkipPragmaOnce(Markup, ref BodyMinIdx) && !SkipHeaderGuard(Markup, ref BodyMinIdx, ref BodyMaxIdx)) { break; } } // Inline files must not have a header guard (because it would result in a different derivation), and must not include any other files (because we include them // from their original location) if ((Flags & SourceFileFlags.Inline) != 0) { if (HasHeaderGuard) { throw new Exception("Files marked as 'inline' may not have a header guard, since they will be included directly."); } } }