예제 #1
0
        /// <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;
                        }
                    }
                }
            }
        }
예제 #2
0
        /// <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);
        }
예제 #3
0
        /// <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);
        }
예제 #4
0
        /// <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);
                        }
                    }
                }
            }
        }
예제 #5
0
        /// <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);
            }
        }
예제 #6
0
        /// <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);
        }
예제 #7
0
        /// <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.");
                }
            }
        }
예제 #8
0
        /// <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);
        }
예제 #9
0
        /// <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);
        }
예제 #10
0
        /// <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);
                }
            }
        }
예제 #11
0
        /// <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.");
                }
            }
        }