/* Function: Duplicate * Creates an independent copy of the prototype enders and all their attributes. */ public TextFilePrototypeEnders Duplicate() { TextFilePrototypeEnders copy = new TextFilePrototypeEnders(commentType, propertyLocation); copy.enderStrings = new List <string>(enderStrings.Count); copy.enderStrings.AddRange(enderStrings); return(copy); }
/* Function: AddPrototypeEnders * Adds <TextFilePrototypeEnders> which maps a comment type string to its ender strings. Line breaks are represented * with "\n". If a set of enders strings already exists for the comment type string it will be replaced. */ public void AddPrototypeEnders(TextFilePrototypeEnders prototypeEnders) { if (this.prototypeEnders == null) { this.prototypeEnders = new List <TextFilePrototypeEnders>(); } int index = FindPrototypeEndersIndex(prototypeEnders.CommentType); if (index == -1) { this.prototypeEnders.Add(prototypeEnders); } else { // We could just overwrite the entry at the index but we want to preserve the order they appear in. this.prototypeEnders.RemoveAt(index); this.prototypeEnders.Add(prototypeEnders); } }
// Group: Loading Functions // __________________________________________________________________________ /* Function: Load * * Loads the contents of a <Languages.txt> file into a <ConfigFiles.Textfile>, returning whether it was successful. If it * was unsuccessful config will be null and it will place errors on the errorList. * * Parameters: * * filename - The <Path> where the file is located. * propertySource - The <Engine.Config.PropertySource> associated with the file. * errorList - If it couldn't successfully parse the file it will add error messages to this list. * config - The contents of the file as a <ConfigFiles.TextFile>. */ public bool Load(Path filename, Engine.Config.PropertySource propertySource, Errors.ErrorList errorList, out ConfigFiles.TextFile config) { int previousErrorCount = errorList.Count; using (ConfigFile file = new ConfigFile()) { bool openResult = file.Open(filename, propertySource, ConfigFile.FileFormatFlags.CondenseIdentifierWhitespace | ConfigFile.FileFormatFlags.CondenseValueWhitespace | ConfigFile.FileFormatFlags.MakeIdentifiersLowercase, errorList); if (openResult == false) { config = null; return(false); } config = new ConfigFiles.TextFile(); TextFileLanguage currentLanguage = null; char[] space = { ' ' }; System.Text.RegularExpressions.Match match; while (file.Get(out string identifier, out string value)) { // // Ignore Extensions // if (ignoreExtensionsRegex.IsMatch(identifier)) { currentLanguage = null; var ignoredExtensions = value.Split(space); NormalizeFileExtensions(ignoredExtensions); config.AddIgnoredFileExtensions(ignoredExtensions, file.PropertyLocation); } // // Language // else if (identifier == "language") { var existingLanguage = config.FindLanguage(value); if (existingLanguage != null) { 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 = existingLanguage; } else { currentLanguage = new TextFileLanguage(value, file.PropertyLocation); config.AddLanguage(currentLanguage); } } // // Alter Language // else if (alterLanguageRegex.IsMatch(identifier)) { // We don't check if the name exists because it may exist in a different file. We also don't check if it exists // in the current file because using Alter is valid (if unnecessary) in that case and we don't want to combine // their definitions. Why? Consider this: // // Language: Language A // Extensions: langA // // Language: Language B // Extensions: langB // // Alter Language: Language A // Add Extensions: langB // // Extension langB should be part of Language A. However, if we merged the definitions it would appear // first and be overridden by Language B. So we just create two language entries for A instead. currentLanguage = new TextFileLanguage(value, file.PropertyLocation, alterLanguage: true); config.AddLanguage(currentLanguage); } // // Aliases // else if (aliasesRegex.IsMatch(identifier)) { if (currentLanguage == null) { AddNeedsLanguageError(file, identifier); } else if (currentLanguage.AlterLanguage) { file.AddError( Locale.Get("NaturalDocs.Engine", "Languages.txt.NeedAddReplaceWhenAlteringLanguage(keyword)", "Aliases") ); } else { var aliases = value.Split(space); currentLanguage.SetAliases(aliases, file.PropertyLocation); } } // // Add/Replace Aliases // else if ((match = addReplaceAliasesRegex.Match(identifier)) != null && match.Success) { if (currentLanguage == null) { AddNeedsLanguageError(file, identifier); } else { TextFileLanguage.PropertyChange propertyChange; if (match.Groups[1].Value == "add") { propertyChange = TextFileLanguage.PropertyChange.Add; } else if (match.Groups[1].Value == "replace") { propertyChange = TextFileLanguage.PropertyChange.Replace; } else { throw new NotImplementedException(); } // If we're adding to a language that already has them, we need to combine the properties. if (propertyChange == TextFileLanguage.PropertyChange.Add && currentLanguage.HasAliases) { var oldAliases = currentLanguage.Aliases; var newAliases = value.Split(space); List <string> combinedAliases = new List <string>(oldAliases.Count + newAliases.Length); combinedAliases.AddRange(oldAliases); combinedAliases.AddRange(newAliases); currentLanguage.SetAliases(combinedAliases, file.PropertyLocation, propertyChange); } // Otherwise we can just add them as is. else { var aliases = value.Split(space); currentLanguage.SetAliases(aliases, file.PropertyLocation, propertyChange); } } } // // File Extensions // else if (fileExtensionsRegex.IsMatch(identifier)) { if (currentLanguage == null) { AddNeedsLanguageError(file, identifier); } else if (currentLanguage.AlterLanguage) { file.AddError( Locale.Get("NaturalDocs.Engine", "Languages.txt.NeedAddReplaceWhenAlteringLanguage(keyword)", "Extensions") ); } else { var extensions = value.Split(space); NormalizeFileExtensions(extensions); currentLanguage.SetFileExtensions(extensions, file.PropertyLocation); } } // // Add/Replace File Extensions // else if ((match = addReplaceExtensionsRegex.Match(identifier)) != null && match.Success) { if (currentLanguage == null) { AddNeedsLanguageError(file, identifier); } else { TextFileLanguage.PropertyChange propertyChange; if (match.Groups[1].Value == "add") { propertyChange = TextFileLanguage.PropertyChange.Add; } else if (match.Groups[1].Value == "replace") { propertyChange = TextFileLanguage.PropertyChange.Replace; } else { throw new NotImplementedException(); } // If we're adding to a language that already has them, we need to combine the properties. if (propertyChange == TextFileLanguage.PropertyChange.Add && currentLanguage.HasFileExtensions) { var oldExtensions = currentLanguage.FileExtensions; var newExtensions = value.Split(space); NormalizeFileExtensions(newExtensions); List <string> combinedExtensions = new List <string>(oldExtensions.Count + newExtensions.Length); combinedExtensions.AddRange(oldExtensions); combinedExtensions.AddRange(newExtensions); currentLanguage.SetFileExtensions(combinedExtensions, file.PropertyLocation, propertyChange); } // Otherwise we can just add them as is. else { var extensions = value.Split(space); NormalizeFileExtensions(extensions); currentLanguage.SetFileExtensions(extensions, file.PropertyLocation, propertyChange); } } } // // Shebang Strings // else if (shebangStringsRegex.IsMatch(identifier)) { if (currentLanguage == null) { AddNeedsLanguageError(file, identifier); } else if (currentLanguage.AlterLanguage) { file.AddError( Locale.Get("NaturalDocs.Engine", "Languages.txt.NeedAddReplaceWhenAlteringLanguage(keyword)", "Shebang Strings") ); } else { var shebangStrings = value.Split(space); currentLanguage.SetShebangStrings(shebangStrings, file.PropertyLocation); } } // // Add/Replace Shebang Strings // else if ((match = addReplaceShebangStringsRegex.Match(identifier)) != null && match.Success) { if (currentLanguage == null) { AddNeedsLanguageError(file, identifier); } else { TextFileLanguage.PropertyChange propertyChange; if (match.Groups[1].Value == "add") { propertyChange = TextFileLanguage.PropertyChange.Add; } else if (match.Groups[1].Value == "replace") { propertyChange = TextFileLanguage.PropertyChange.Replace; } else { throw new NotImplementedException(); } // If we're adding to a language that already has them, we need to combine the properties. if (propertyChange == TextFileLanguage.PropertyChange.Add && currentLanguage.HasShebangStrings) { var oldShebangStrings = currentLanguage.ShebangStrings; var newShebangStrings = value.Split(space); List <string> combinedShebangStrings = new List <string>(oldShebangStrings.Count + newShebangStrings.Length); combinedShebangStrings.AddRange(oldShebangStrings); combinedShebangStrings.AddRange(newShebangStrings); currentLanguage.SetShebangStrings(combinedShebangStrings, file.PropertyLocation, propertyChange); } // Otherwise we can just add them as is. else { var shebangStrings = value.Split(space); currentLanguage.SetShebangStrings(shebangStrings, file.PropertyLocation, propertyChange); } } } // // Simple Identifier // else if (identifier == "simple identifier") { if (currentLanguage == null) { AddNeedsLanguageError(file, identifier); } else if (nonASCIILettersRegex.IsMatch(value)) { file.AddError( Locale.Get("NaturalDocs.Engine", "Languages.txt.SimpleIdentifierMustOnlyBeASCIILetters(name)", value) ); } else { currentLanguage.SetSimpleIdentifier(value, file.PropertyLocation); } } // // Line Comments // else if (lineCommentsRegex.IsMatch(identifier)) { if (currentLanguage == null) { AddNeedsLanguageError(file, identifier); } else { var lineCommentSymbols = value.Split(space); currentLanguage.SetLineCommentSymbols(lineCommentSymbols, file.PropertyLocation); } } // // Block Comments // else if (blockCommentsRegex.IsMatch(identifier)) { if (currentLanguage == null) { AddNeedsLanguageError(file, identifier); } else { var blockCommentStrings = value.Split(space); if (blockCommentStrings.Length % 2 != 0) { file.AddError( Locale.Get("NaturalDocs.Engine", "Languages.txt.BlockCommentsMustHaveAnEvenNumberOfSymbols") ); } else { List <BlockCommentSymbols> blockCommentSymbols = new List <BlockCommentSymbols>(blockCommentStrings.Length / 2); for (int i = 0; i < blockCommentStrings.Length; i += 2) { blockCommentSymbols.Add( new BlockCommentSymbols(blockCommentStrings[i], blockCommentStrings[i + 1]) ); } currentLanguage.SetBlockCommentSymbols(blockCommentSymbols, file.PropertyLocation); } } } // // Member Operator // else if (memberOperatorRegex.IsMatch(identifier)) { if (currentLanguage == null) { AddNeedsLanguageError(file, identifier); } else { currentLanguage.SetMemberOperator(value, file.PropertyLocation); } } // // Line Extender // else if (identifier == "line extender") { if (currentLanguage == null) { AddNeedsLanguageError(file, identifier); } else { currentLanguage.SetLineExtender(value, file.PropertyLocation); } } // // Enum Values // else if (enumValuesRegex.IsMatch(identifier)) { if (currentLanguage == null) { AddNeedsLanguageError(file, identifier); } else { string lcValue = value.ToLower(); if (lcValue == "global") { currentLanguage.SetEnumValues(Language.EnumValues.Global, file.PropertyLocation); } else if (lcValue == "under type") { currentLanguage.SetEnumValues(Language.EnumValues.UnderType, file.PropertyLocation); } else if (lcValue == "under parent") { currentLanguage.SetEnumValues(Language.EnumValues.UnderParent, file.PropertyLocation); } else { file.AddError( Locale.Get("NaturalDocs.Engine", "Languages.txt.InvalidEnumValue(value)", value) ); } } } // // Case Sensitive // else if (caseSensitiveRegex.IsMatch(identifier)) { if (currentLanguage == null) { AddNeedsLanguageError(file, identifier); } else { string lcValue = value.ToLower(); if (yesRegex.IsMatch(lcValue)) { currentLanguage.SetCaseSensitive(true, file.PropertyLocation); } else if (noRegex.IsMatch(lcValue)) { currentLanguage.SetCaseSensitive(false, file.PropertyLocation); } 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) { AddNeedsLanguageError(file, identifier); } else { string commentType = match.Groups[1].Value; string[] enderStrings = value.Split(space); var enders = new TextFilePrototypeEnders(commentType, file.PropertyLocation); enders.AddEnderStrings(enderStrings); currentLanguage.AddPrototypeEnders(enders); } } // // Deprecated keywords // else if (ignorePrefixesRegex.IsMatch(identifier) || identifier == "perl package" || identifier == "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); } }