// Group: Initialization Functions // __________________________________________________________________________ /* Function: Start_Stage1 * * Loads and combines the two versions of <Languages.txt>, returning whether it was successful. If there were any * errors they will be added to errorList. * * Only the settings which don't depend on <Comments.txt> will be loaded. Call <Start_Stage2()> after * <CommentTypes.Manager.Start_Stage1()> has been called to complete the process. * * Dependencies: * * - <Config.Manager> must be started before this class can start. */ public bool Start_Stage1(Errors.ErrorList errorList) { StartupIssues newStartupIssues = StartupIssues.None; bool success = true; // // Languages.txt // Path systemTextConfigPath = EngineInstance.Config.SystemConfigFolder + "/Languages.txt"; Path projectTextConfigPath = EngineInstance.Config.ProjectConfigFolder + "/Languages.txt"; ConfigFiles.TextFileParser textConfigFileParser = new ConfigFiles.TextFileParser(); // Load the system Languages.txt. if (!textConfigFileParser.Load(systemTextConfigPath, PropertySource.SystemLanguagesFile, errorList, out systemTextConfig)) { success = false; } // Load the project Languages.txt. We want to do this even if the system Languages.txt failed so we get the error messages // from both. if (System.IO.File.Exists(projectTextConfigPath)) { if (!textConfigFileParser.Load(projectTextConfigPath, PropertySource.ProjectLanguagesFile, errorList, out projectTextConfig)) { success = false; } } // If it doesn't exist it's not an error. Just create a blank config. else { projectTextConfig = new ConfigFiles.TextFile(); } if (!success) { return(false); } if (!ValidateLanguages(systemTextConfig, errorList)) { success = false; } if (!ValidateLanguages(projectTextConfig, errorList)) { success = false; } if (!success) { return(false); } // Merge them into one combined config. Note that this doesn't do file extensions, aliases, or shebang strings. Only the // languages and their other properties will exist in the merged config. if (!MergeLanguages(systemTextConfig, projectTextConfig, out mergedTextConfig, errorList)) { return(false); } if (!ValidateLanguages(mergedTextConfig, errorList)) { return(false); } // // Languages.nd // Path lastRunConfigPath = EngineInstance.Config.WorkingDataFolder + "/Languages.nd"; ConfigFiles.BinaryFileParser binaryConfigFileParser = new ConfigFiles.BinaryFileParser(); // If we need to start fresh anyway we can skip loading the file and create a blank config if (EngineInstance.HasIssues(StartupIssues.NeedToStartFresh | StartupIssues.CommentIDsInvalidated | StartupIssues.CodeIDsInvalidated) || // though if that wasn't the case but we failed at loading the file, the result is the same !binaryConfigFileParser.Load(lastRunConfigPath, out lastRunConfig)) { lastRunConfig = new Config(); newStartupIssues |= StartupIssues.NeedToReparseAllFiles | StartupIssues.CodeIDsInvalidated; } // // Create the final config // config = new Config(); // We go through the languages present in the merged text config files and convert them into the final config. We // need the contents of Comments.nd so we can keep the language IDs consistent from one run to the next if possible. IDObjects.NumberSet usedLanguageIDs = new IDObjects.NumberSet(); IDObjects.NumberSet lastRunUsedLanguageIDs = lastRunConfig.UsedLanguageIDs(); if (mergedTextConfig.HasLanguages) { foreach (var textLanguage in mergedTextConfig.Languages) { // First we need our base language object. If there's a predefined language matching the name we'll use that so // we get its settings and parser. If not we'll create a new object. We shouldn't have to worry about more than one // language using the same predefined language object because validation should have taken care of that. var finalLanguage = FindPredefinedLanguage(textLanguage.Name); if (finalLanguage == null) { finalLanguage = new Language(textLanguage.Name); } // Apply the properties. Keep going if there are errors since we want to find them all. if (!FinalizeLanguage(ref finalLanguage, textLanguage, errorList)) { success = false; } // We still need to set the ID. See if a language of the same name existed in the previous run. var lastRunLanguage = lastRunConfig.LanguageFromName(textLanguage.Name); // If there wasn't one we can assign a new ID, but pick one that isn't used in this run or the last run so there's no // conflicts. if (lastRunLanguage == null) { int id = lastRunUsedLanguageIDs.LowestAvailable; if (usedLanguageIDs.Contains(id)) { id = Math.Max(usedLanguageIDs.Highest + 1, lastRunUsedLanguageIDs.Highest + 1); } finalLanguage.ID = id; config.AddLanguage(finalLanguage); usedLanguageIDs.Add(finalLanguage.ID); lastRunUsedLanguageIDs.Add(finalLanguage.ID); } // If the language did exist but we haven't used its ID yet, we can keep it. else if (!usedLanguageIDs.Contains(lastRunLanguage.ID)) { finalLanguage.ID = lastRunLanguage.ID; config.AddLanguage(finalLanguage); usedLanguageIDs.Add(finalLanguage.ID); } // However, if the language did exist and we assigned that ID already, then we have a conflict and have to tell the // engine that the IDs from the last run were invalidated. We can just assign anything unused at this point since it // no longer matters. else { finalLanguage.ID = usedLanguageIDs.LowestAvailable; config.AddLanguage(finalLanguage); usedLanguageIDs.Add(finalLanguage.ID); newStartupIssues |= StartupIssues.CodeIDsInvalidated; } // Now that we have a final ID, set the simple identifier for any type that still needs it. Some types may have it null if // it wasn't manually defined and the name didn't contain A-Z characters. if (finalLanguage.SimpleIdentifier == null) { finalLanguage.SimpleIdentifier = "LanguageID" + finalLanguage.ID; } // Also create a generic parser object if one wasn't inherited from a predefined language. if (!finalLanguage.HasParser) { finalLanguage.Parser = new Parser(EngineInstance, finalLanguage); } } } if (!success) { return(false); } // Apply file extensions, aliases, and shebang strings now. MergeLanguageIdentifiersInto(ref config, systemTextConfig, projectTextConfig); // That's it for stage one. Everything else is in stage 2. if (newStartupIssues != StartupIssues.None) { EngineInstance.AddStartupIssues(newStartupIssues); } return(true); }
/* Function: Start_Stage2 * * Finishes loading and and combing the two versions of <Languages.txt>, returning whether it was successful. If there * were any errors they will be added to errorList. * * This must be called after <Start_Stage1()> has been called, and also <CommentTypes.Manager.Start_Stage1()>. This * finalizes any settings which also depend on <Comments.txt>. * * Dependencies: * * - <Config.Manager> must be started before this class can start. * - <Start_Stage1()> must be called and return true before this function can be called. * - <CommentTypes.Manager.Start_Stage1()> must be called and return true before this function can be called. */ public bool Start_Stage2(Errors.ErrorList errorList) { bool success = true; // Go through the lists and apply prototype enders, since CommentTypes.Manager should have all their names now. if (!MergePrototypeEndersInto_Stage2(ref config, systemTextConfig, errorList)) { success = false; } if (!MergePrototypeEndersInto_Stage2(ref config, projectTextConfig, errorList)) { success = false; } if (!success) { return(false); } // Now we have our final configuration and everything is okay. Save the text files again to reformat them. TouchUp_Stage2(ref systemTextConfig, config); TouchUp_Stage2(ref projectTextConfig, config); Path systemTextConfigPath = EngineInstance.Config.SystemConfigFolder + "/Languages.txt"; Path projectTextConfigPath = EngineInstance.Config.ProjectConfigFolder + "/Languages.txt"; ConfigFiles.TextFileParser textConfigFileParser = new ConfigFiles.TextFileParser(); // If the project Languages.txt didn't exist, saving the blank structure that was created will create a default one. if (!textConfigFileParser.Save(projectTextConfigPath, PropertySource.ProjectLanguagesFile, projectTextConfig, errorList)) { return(false); } ; // We don't care if we're not able to resave the system Languages.txt since it may be in a protected location. textConfigFileParser.Save(systemTextConfigPath, PropertySource.SystemLanguagesFile, systemTextConfig, errorList: null); // Save Languages.nd as well. Path lastRunConfigPath = EngineInstance.Config.WorkingDataFolder + "/Languages.nd"; ConfigFiles.BinaryFileParser binaryConfigFileParser = new ConfigFiles.BinaryFileParser(); binaryConfigFileParser.Save(lastRunConfigPath, config); // Compare the config against the previous one and reparse everything if there are changes. Changes that would invalidate // IDs have already been handled. if (config != lastRunConfig) { EngineInstance.AddStartupIssues(StartupIssues.NeedToReparseAllFiles); } // We're done with these variables which were only needed between start stages 1 and 2. systemTextConfig = null; projectTextConfig = null; mergedTextConfig = null; lastRunConfig = null; started = true; return(true); }