/* Function: Start_CantDefinePropertyError * A helper function used only by <Start()> and its other helper functions which adds an error saying the passed * property cannot be defined for the current language type. */ private void Start_CantDefinePropertyError(ConfigFileLanguage configFileLanguage, Language.LanguageType type, Path sourceFile, string propertyName, Errors.ErrorList errorList) { string typeString; if (type == Language.LanguageType.TextFile) { typeString = "TextFiles"; } else if (type == Language.LanguageType.Container) { typeString = "Containers"; } else if (type == Language.LanguageType.FullSupport) { typeString = "FullLanguageSupport"; } else // BasicSupport { typeString = "BasicLanguageSupport"; } errorList.Add( Locale.Get("NaturalDocs.Engine", "Languages.txt.CantDefinePropertyFor" + typeString + "(property, language)", propertyName, configFileLanguage.Name), sourceFile, configFileLanguage.LineNumber ); }
// Group: Loading Functions // __________________________________________________________________________ /* Function: Load * * Loads the configuration file and parses it. Redundant information will be simplified out, such as an Alter * Language section that applies to a language defined in the same file. * * Parameters: * * filename - The <Path> where the file is located. * fileLanguages - Returns a list of <ConfigFileLanguages> in no particular order. * fileIgnoredExtensions - Returns any ignored extensions as a string array. * errorList - If it couldn't successfully parse the file it will add error messages to this list. * * Returns: * * Whether it was able to successfully load and parse the file without any errors. */ public bool Load(Path filename, out List <ConfigFileLanguage> fileLanguages, out List <string> fileIgnoredExtensions, Errors.ErrorList errorList) { fileLanguages = new List <ConfigFileLanguage>(); fileIgnoredExtensions = new List <string>(); StringTable <ConfigFileLanguage> fileLanguageNames = new StringTable <ConfigFileLanguage>(Engine.Languages.Manager.KeySettingsForLanguageName); int previousErrorCount = errorList.Count; using (ConfigFile file = new ConfigFile()) { // Can't make identifiers lowercase here or we'd lose the case of the comment type in prototype ender lines. bool openResult = file.Open(filename, ConfigFile.FileFormatFlags.CondenseIdentifierWhitespace | ConfigFile.FileFormatFlags.CondenseValueWhitespace, errorList); if (openResult == false) { return(false); } string identifier, lcIdentifier, value; ConfigFileLanguage currentLanguage = null; // We need this in addition to ConfigFileLanguage.AlterLanguage because an entry altering a type defined in the // same file would be combined into the original, yet we still need to know if that entry is Alter to properly // detect whether we need to use Add/Replace with certain properties. bool alterCurrentLanguage = false; char[] space = { ' ' }; System.Text.RegularExpressions.Match match; while (file.Get(out identifier, out value)) { lcIdentifier = identifier.ToLower(); // // Ignore Extensions // if (ignoreExtensionsRegex.IsMatch(lcIdentifier)) { currentLanguage = null; string[] ignoredExtensionsArray = value.Split(space); fileIgnoredExtensions.AddRange(ignoredExtensionsArray); } // // Language // else if (lcIdentifier == "language") { if (fileLanguageNames.ContainsKey(value)) { file.AddError( Locale.Get("NaturalDocs.Engine", "Languages.txt.LanguageAlreadyExists(name)", value) ); // Continue parsing. We'll throw this into the existing language even though it shouldn't be overwriting // its values because we want to find any other errors there are in the file. currentLanguage = fileLanguageNames[value]; alterCurrentLanguage = false; } else { currentLanguage = new ConfigFileLanguage(value, false, file.LineNumber); alterCurrentLanguage = false; fileLanguages.Add(currentLanguage); fileLanguageNames.Add(value, currentLanguage); } } // // Alter Language // else if (alterLanguageRegex.IsMatch(lcIdentifier)) { // If this language already exists, collapse it into the current definition. if (fileLanguageNames.ContainsKey(value)) { currentLanguage = fileLanguageNames[value]; alterCurrentLanguage = true; } // If it doesn't exist, create the new language anyway with the alter flag set because it may exist in another // file. else { currentLanguage = new ConfigFileLanguage(value, true, file.LineNumber); alterCurrentLanguage = true; fileLanguages.Add(currentLanguage); fileLanguageNames.Add(value, currentLanguage); } } // // Aliases // else if (aliasesRegex.IsMatch(lcIdentifier)) { if (currentLanguage == null) { NeedsLanguageError(file, identifier); } else if (alterCurrentLanguage == true) { file.AddError( Locale.Get("NaturalDocs.Engine", "Languages.txt.NeedAddReplaceWhenAlteringLanguage(keyword)", "Aliases") ); } else { currentLanguage.Aliases = value.Split(space); currentLanguage.AddAliases = false; } } // // Add/Replace Aliases // else if ((match = addReplaceAliasesRegex.Match(lcIdentifier)) != null && match.Success) { if (currentLanguage == null) { NeedsLanguageError(file, identifier); } else if (alterCurrentLanguage == true && match.Groups[1].Value == "add" && currentLanguage.Aliases != null) { string[] addAliases = value.Split(space); string[] newAliases = new string[addAliases.Length + currentLanguage.Aliases.Length]; currentLanguage.Aliases.CopyTo(newAliases, 0); addAliases.CopyTo(newAliases, currentLanguage.Aliases.Length); currentLanguage.Aliases = newAliases; currentLanguage.AddAliases = true; } // Covers "replace" when altering a language, "add" and "replace" when not altering a language (no point // in adding an error when we can just tolerate it, and "replace" when altering a language that doesn't have // anything defined. else { currentLanguage.Aliases = value.Split(space); currentLanguage.AddAliases = (match.Groups[1].Value == "add"); } } // // Extensions // else if (extensionsRegex.IsMatch(lcIdentifier)) { if (currentLanguage == null) { NeedsLanguageError(file, identifier); } else if (alterCurrentLanguage == true) { file.AddError( Locale.Get("NaturalDocs.Engine", "Languages.txt.NeedAddReplaceWhenAlteringLanguage(keyword)", "Extensions") ); } else { currentLanguage.Extensions = value.Split(space); currentLanguage.AddExtensions = false; } } // // Add/Replace Extensions // else if ((match = addReplaceExtensionsRegex.Match(lcIdentifier)) != null && match.Success) { if (currentLanguage == null) { NeedsLanguageError(file, identifier); } else if (alterCurrentLanguage == true && match.Groups[1].Value == "add" && currentLanguage.Extensions != null) { string[] addExtensions = value.Split(space); string[] newExtensions = new string[addExtensions.Length + currentLanguage.Extensions.Length]; currentLanguage.Extensions.CopyTo(newExtensions, 0); addExtensions.CopyTo(newExtensions, currentLanguage.Extensions.Length); currentLanguage.Extensions = newExtensions; currentLanguage.AddExtensions = true; } // Covers "replace" when altering a language, "add" and "replace" when not altering a language (no point // in adding an error when we can just tolerate it, and "replace" when altering a language that doesn't have // anything defined. else { currentLanguage.Extensions = value.Split(space); currentLanguage.AddExtensions = (match.Groups[1].Value == "add"); } } // // Shebang Strings // else if (shebangStringsRegex.IsMatch(lcIdentifier)) { if (currentLanguage == null) { NeedsLanguageError(file, identifier); } else if (alterCurrentLanguage == true) { file.AddError( Locale.Get("NaturalDocs.Engine", "Languages.txt.NeedAddReplaceWhenAlteringLanguage(keyword)", "Shebang Strings") ); } else { currentLanguage.ShebangStrings = value.Split(space); currentLanguage.AddShebangStrings = false; } } // // Add/Replace Shebang Strings // else if ((match = addReplaceShebangStringsRegex.Match(lcIdentifier)) != null && match.Success) { if (currentLanguage == null) { NeedsLanguageError(file, identifier); } else if (alterCurrentLanguage == true && match.Groups[1].Value == "add" && currentLanguage.ShebangStrings != null) { string[] addShebangStrings = value.Split(space); string[] newShebangStrings = new string[addShebangStrings.Length + currentLanguage.ShebangStrings.Length]; currentLanguage.ShebangStrings.CopyTo(newShebangStrings, 0); addShebangStrings.CopyTo(newShebangStrings, currentLanguage.ShebangStrings.Length); currentLanguage.ShebangStrings = newShebangStrings; currentLanguage.AddShebangStrings = true; } // Covers "replace" when altering a language, "add" and "replace" when not altering a language (no point // in adding an error when we can just tolerate it, and "replace" when altering a language that doesn't have // anything defined. else { currentLanguage.ShebangStrings = value.Split(space); currentLanguage.AddShebangStrings = (match.Groups[1].Value == "add"); } } // // Simple Identifier // else if (lcIdentifier == "simple identifier") { if (currentLanguage == null) { NeedsLanguageError(file, identifier); } else if (nonASCIILettersRegex.IsMatch(value)) { file.AddError( Locale.Get("NaturalDocs.Engine", "Languages.txt.SimpleIdentifierMustOnlyBeASCIILetters(name)", value) ); } else { currentLanguage.SimpleIdentifier = value; } } // // Line Comments // else if (lineCommentsRegex.IsMatch(lcIdentifier)) { if (currentLanguage == null) { NeedsLanguageError(file, identifier); } else { currentLanguage.LineCommentStrings = value.Split(space); } } // // Block Comments // else if (blockCommentsRegex.IsMatch(lcIdentifier)) { if (currentLanguage == null) { NeedsLanguageError(file, identifier); } else { string[] newBlockCommentStrings = value.Split(space); if (newBlockCommentStrings.Length % 2 != 0) { file.AddError( Locale.Get("NaturalDocs.Engine", "Languages.txt.BlockCommentsMustHaveAnEvenNumberOfSymbols") ); } else { currentLanguage.BlockCommentStringPairs = newBlockCommentStrings; } } } // // Member Operator // else if (memberOperatorRegex.IsMatch(lcIdentifier)) { if (currentLanguage == null) { NeedsLanguageError(file, identifier); } else { currentLanguage.MemberOperator = value; } } // // Line Extender // else if (lcIdentifier == "line extender") { if (currentLanguage == null) { NeedsLanguageError(file, identifier); } else { currentLanguage.LineExtender = value; } } // // Enum Values // else if (enumValuesRegex.IsMatch(lcIdentifier)) { string lcValue = value.ToLower(); if (currentLanguage == null) { NeedsLanguageError(file, identifier); } else if (lcValue == "global") { currentLanguage.EnumValue = Language.EnumValues.Global; } else if (lcValue == "under type") { currentLanguage.EnumValue = Language.EnumValues.UnderType; } else if (lcValue == "under parent") { currentLanguage.EnumValue = Language.EnumValues.UnderParent; } else { file.AddError( Locale.Get("NaturalDocs.Engine", "Languages.txt.InvalidEnumValue(value)", value) ); } } // // Case Sensitive // else if (caseSensitiveRegex.IsMatch(lcIdentifier)) { string lcValue = value.ToLower(); if (currentLanguage == null) { NeedsLanguageError(file, identifier); } else if (yesRegex.IsMatch(lcValue)) { currentLanguage.CaseSensitive = true; } else if (noRegex.IsMatch(lcValue)) { currentLanguage.CaseSensitive = false; } else { file.AddError( Locale.Get("NaturalDocs.Engine", "Languages.txt.UnrecognizedValue(keyword, value)", "Case Sensitive", value) ); } } // // Prototype Enders // // Use identifier and not lcIdentifier to keep the case of the comment type. The regex will compensate. else if ((match = prototypeEndersRegex.Match(identifier)) != null && match.Success) { if (currentLanguage == null) { NeedsLanguageError(file, identifier); } else { string commentType = match.Groups[1].Value; string[] enderStrings = value.Split(space); currentLanguage.SetPrototypeEnderStrings(commentType, enderStrings); } } // // Deprecated keywords // else if (ignorePrefixesRegex.IsMatch(lcIdentifier) || lcIdentifier == "perl package" || lcIdentifier == "full language support") { // Ignore } // // Unrecognized keywords // else { file.AddError( Locale.Get("NaturalDocs.Engine", "Languages.txt.UnrecognizedKeyword(keyword)", identifier) ); } } // while (file.Get) file.Close(); } if (errorList.Count == previousErrorCount) { return(true); } else { return(false); } }
/* Function: Start_AddLanguage * A helper function that is used only by <Start()> to add a <ConfigFileLanguage> into <languages>. * Returns whether it was able to do so without any errors. */ private bool Start_AddLanguage(ConfigFileLanguage configFileLanguage, Path sourceFile, bool isSystemFile, StringSet ignoredExtensions, Errors.ErrorList errorList) { bool success = true; // Validate or create the language. if (configFileLanguage.AlterLanguage == true) { // If altering a language that doesn't exist at all, at least not in the config files... if (languages.Contains(configFileLanguage.Name) == false || languages[configFileLanguage.Name].InConfigFiles == false) { errorList.Add( Locale.Get("NaturalDocs.Engine", "Languages.txt.AlteredLanguageDoesntExist(name)", configFileLanguage.Name), sourceFile, configFileLanguage.LineNumber ); success = false; } } else // define language, not alter { // Error if defining a language that already exists in the config files. Having it exist otherwise is fine. if (languages.Contains(configFileLanguage.Name)) { if (languages[configFileLanguage.Name].InConfigFiles == true) { errorList.Add( Locale.Get("NaturalDocs.Engine", "Languages.txt.LanguageAlreadyExists(name)", configFileLanguage.Name), sourceFile, configFileLanguage.LineNumber ); success = false; } } else { Language newLanguage = new Language(this, configFileLanguage.Name); languages.Add(newLanguage); } if (isSystemFile) { languages[configFileLanguage.Name].InSystemFile = true; } else { languages[configFileLanguage.Name].InProjectFile = true; } } if (success == false) { return(false); } // Apply the properties. Language language = languages[configFileLanguage.Name]; if (configFileLanguage.SimpleIdentifier != null) { language.SimpleIdentifier = configFileLanguage.SimpleIdentifier; } if (configFileLanguage.LineCommentStrings != null) { if (language.Type != Language.LanguageType.BasicSupport) { Start_CantDefinePropertyError(configFileLanguage, language.Type, sourceFile, "Line Comment", errorList); success = false; } else { language.LineCommentStrings = configFileLanguage.LineCommentStrings; } } if (configFileLanguage.BlockCommentStringPairs != null) { if (language.Type != Language.LanguageType.BasicSupport) { Start_CantDefinePropertyError(configFileLanguage, language.Type, sourceFile, "Block Comment", errorList); success = false; } else { language.BlockCommentStringPairs = configFileLanguage.BlockCommentStringPairs; } } if (configFileLanguage.MemberOperator != null) { if (language.Type != Language.LanguageType.BasicSupport && language.Type != Language.LanguageType.TextFile) { Start_CantDefinePropertyError(configFileLanguage, language.Type, sourceFile, "Member Operator", errorList); success = false; } else { language.MemberOperator = configFileLanguage.MemberOperator; } } if (configFileLanguage.LineExtender != null) { if (language.Type != Language.LanguageType.BasicSupport) { Start_CantDefinePropertyError(configFileLanguage, language.Type, sourceFile, "Line Extender", errorList); success = false; } else { language.LineExtender = configFileLanguage.LineExtender; } } if (configFileLanguage.EnumValue != null) { if (language.Type != Language.LanguageType.BasicSupport && language.Type != Language.LanguageType.TextFile) { Start_CantDefinePropertyError(configFileLanguage, language.Type, sourceFile, "Enum Value", errorList); success = false; } else { language.EnumValue = (Language.EnumValues)configFileLanguage.EnumValue; } } if (configFileLanguage.CaseSensitive != null) { if (language.Type != Language.LanguageType.BasicSupport && language.Type != Language.LanguageType.TextFile) { Start_CantDefinePropertyError(configFileLanguage, language.Type, sourceFile, "Case Sensitive", errorList); success = false; } else { language.CaseSensitive = (bool)configFileLanguage.CaseSensitive; } } string[] commentTypeNamesWithPrototypeEnders = configFileLanguage.GetCommentTypeNamesWithPrototypeEnders(); if (commentTypeNamesWithPrototypeEnders != null) { if (language.Type != Language.LanguageType.BasicSupport) { Start_CantDefinePropertyError(configFileLanguage, language.Type, sourceFile, "Prototype Enders", errorList); success = false; } else { foreach (string commentTypeName in commentTypeNamesWithPrototypeEnders) { CommentTypes.CommentType commentType = EngineInstance.CommentTypes.FromName(commentTypeName); if (commentType == null) { errorList.Add( Locale.Get("NaturalDocs.Engine", "Languages.txt.PrototypeEnderCommentTypeDoesntExist(name)", commentTypeName), sourceFile, configFileLanguage.LineNumber ); success = false; } else { string[] prototypeEnderStrings = configFileLanguage.GetPrototypeEnderStrings(commentTypeName); PrototypeEnders prototypeEnders = new PrototypeEnders(prototypeEnderStrings); language.SetPrototypeEnders(commentType.ID, prototypeEnders); } } } } // Apply the aliases, extensions, and shebang strings. if (configFileLanguage.Aliases != null) { // If using Replace Aliases, find all existing aliases pointing to this language and remove them. if (configFileLanguage.AlterLanguage == true && configFileLanguage.AddAliases == false) { List <string> removedAliases = new List <string>(); foreach (KeyValuePair <string, Language> pair in aliases) { if ((object)pair.Value == (object)language) { removedAliases.Add(pair.Key); } } foreach (string removedAlias in removedAliases) { aliases.Remove(removedAlias); } } // Add new aliases. foreach (string alias in configFileLanguage.Aliases) { aliases[alias] = language; } } if (configFileLanguage.Extensions != null) { // If using Replace Extensions, find all existing extensions pointing to this language and remove them. if (configFileLanguage.AlterLanguage == true && configFileLanguage.AddExtensions == false) { List <string> removedExtensions = new List <string>(); foreach (KeyValuePair <string, Language> pair in extensions) { if ((object)pair.Value == (object)language) { removedExtensions.Add(pair.Key); } } foreach (string removedExtension in removedExtensions) { extensions.Remove(removedExtension); } } // Add new extensions. foreach (string extension in configFileLanguage.Extensions) { if (ignoredExtensions.Contains(extension) == false) { extensions[extension] = language; } } } if (configFileLanguage.ShebangStrings != null) { // If using Replace Shebang Strings, find all existing shebang strings pointing to this language and remove them. if (configFileLanguage.AlterLanguage == true && configFileLanguage.AddShebangStrings == false) { List <string> removedShebangStrings = new List <string>(); foreach (KeyValuePair <string, Language> pair in shebangStrings) { if ((object)pair.Value == (object)language) { removedShebangStrings.Add(pair.Key); } } foreach (string removedShebangString in removedShebangStrings) { shebangStrings.Remove(removedShebangString); } } // Add new shebang strings. foreach (string shebangString in configFileLanguage.ShebangStrings) { shebangStrings[shebangString] = language; } } return(success); }