private static void LibraryUsagePostOrderTraversal(
            LibraryCompilationScope libraryToUse,
            List <LibraryCompilationScope> libraryOrderOut,
            HashSet <LibraryCompilationScope> usedLibraries,
            HashSet <LibraryCompilationScope> cycleCheck,
            Stack <LibraryCompilationScope> breadcrumbs)
        {
            if (usedLibraries.Contains(libraryToUse))
            {
                return;
            }

            breadcrumbs.Push(libraryToUse);

            if (cycleCheck.Contains(libraryToUse))
            {
                StringBuilder message = new StringBuilder();
                message.Append("There is a dependency cycle in your libraries: ");
                bool first = true;
                foreach (LibraryCompilationScope breadcrumb in breadcrumbs)
                {
                    if (first)
                    {
                        first = false;
                    }
                    else
                    {
                        message.Append(" -> ");
                    }
                    message.Append(breadcrumb.Library.ID);
                }
                throw new InvalidOperationException(message.ToString());
            }
            cycleCheck.Add(libraryToUse);

            foreach (LocalizedLibraryView dependency in libraryToUse.Dependencies)
            {
                LibraryUsagePostOrderTraversal(dependency.LibraryScope, libraryOrderOut, usedLibraries, cycleCheck, breadcrumbs);
            }
            cycleCheck.Remove(libraryToUse);
            breadcrumbs.Pop();

            usedLibraries.Add(libraryToUse);
            libraryOrderOut.Add(libraryToUse);
        }
        private LocalizedLibraryView GetOrImportLibraryImpl(ParserContext parser, Token throwToken, string fullImportNameWithDots)
        {
            // TODO: allow importing from a user-specified locale
            Locale fromLocale = parser.CurrentLocale;
            string name       = fullImportNameWithDots.Contains('.') ? fullImportNameWithDots.Split('.')[0] : fullImportNameWithDots;

            string          secondAttemptedKey = name;
            LibraryMetadata libraryMetadata    = this.libraryFinder.GetLibraryMetadataFromAnyPossibleKey(fromLocale.ID + ":" + name);
            Locale          effectiveLocale    = fromLocale;

            if (libraryMetadata == null)
            {
                libraryMetadata = this.libraryFinder.GetLibraryMetadataFromAnyPossibleKey(name);
                if (libraryMetadata != null &&
                    libraryMetadata.SupportedLocales.Contains(fromLocale) &&
                    libraryMetadata.InternalLocale != fromLocale)
                {
                    // Coincidental cross-language collision.
                    return(null);
                }

                if (libraryMetadata == null)
                {
                    // Simply no matches at all.
                    return(null);
                }

                effectiveLocale = libraryMetadata.InternalLocale;
            }

            // Are there any restrictions on importing that library from this location?
            if (!libraryMetadata.IsAllowedImport(parser.CurrentLibrary))
            {
                throw new ParserException(throwToken, "This library cannot be imported from here.");
            }

            // Ensure all secondary lookups for each locale is instantiated to make the upcoming code more readable.
            if (!this.importedLibrariesByLocalizedName.ContainsKey(effectiveLocale))
            {
                this.importedLibrariesByLocalizedName[effectiveLocale] = new Dictionary <string, LocalizedLibraryView>();
            }
            if (!this.importedLibrariesByLocalizedName.ContainsKey(libraryMetadata.InternalLocale))
            {
                this.importedLibrariesByLocalizedName[libraryMetadata.InternalLocale] = new Dictionary <string, LocalizedLibraryView>();
            }

            // Check to see if this library has been imported before.
            if (this.importedLibrariesById.ContainsKey(libraryMetadata.ID))
            {
                // Is it imported by the same locale?
                if (this.importedLibrariesByLocalizedName[effectiveLocale].ContainsKey(name))
                {
                    // Then just return the previous instance as-is.
                    return(this.importedLibrariesByLocalizedName[effectiveLocale][name]);
                }

                // Wrap the previous instance in the new locale.
                LocalizedLibraryView output = new LocalizedLibraryView(effectiveLocale, this.importedLibrariesById[libraryMetadata.ID]);
                this.importedLibrariesByLocalizedName[effectiveLocale][output.Name] = output;
                return(output);
            }

            // If the library exists but hasn't been imported before, instantiate it and
            // add it to all the lookups. This needs to happen before parsing the embedded
            // code to prevent infinite recursion.
            LibraryCompilationScope libraryScope = new LibraryCompilationScope(parser.BuildContext, libraryMetadata);

            this.librariesAlreadyImportedIndexByKey[libraryMetadata.CanonicalKey] = this.ImportedLibraries.Count;
            this.ImportedLibraries.Add(libraryScope);
            this.importedLibrariesById[libraryMetadata.ID] = libraryScope;
            LocalizedLibraryView localizedView = new LocalizedLibraryView(effectiveLocale, libraryScope);

            this.importedLibrariesByLocalizedName[effectiveLocale][name] = localizedView;

            // Parse the library.
            parser.PushScope(libraryScope);
            Dictionary <string, string> embeddedCode = libraryMetadata.GetEmbeddedCode();

            foreach (string embeddedFile in embeddedCode.Keys.OrderBy(s => s.ToLower()))
            {
                string fakeName = "[" + embeddedFile + "]";
                string code     = embeddedCode[embeddedFile];
                parser.ParseInterpretedCode(fakeName, code);
            }
            parser.PopScope();

            return(localizedView);
        }
 public LocalizedLibraryView(Locale locale, LibraryCompilationScope libraryScope)
 {
     this.Locale       = locale;
     this.LibraryScope = libraryScope;
     this.FullyQualifiedEntityLookup = null;
 }