Esempio n. 1
0
        /* Function: FinalizeLanguage
         *
         * Merges the settings of a <ConfigFiles.TextFileLanguage> into a <Language>, returning whether it was successful.  If there
         * are any errors it will add them to the error list.
         *
         * - This does not handle file extensions, aliases, or shebang strings as <Language> does not store them.
         *
         * - This does not handle prototype enders as comment type names aren't available in <CommentTypes.Manager> until
         *   stage 2.
         *
         * - This will check for errors such as defining comment symbols on text files, shebang strings, or languages with full support.
         *
         * - This will generate Javadoc and XML comment symbols from the line and block comment symbols for languages with
         *   basic support, assuming they were not already set as part of a predefined language object.
         *
         * - This will generate <Language.SimpleIdentifier> if one is not defined, though it may still be null if there are no acceptable
         *   characters in the name, such as if the language name only used Japanese characters.
         */
        protected bool FinalizeLanguage(ref Language baseLanguage, ConfigFiles.TextFileLanguage overridingLanguage,
                                        Errors.ErrorList errorList)
        {
                        #if DEBUG
            if (baseLanguage.Name.NormalizeKey(Config.KeySettingsForLanguageName) !=
                overridingLanguage.Name.NormalizeKey(Config.KeySettingsForLanguageName))
            {
                throw new Exception("Can't finalize language " + baseLanguage.Name + " with settings for " + overridingLanguage.Name + ".");
            }
                        #endif

            if (!ApplyProperties(ref baseLanguage, overridingLanguage, errorList))
            {
                return(false);
            }

            GenerateJavadocCommentSymbols(baseLanguage);
            GenerateXMLCommentSymbols(baseLanguage);

            if (baseLanguage.SimpleIdentifier == null)
            {
                // This may end up as an empty string if there's no A-Z characters, such as if the name is in Japanese.  In this case
                // we want it to be "LanguageID[number]" but the number isn't determind yet, so leave it as null for now.
                string simpleIdentifier = baseLanguage.Name.OnlyAToZ();

                if (!string.IsNullOrEmpty(simpleIdentifier))
                {
                    baseLanguage.SimpleIdentifier = simpleIdentifier;
                }
            }

            return(true);
        }
Esempio n. 2
0
        /* Function: ApplyProperties
         *
         * Merges the settings of a <ConfigFiles.TextFileLanguage> into a <Language>, returning whether it was successful.  If there
         * are any errors it will add them to the error list.
         *
         * - This does not handle file extensions, aliases, or shebang strings as <Language> does not store them.
         *
         * - This does not handle prototype enders as comment type names aren't available in <CommentTypes.Manager> until
         *   stage 2.
         *
         * - This will check for errors such as defining comment symbols on text files, shebang strings, or languages with full support.
         */
        protected bool ApplyProperties(ref Language baseLanguage, ConfigFiles.TextFileLanguage overridingLanguage,
                                       Errors.ErrorList errorList)
        {
            int originalErrorCount = errorList.Count;

            if (overridingLanguage.HasSimpleIdentifier)
            {
                baseLanguage.SimpleIdentifier = overridingLanguage.SimpleIdentifier;
            }

            if (overridingLanguage.HasLineCommentSymbols &&
                CheckForBasicLanguageSupport(baseLanguage, "Line Comments",
                                             overridingLanguage.LineCommentSymbolsPropertyLocation, errorList))
            {
                baseLanguage.LineCommentSymbols = overridingLanguage.LineCommentSymbols;
            }

            if (overridingLanguage.HasBlockCommentSymbols &&
                CheckForBasicLanguageSupport(baseLanguage, "Block Comments",
                                             overridingLanguage.BlockCommentSymbolsPropertyLocation, errorList))
            {
                baseLanguage.BlockCommentSymbols = overridingLanguage.BlockCommentSymbols;
            }

            if (overridingLanguage.HasMemberOperator &&
                (baseLanguage.Type == Language.LanguageType.TextFile ||
                 CheckForBasicLanguageSupport(baseLanguage, "Member Operator",
                                              overridingLanguage.MemberOperatorPropertyLocation, errorList)))
            {
                baseLanguage.MemberOperator = overridingLanguage.MemberOperator;
            }

            // Skip prototype enders in stage 1

            if (overridingLanguage.HasLineExtender &&
                CheckForBasicLanguageSupport(baseLanguage, "LineExtender",
                                             overridingLanguage.LineExtenderPropertyLocation, errorList))
            {
                baseLanguage.LineExtender = overridingLanguage.LineExtender;
            }

            if (overridingLanguage.HasEnumValues &&
                (baseLanguage.Type == Language.LanguageType.TextFile ||
                 CheckForBasicLanguageSupport(baseLanguage, "Enum Values",
                                              overridingLanguage.EnumValuesPropertyLocation, errorList)))
            {
                baseLanguage.EnumValue = (Language.EnumValues)overridingLanguage.EnumValues;
            }

            if (overridingLanguage.HasCaseSensitive &&
                (baseLanguage.Type == Language.LanguageType.TextFile ||
                 CheckForBasicLanguageSupport(baseLanguage, "Case Sensitive",
                                              overridingLanguage.CaseSensitivePropertyLocation, errorList)))
            {
                baseLanguage.CaseSensitive = (bool)overridingLanguage.CaseSensitive;
            }

            return(errorList.Count == originalErrorCount);
        }
Esempio n. 3
0
        /* Function: AddLanguage
         * Adds a language to the file.
         */
        public void AddLanguage(TextFileLanguage language)
        {
            if (languages == null)
            {
                languages = new List <TextFileLanguage>();
            }

            languages.Add(language);
        }
Esempio n. 4
0
        /* Function: ValidateLanguage
         * Validates all the settings in a <ConfigFiles.TextFileLanguage>.  Returns whether it is valid, and adds any errors it finds to
         * errorList.
         */
        protected bool ValidateLanguage(ConfigFiles.TextFileLanguage language, Errors.ErrorList errorList)
        {
            // TextFileParser should have already normalized file extensions, so entering ".txt" or "*.txt" is converted to just "txt".

            // TextFileParser should have already validated that Simple Identifier only contains acceptable characters.

            // We'll check for Alter Language entries not matching an existing one when we merge languages.

            // We'll check for basic language support entries being applied to languages with full support when we merge languages.

            // So I guess there's nothing to do then!
            return(true);
        }
Esempio n. 5
0
        /* Function: Duplicate
         * Creates an independent copy of the language and all its properties.
         */
        public TextFileLanguage Duplicate()
        {
            TextFileLanguage copy = new TextFileLanguage(name, namePropertyLocation, alterLanguage);

            copy.simpleIdentifier = simpleIdentifier;
            copy.simpleIdentifierPropertyLocation = simpleIdentifierPropertyLocation;

            if (aliases != null)
            {
                copy.aliases = new List <string>(aliases.Count);
                copy.aliases.AddRange(aliases);
            }

            copy.aliasesPropertyChange   = aliasesPropertyChange;
            copy.aliasesPropertyLocation = aliasesPropertyLocation;

            if (fileExtensions != null)
            {
                copy.fileExtensions = new List <string>(fileExtensions.Count);
                copy.fileExtensions.AddRange(fileExtensions);
            }

            copy.fileExtensionsPropertyChange   = fileExtensionsPropertyChange;
            copy.fileExtensionsPropertyLocation = fileExtensionsPropertyLocation;

            if (shebangStrings != null)
            {
                copy.shebangStrings = new List <string>(shebangStrings.Count);
                copy.shebangStrings.AddRange(shebangStrings);
            }

            copy.shebangStringsPropertyChange   = shebangStringsPropertyChange;
            copy.shebangStringsPropertyLocation = shebangStringsPropertyLocation;

            if (lineCommentSymbols != null)
            {
                copy.lineCommentSymbols = new List <string>(lineCommentSymbols.Count);
                copy.lineCommentSymbols.AddRange(lineCommentSymbols);
            }

            copy.lineCommentSymbolsPropertyLocation = lineCommentSymbolsPropertyLocation;

            if (blockCommentSymbols != null)
            {
                copy.blockCommentSymbols = new List <BlockCommentSymbols>(blockCommentSymbols.Count);
                copy.blockCommentSymbols.AddRange(blockCommentSymbols);                  // works because it's a struct
            }

            copy.blockCommentSymbolsPropertyLocation = blockCommentSymbolsPropertyLocation;
            copy.memberOperator = memberOperator;
            copy.memberOperatorPropertyLocation = memberOperatorPropertyLocation;

            if (prototypeEnders != null)
            {
                copy.prototypeEnders = new List <TextFilePrototypeEnders>();
                foreach (var prototypeEnder in prototypeEnders)
                {
                    copy.prototypeEnders.Add(prototypeEnder.Duplicate());
                }
            }

            copy.lineExtender = lineExtender;
            copy.lineExtenderPropertyLocation = lineExtenderPropertyLocation;
            copy.enumValues = enumValues;
            copy.enumValuesPropertyLocation = enumValuesPropertyLocation;
            copy.caseSensitive = caseSensitive;
            copy.caseSensitivePropertyLocation = caseSensitivePropertyLocation;

            return(copy);
        }
Esempio n. 6
0
        /* Function: MergeLanguageInto
         * Merges the settings of a <ConfigFiles.TextFileLanguage> into another one, overriding the settings of the first.  This does
         * NOT cover file extensions, aliases, or shebang strings.  The base object will be altered.
         */
        protected void MergeLanguageInto(ref ConfigFiles.TextFileLanguage baseLanguage,
                                         ConfigFiles.TextFileLanguage overridingLanguage)
        {
            // Leave Name and PropertyLocation alone.  We'll keep the base's.

            // Leave AlterLanguage alone.  The base should be false and the overriding should be true, and we want the end
            // result to be false.

            if (overridingLanguage.HasSimpleIdentifier)
            {
                baseLanguage.SetSimpleIdentifier(overridingLanguage.SimpleIdentifier,
                                                 overridingLanguage.SimpleIdentifierPropertyLocation);
            }

            // Ignore Aliases

            // Ignore FileExtensions

            // Ignore ShebangStrings

            if (overridingLanguage.HasLineCommentSymbols)
            {
                baseLanguage.SetLineCommentSymbols(overridingLanguage.LineCommentSymbols,
                                                   overridingLanguage.LineCommentSymbolsPropertyLocation);
            }

            if (overridingLanguage.HasBlockCommentSymbols)
            {
                baseLanguage.SetBlockCommentSymbols(overridingLanguage.BlockCommentSymbols,
                                                    overridingLanguage.BlockCommentSymbolsPropertyLocation);
            }

            if (overridingLanguage.HasMemberOperator)
            {
                baseLanguage.SetMemberOperator(overridingLanguage.MemberOperator,
                                               overridingLanguage.MemberOperatorPropertyLocation);
            }

            if (overridingLanguage.HasPrototypeEnders)
            {
                foreach (var prototypeEnders in overridingLanguage.PrototypeEnders)
                {
                    // This will automatically overwrite any ender with the same comment type.
                    baseLanguage.AddPrototypeEnders(prototypeEnders);
                }
            }

            if (overridingLanguage.HasLineExtender)
            {
                baseLanguage.SetLineExtender(overridingLanguage.LineExtender,
                                             overridingLanguage.LineExtenderPropertyLocation);
            }

            if (overridingLanguage.HasEnumValues)
            {
                baseLanguage.SetEnumValues(overridingLanguage.EnumValues,
                                           overridingLanguage.EnumValuesPropertyLocation);
            }

            if (overridingLanguage.HasCaseSensitive)
            {
                baseLanguage.SetCaseSensitive(overridingLanguage.CaseSensitive,
                                              overridingLanguage.CaseSensitivePropertyLocation);
            }
        }
Esempio n. 7
0
        // 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);
            }
        }