about summary refs log tree commit diff
path: root/docs/tool/Modules/NaturalDocs/SymbolTable.pm
diff options
context:
space:
mode:
authorMagnus Auvinen <magnus.auvinen@gmail.com>2008-08-02 08:21:29 +0000
committerMagnus Auvinen <magnus.auvinen@gmail.com>2008-08-02 08:21:29 +0000
commit61bfe2d70cae6be8c4086a210a5451135ccca9ea (patch)
tree62bf7808b1b2bfe5f56fe1e329871fb0991d0687 /docs/tool/Modules/NaturalDocs/SymbolTable.pm
parenta13b94f9e0bca8ea892311d9d9e0c0bc48616ea7 (diff)
downloadzcatch-61bfe2d70cae6be8c4086a210a5451135ccca9ea.tar.gz
zcatch-61bfe2d70cae6be8c4086a210a5451135ccca9ea.zip
added doc tool
Diffstat (limited to 'docs/tool/Modules/NaturalDocs/SymbolTable.pm')
-rw-r--r--docs/tool/Modules/NaturalDocs/SymbolTable.pm1984
1 files changed, 1984 insertions, 0 deletions
diff --git a/docs/tool/Modules/NaturalDocs/SymbolTable.pm b/docs/tool/Modules/NaturalDocs/SymbolTable.pm
new file mode 100644
index 00000000..02f010f3
--- /dev/null
+++ b/docs/tool/Modules/NaturalDocs/SymbolTable.pm
@@ -0,0 +1,1984 @@
+###############################################################################
+#
+#   Package: NaturalDocs::SymbolTable
+#
+###############################################################################
+#
+#   A package that handles all the gory details of managing symbols.  It handles where they are defined, which files
+#   reference them, if any are undefined or duplicated, and loading and saving them to a file.
+#
+#   Usage and Dependencies:
+#
+#       - At any time, <RebuildAllIndexes()> can be called.
+#
+#       - <NaturalDocs::Settings>, <NaturalDocs::Languages>, and <NaturalDocs::Project> must be initialized before use.
+#
+#       - <Load()> must be called to initialize the package.  At this point, the <Information Functions> will return the symbol
+#         table as of the last time Natural Docs was run.
+#
+#       - Note that <Load()> and <Save()> only manage <REFERENCE_TEXT> references.  All other reference types must be
+#         managed by their respective classes.  They should be readded after <Load()> to recreate the state of the last time
+#         Natural Docs was run.
+#
+#       - <Purge()> must be called, and then <NaturalDocs::Parser->ParseForInformation()> on all files that have changed so it
+#         can fully resolve the symbol table via the <Modification Functions>.  Afterwards <PurgeResolvingInfo()> can be called
+#         to reclaim some memory, and the symbol table will reflect the current state of the code.
+#
+#       - <Save()> must be called to commit any changes to the symbol table back to disk.
+#
+###############################################################################
+
+# This file is part of Natural Docs, which is Copyright (C) 2003-2008 Greg Valure
+# Natural Docs is licensed under the GPL
+
+
+use NaturalDocs::SymbolTable::Symbol;
+use NaturalDocs::SymbolTable::SymbolDefinition;
+use NaturalDocs::SymbolTable::Reference;
+use NaturalDocs::SymbolTable::File;
+use NaturalDocs::SymbolTable::ReferenceTarget;
+use NaturalDocs::SymbolTable::IndexElement;
+
+use strict;
+use integer;
+
+package NaturalDocs::SymbolTable;
+
+
+
+###############################################################################
+# Group: Variables
+
+#
+#   handle: SYMBOLTABLE_FILEHANDLE
+#
+#   The file handle used with <SymbolTable.nd>.
+#
+
+#
+#   hash: symbols
+#
+#   A hash of all <SymbolStrings>.  The keys are the <SymbolStrings> and the values are <NaturalDocs::SymbolTable::Symbol>
+#   objects.
+#
+#   Prior to <PurgeResolvingInfo()>, both defined symbols and symbols that are merely potential interpretations of references
+#   will be here.  Afterwards, only defined symbols will be here.
+#
+my %symbols;
+
+#
+#   hash: references
+#
+#   A hash of all references in the project.  The keys are <ReferenceStrings> and the values are
+#   <NaturalDocs::SymbolTable::Reference> objects.
+#
+#   Prior to <PurgeResolvingInfo()>, all possible interpretations will be stored for each reference.  Afterwards, only the current
+#   interpretation will be.
+#
+my %references;
+
+#
+#   hash: files
+#
+#   A hash of all the files that define symbols and references in the project.  The keys are the <FileNames>, and the values are
+#   <NaturalDocs::SymbolTable::File> objects.
+#
+#   After <PurgeResolvingInfo()>, this hash will be empty.
+#
+my %files;
+
+#
+#   object: watchedFile
+#
+#   A <NaturalDocs::SymbolTable::File> object of the file being watched for changes.  This is compared to the version in <files>
+#   to see if anything was changed since the last parse.
+#
+my $watchedFile;
+
+#
+#   string: watchedFileName
+#
+#   The <FileName> of the watched file, if any.  If there is no watched file, this will be undef.
+#
+my $watchedFileName;
+
+#
+#   hash: watchedFileSymbolDefinitions
+#
+#   A hashref of the symbol definition information for all the <SymbolStrings> in the watched file.  The keys are the symbol strings,
+#   and the values are <NaturalDocs::SymbolTable::SymbolDefinition> objects.
+#
+my %watchedFileSymbolDefinitions;
+
+
+#
+#   hash: indexes
+#
+#   A hash of generated symbol indexes.  The keys are <TopicTypes> and the values are sorted arrayrefs of
+#   <NaturalDocs::SymbolTable::IndexElements>, or undef if its empty.
+#
+my %indexes;
+
+
+#
+#   hash: indexChanges
+#
+#   A hash of all the indexes that have changed.  The keys are the <TopicTypes> and the entries are undef if they have not
+#   changed, or 1 if they have.  The key will not exist if the <TopicType> has not been checked.
+#
+my %indexChanges;
+
+
+#
+#   hash: indexSectionsWithContent
+#
+#   A hash of which sections in an index have content.  The keys are the <TopicTypes> of each index, and the values are
+#   arrayrefs of bools where the first represents symbols, the second numbers, and the rest A-Z.  If there is no information
+#   available for an index, it's entry will not exist here.
+#
+my %indexSectionsWithContent;
+
+
+#
+#   bool: rebuildIndexes
+#
+#   Whether all indexes should be rebuilt regardless of whether they have been changed.
+#
+my $rebuildIndexes;
+
+
+
+###############################################################################
+# Group: Files
+
+
+#
+#   File: SymbolTable.nd
+#
+#   The storage file for the symbol table.
+#
+#   Format:
+#
+#       > [BINARY_FORMAT]
+#       > [VersionInt: app version]
+#
+#       The file starts with the standard <BINARY_FORMAT> <VersionInt> header.
+#
+#       The first stage of the file is for symbol definitions, analogous to <symbols>.
+#
+#       > [SymbolString: symbol or undef to end] ...
+#       >
+#       > [UInt16: number of definitions]
+#       >
+#       >    [AString16: global definition file] [AString16: TopicType]
+#       >       [AString16: prototype] [AString16: summary]
+#       >
+#       >    [AString16: definition file] ...
+#       >
+#       >    ...
+#
+#       These blocks continue until the <SymbolString> is undef.  Only defined symbols will be included in this file, so
+#       number of definitions will never be zero.  The first one is always the global definition.  If a symbol does not have a
+#       prototype or summary, the UInt16 length of the string will be zero.
+#
+#       The second stage is for references, which is analogous to <references>.  Only <REFERENCE_TEXT> references are
+#       stored in this file, and their <Resolving Flags> are implied so they aren't stored either.
+#
+#       > [ReferenceString (no type, resolving flags): reference or undef to end]
+#       >
+#       > [UInt8: number of definition files]
+#       >    [AString16: definition file] [AString16: definition file] ...
+#
+#       These blocks continue until the <ReferenceString> is undef.  Since there can be multiple using <SymbolStrings>, those
+#       continue until the number of identifiers is zero.  Note that all interpretations are rebuilt rather than stored.
+#
+#   See Also:
+#
+#       <File Format Conventions>
+#
+#   Revisions:
+#
+#       1.3:
+#
+#           - Symbol <TopicTypes> were changed from UInt8s to AString16s, now that <TopicTypes> are strings instead of
+#             integer constants.
+#
+#       1.22:
+#
+#           - File format was completely rebuilt to accommodate the new symbol format and to be in binary.  To see the plain text
+#             format prior to 1.22, check out 1.21's version of this file from CVS.  It is too big a change to note here.
+#
+
+
+#
+#   File: IndexInfo.nd
+#
+#   The storage file for information about the indexes.
+#
+#   Format:
+#
+#       > [Standard Header]
+#
+#       The standard binary file header.
+#
+#       > [AString16: index topic name]
+#       > [uint8: symbols have content (0 or 1)]
+#       > [uint8: numbers have content (0 or 1)]
+#       > [uint8: A has content] [uint8: B has content] ...
+#       > ...
+#
+#       Every index that has information about it is stored with the topic type name first, then 28 uint8s that say whether that
+#       part of the index has content or not.  The first is for symbols, the second is for numbers, and the rest are for A-Z.  If an
+#       index's state is unknown, it won't appear in this file.
+#
+#   Revisions:
+#
+#       1.4:
+#
+#           - The file is introduced.
+#
+
+
+
+###############################################################################
+# Group: File Functions
+
+
+#
+#   Function: Load
+#
+#   Loads all data files from disk.
+#
+sub Load
+    {
+    my ($self) = @_;
+
+    $self->LoadSymbolTable();
+    $self->LoadIndexInfo();
+    };
+
+
+#
+#   Function: LoadSymbolTable
+#
+#   Loads <SymbolTable.nd> from disk.
+#
+sub LoadSymbolTable
+    {
+    my ($self) = @_;
+
+    my $fileIsOkay;
+
+    if (!NaturalDocs::Settings->RebuildData() &&
+        open(SYMBOLTABLE_FILEHANDLE, '<' . NaturalDocs::Project->DataFile('SymbolTable.nd')) )
+        {
+        # See if it's binary.
+        binmode(SYMBOLTABLE_FILEHANDLE);
+
+        my $firstChar;
+        read(SYMBOLTABLE_FILEHANDLE, $firstChar, 1);
+
+        if ($firstChar == ::BINARY_FORMAT())
+            {
+            my $version = NaturalDocs::Version->FromBinaryFile(\*SYMBOLTABLE_FILEHANDLE);
+
+            # 1.3 is incompatible with previous versions.
+
+            if (NaturalDocs::Version->CheckFileFormat( $version, NaturalDocs::Version->FromString('1.3') ))
+                {  $fileIsOkay = 1;  }
+            else
+                {  close(SYMBOLTABLE_FILEHANDLE);  };
+            }
+
+        else
+            {  close(SYMBOLTABLE_FILEHANDLE);  };
+        };
+
+
+    if (!$fileIsOkay)
+        {
+        NaturalDocs::Project->ReparseEverything();
+        return;
+        }
+
+    my $raw;
+
+
+    # Symbols
+
+    for (;;)
+        {
+        # [SymbolString: symbol or undef to end]
+
+        my $symbol = NaturalDocs::SymbolString->FromBinaryFile(\*SYMBOLTABLE_FILEHANDLE);
+
+        if (!defined $symbol)
+            {  last;  };
+
+        my $symbolObject = NaturalDocs::SymbolTable::Symbol->New();
+        $symbols{$symbol} = $symbolObject;
+
+        # [UInt16: number of definitions]
+
+        read(SYMBOLTABLE_FILEHANDLE, $raw, 2);
+        my $definitionCount = unpack('n', $raw);
+
+        do
+            {
+            # [AString16: (global?) definition file]
+
+            read(SYMBOLTABLE_FILEHANDLE, $raw, 2);
+            my $fileLength = unpack('n', $raw);
+
+            my $file;
+            read(SYMBOLTABLE_FILEHANDLE, $file, $fileLength);
+
+            # [AString16: TopicType]
+
+            read(SYMBOLTABLE_FILEHANDLE, $raw, 2);
+            my $typeLength = unpack('n', $raw);
+
+            my $type;
+            read(SYMBOLTABLE_FILEHANDLE, $type, $typeLength);
+
+            # [AString16: prototype]
+
+            read(SYMBOLTABLE_FILEHANDLE, $raw, 2);
+            my $prototypeLength = unpack('n', $raw);
+
+            my $prototype;
+            if ($prototypeLength)
+                {  read(SYMBOLTABLE_FILEHANDLE, $prototype, $prototypeLength);  };
+
+            # [AString16: summary]
+
+            read(SYMBOLTABLE_FILEHANDLE, $raw, 2);
+            my $summaryLength = unpack('n', $raw);
+
+            my $summary;
+            if ($summaryLength)
+                {  read(SYMBOLTABLE_FILEHANDLE, $summary, $summaryLength);  };
+
+            $symbolObject->AddDefinition($file, $type, $prototype, $summary);
+
+            # Add it.
+
+            if (!exists $files{$file})
+                {  $files{$file} = NaturalDocs::SymbolTable::File->New();  };
+
+            $files{$file}->AddSymbol($symbol);
+
+            $definitionCount--;
+            }
+        while ($definitionCount);
+        };
+
+
+    # References
+
+    for (;;)
+        {
+        # [ReferenceString (no type, resolving flags): reference or undef to end]
+
+        my $referenceString = NaturalDocs::ReferenceString->FromBinaryFile(\*SYMBOLTABLE_FILEHANDLE,
+                                                                                                              ::BINARYREF_NOTYPE() |
+                                                                                                              ::BINARYREF_NORESOLVINGFLAGS(),
+                                                                                                              ::REFERENCE_TEXT(), undef);
+
+        if (!defined $referenceString)
+            {  last;  };
+
+        my $referenceObject = NaturalDocs::SymbolTable::Reference->New();
+        $references{$referenceString} = $referenceObject;
+
+        # [UInt8: number of definition files]
+
+        read(SYMBOLTABLE_FILEHANDLE, $raw, 1);
+        my $definitionCount = unpack('C', $raw);
+        do
+            {
+            # [AString16: definition file] [AString16: definition file] ...
+
+            read(SYMBOLTABLE_FILEHANDLE, $raw, 2);
+            my $definitionLength = unpack('n', $raw);
+
+            my $definition;
+            read(SYMBOLTABLE_FILEHANDLE, $definition, $definitionLength);
+
+            # Add it.
+
+            $referenceObject->AddDefinition($definition);
+
+            if (!exists $files{$definition})
+                {  $files{$definition} = NaturalDocs::SymbolTable::File->New();  };
+
+            $files{$definition}->AddReference($referenceString);
+
+            $definitionCount--;
+            }
+        while ($definitionCount);
+
+        $self->GenerateInterpretations($referenceString);
+        $self->InterpretReference($referenceString);
+        };
+
+    close(SYMBOLTABLE_FILEHANDLE);
+    };
+
+
+#
+#   Function: LoadIndexInfo
+#
+#   Loads <IndexInfo.nd> from disk.
+#
+sub LoadIndexInfo
+    {
+    my ($self) = @_;
+
+    if (NaturalDocs::Settings->RebuildData())
+        {  return;  };
+
+    my $version = NaturalDocs::BinaryFile->OpenForReading( NaturalDocs::Project->DataFile('IndexInfo.nd') );
+
+    if (!defined $version)
+        {  return;  }
+
+    # The file format hasn't changed since it was introduced.
+    if (!NaturalDocs::Version->CheckFileFormat($version))
+        {
+        NaturalDocs::BinaryFile->Close();
+        return;
+        };
+
+    my $topicTypeName;
+    while ($topicTypeName = NaturalDocs::BinaryFile->GetAString16())
+        {
+        my $topicType = NaturalDocs::Topics->TypeFromName($topicTypeName);
+        my $content = [ ];
+
+        for (my $i = 0; $i < 28; $i++)
+            {  push @$content, NaturalDocs::BinaryFile->GetUInt8();  };
+
+        if (defined $topicType)  # The name in the file could be from a type that was deleted
+            {  $indexSectionsWithContent{$topicType} = $content;  };
+        };
+
+    NaturalDocs::BinaryFile->Close();
+    };
+
+
+#
+#   Function: Purge
+#
+#   Purges the symbol table of all symbols and references from files that no longer have Natural Docs content.
+#
+sub Purge
+    {
+    my ($self) = @_;
+
+    my $filesToPurge = NaturalDocs::Project->FilesToPurge();
+
+    # We do this in two stages.  First we delete all the references, and then we delete all the definitions.  This causes us to go
+    # through the list twice, but it makes sure no purged files get added to the build list.  For example, if we deleted all of
+    # Purge File A's references and definitions, and Purge File B had a reference to one of those symbols, Purge File B
+    # would be added to the build list because one of its references changed.  By removing all the references in all the files
+    # before removing the definitions, we avoid this.
+
+    foreach my $file (keys %$filesToPurge)
+        {
+        if (exists $files{$file})
+            {
+            my @references = $files{$file}->References();
+            foreach my $reference (@references)
+                {  $self->DeleteReference($reference, $file);  };
+            };
+        };
+
+    foreach my $file (keys %$filesToPurge)
+        {
+        if (exists $files{$file})
+            {
+            my @symbols = $files{$file}->Symbols();
+            foreach my $symbol (@symbols)
+                {  $self->DeleteSymbol($symbol, $file);  };
+
+            delete $files{$file};
+            };
+        };
+    };
+
+
+#
+#   Function: Save
+#
+#   Saves all data files to disk.
+#
+sub Save
+    {
+    my ($self) = @_;
+
+    $self->SaveSymbolTable();
+    $self->SaveIndexInfo();
+    };
+
+
+#
+#   Function: SaveSymbolTable
+#
+#   Saves <SymbolTable.nd> to disk.
+#
+sub SaveSymbolTable
+    {
+    my ($self) = @_;
+
+    open (SYMBOLTABLE_FILEHANDLE, '>' . NaturalDocs::Project->DataFile('SymbolTable.nd'))
+        or die "Couldn't save " . NaturalDocs::Project->DataFile('SymbolTable.nd') . ".\n";
+
+    binmode(SYMBOLTABLE_FILEHANDLE);
+
+    print SYMBOLTABLE_FILEHANDLE '' . ::BINARY_FORMAT();
+
+    NaturalDocs::Version->ToBinaryFile(\*SYMBOLTABLE_FILEHANDLE, NaturalDocs::Settings->AppVersion());
+
+
+    # Symbols
+
+    while (my ($symbol, $symbolObject) = each %symbols)
+        {
+        # Only existing symbols.
+        if ($symbolObject->IsDefined())
+            {
+            # [SymbolString: symbol or undef to end]
+
+            NaturalDocs::SymbolString->ToBinaryFile(\*SYMBOLTABLE_FILEHANDLE, $symbol);
+
+            # [UInt16: number of definitions]
+
+            my @definitions = $symbolObject->Definitions();
+            print SYMBOLTABLE_FILEHANDLE pack('n', scalar @definitions);
+
+            # [AString16: global definition file] [AString16: TopicType]
+
+            print SYMBOLTABLE_FILEHANDLE pack('nA*nA*', length $symbolObject->GlobalDefinition(),
+                                                                                   $symbolObject->GlobalDefinition(),
+                                                                                   length $symbolObject->GlobalType(),
+                                                                                   $symbolObject->GlobalType());
+
+            # [AString16: prototype]
+
+            my $prototype = $symbolObject->GlobalPrototype();
+
+            if (defined $prototype)
+                {  print SYMBOLTABLE_FILEHANDLE pack('nA*', length($prototype), $prototype);  }
+            else
+                {  print SYMBOLTABLE_FILEHANDLE pack('n', 0);  };
+
+            # [AString16: summary]
+
+            my $summary = $symbolObject->GlobalSummary();
+
+            if (defined $summary)
+                {  print SYMBOLTABLE_FILEHANDLE pack('nA*', length($summary), $summary);  }
+            else
+                {  print SYMBOLTABLE_FILEHANDLE pack('n', 0);  };
+
+
+            foreach my $definition (@definitions)
+                {
+                if ($definition ne $symbolObject->GlobalDefinition())
+                    {
+                    # [AString16: definition file] [AString16: TopicType]
+
+                    print SYMBOLTABLE_FILEHANDLE pack('nA*nA*', length $definition, $definition,
+                                                                                           length $symbolObject->TypeDefinedIn($definition),
+                                                                                           $symbolObject->TypeDefinedIn($definition));
+
+                    # [AString16: prototype]
+
+                    my $prototype = $symbolObject->PrototypeDefinedIn($definition);
+
+                    if (defined $prototype)
+                        {  print SYMBOLTABLE_FILEHANDLE pack('nA*', length($prototype), $prototype);  }
+                    else
+                        {  print SYMBOLTABLE_FILEHANDLE pack('n', 0);  };
+
+                    # [AString16: summary]
+
+                    my $summary = $symbolObject->SummaryDefinedIn($definition);
+
+                    if (defined $summary)
+                        {  print SYMBOLTABLE_FILEHANDLE pack('nA*', length($summary), $summary);  }
+                    else
+                        {  print SYMBOLTABLE_FILEHANDLE pack('n', 0);  };
+                    };
+                };
+            };
+        };
+
+     # [SymbolString: symbol or undef to end]
+
+     NaturalDocs::SymbolString->ToBinaryFile(\*SYMBOLTABLE_FILEHANDLE, undef);
+
+
+     # References
+
+    while (my ($reference, $referenceObject) = each %references)
+        {
+        my $type = NaturalDocs::ReferenceString->TypeOf($reference);
+
+        if ($type == ::REFERENCE_TEXT())
+            {
+            # [ReferenceString (no type, resolving flags): reference or undef to end]
+
+            NaturalDocs::ReferenceString->ToBinaryFile(\*SYMBOLTABLE_FILEHANDLE, $reference,
+                                                                             ::BINARYREF_NOTYPE() | ::BINARYREF_NORESOLVINGFLAGS());
+
+            # [UInt8: number of definition files]
+
+            my @definitions = $referenceObject->Definitions();
+            print SYMBOLTABLE_FILEHANDLE pack('C', scalar @definitions);
+
+            # [AString16: definition file] [AString16: definition file] ...
+
+            foreach my $definition (@definitions)
+                {
+                print SYMBOLTABLE_FILEHANDLE pack('nA*', length($definition), $definition);
+                };
+            };
+        };
+
+    # [ReferenceString (no type, resolving flags): reference or undef to end]
+
+    NaturalDocs::ReferenceString->ToBinaryFile(\*SYMBOLTABLE_FILEHANDLE, undef,
+                                                                     ::BINARYREF_NOTYPE() | ::BINARYREF_NORESOLVINGFLAGS());
+
+    close(SYMBOLTABLE_FILEHANDLE);
+    };
+
+
+#
+#   Function: SaveIndexInfo
+#
+#   Saves <IndexInfo.nd> to disk.
+#
+sub SaveIndexInfo
+    {
+    my ($self) = @_;
+
+    NaturalDocs::BinaryFile->OpenForWriting( NaturalDocs::Project->DataFile('IndexInfo.nd') );
+
+    while (my ($topicType, $content) = each %indexSectionsWithContent)
+        {
+        NaturalDocs::BinaryFile->WriteAString16( NaturalDocs::Topics->NameOfType($topicType) );
+
+        for (my $i = 0; $i < 28; $i++)
+            {
+            if ($content->[$i])
+                {  NaturalDocs::BinaryFile->WriteUInt8(1);  }
+            else
+                {  NaturalDocs::BinaryFile->WriteUInt8(0);  };
+            };
+        };
+
+    NaturalDocs::BinaryFile->Close();
+    };
+
+
+
+###############################################################################
+# Group: Modification Functions
+# These functions should not be called after <PurgeResolvingInfo()>.
+
+#
+#   Function: AddSymbol
+#
+#   Adds a symbol definition to the table, if it doesn't already exist.  If the definition changes or otherwise requires the files that
+#   reference it to be updated, the function will call <NaturalDocs::Project->RebuildFile()> to make sure that they are.
+#
+#   Parameters:
+#
+#       symbol  - The <SymbolString>.
+#       file        - The <FileName> where it's defined.
+#       type      - The symbol's <TopicType>.
+#       prototype - The symbol's prototype, if applicable.
+#       summary - The symbol's summary, if applicable.
+#
+sub AddSymbol #(symbol, file, type, prototype, summary)
+    {
+    my ($self, $symbol, $file, $type, $prototype, $summary) = @_;
+
+
+    # If the symbol doesn't exist...
+    if (!exists $symbols{$symbol})
+        {
+        # Create the symbol.  There are no references that could be interpreted as this or else it would have existed already.
+
+        my $newSymbol = NaturalDocs::SymbolTable::Symbol->New();
+        $newSymbol->AddDefinition($file, $type, $prototype, $summary);
+
+        $symbols{$symbol} = $newSymbol;
+
+        $self->OnIndexChange($type);
+        NaturalDocs::Project->RebuildFile($file);
+        }
+
+
+    # If the symbol already exists...
+    else
+        {
+        my $symbolObject = $symbols{$symbol};
+
+        # If the symbol isn't defined, i.e. it was a potential interpretation only...
+        if (!$symbolObject->IsDefined())
+            {
+            $symbolObject->AddDefinition($file, $type, $prototype, $summary);
+
+            # See if this symbol provides a better interpretation of any references.  We can assume this symbol has interpretations
+            # because the object won't exist without either that or definitions.
+
+            my %referencesAndScores = $symbolObject->ReferencesAndScores();
+
+            while (my ($referenceString, $referenceScore) = each %referencesAndScores)
+                {
+                my $referenceObject = $references{$referenceString};
+
+                if (!$referenceObject->HasCurrentInterpretation() ||
+                    $referenceScore > $referenceObject->CurrentScore())
+                    {
+                    $referenceObject->SetCurrentInterpretation($symbol);
+                    $self->OnInterpretationChange($referenceString);
+                    };
+                };
+
+            $self->OnIndexChange($type);
+            NaturalDocs::Project->RebuildFile($file);
+            }
+
+        # If the symbol is defined but not in this file...
+        elsif (!$symbolObject->IsDefinedIn($file))
+            {
+            $symbolObject->AddDefinition($file, $type, $prototype, $summary);
+
+            $self->OnIndexChange($type);
+            NaturalDocs::Project->RebuildFile($file);
+
+            # We don't have to check other files because if the symbol is defined it already has a global definiton,
+            # and everything else is either using that or its own definition, and thus wouldn't be affected by this.
+            };
+
+        # If the symbol was already defined in this file, ignore it.
+
+        };
+
+
+    # Add it to the file index.
+
+    if (!exists $files{$file})
+        {  $files{$file} = NaturalDocs::SymbolTable::File->New();  };
+
+    $files{$file}->AddSymbol($symbol);
+
+
+    # Add it to the watched file, if necessary.
+
+    if (defined $watchedFileName)
+        {
+        $watchedFile->AddSymbol($symbol);
+
+        if (!exists $watchedFileSymbolDefinitions{$symbol})
+            {
+            $watchedFileSymbolDefinitions{$symbol} =
+                 NaturalDocs::SymbolTable::SymbolDefinition->New($type, $prototype, $summary);
+            };
+        };
+    };
+
+
+#
+#   Function: AddReference
+#
+#   Adds a reference to the table, if it doesn't already exist.
+#
+#   Parameters:
+#
+#       type        - The <ReferenceType>.
+#       symbol    - The reference <SymbolString>.
+#       scope      - The scope <SymbolString> it appears in.
+#       using       - An arrayref of scope <SymbolStrings> accessible to the reference via "using" statements, or undef if none.
+#       file          - The <FileName> where the reference appears.  This is not required unless the type is <REFERENCE_TEXT>.
+#       resolvingFlags - The <Resolving Flags> of the reference.  They will be ignored if the type is <REFERENCE_TEXT>.
+#
+#   Alternate Parameters:
+#
+#       referenceString - The <ReferenceString> to add.
+#       file - The <FileName> where the reference appears.  This is not required unless the type is <REFERENCE_TEXT>.
+#
+sub AddReference #(type, symbol, scope, using, file, resolvingFlags) or (referenceString, file)
+    {
+    my ($self, $referenceString, $file);
+
+    if (scalar @_ <= 3)
+        {
+        ($self, $referenceString, $file) = @_;
+        }
+    else
+        {
+        my ($type, $symbol, $scope, $using, $resolvingFlags);
+        ($self, $type, $symbol, $scope, $using, $file, $resolvingFlags) = @_;
+
+        $referenceString = NaturalDocs::ReferenceString->MakeFrom($type, $symbol,
+                                                                                                   NaturalDocs::Languages->LanguageOf($file)->Name(),
+                                                                                                   $scope, $using, $resolvingFlags);
+        };
+
+
+    # If the reference doesn't exist...
+    if (!exists $references{$referenceString})
+        {
+        my $referenceObject = NaturalDocs::SymbolTable::Reference->New();
+
+        $references{$referenceString} = $referenceObject;
+
+        $self->GenerateInterpretations($referenceString);
+        $self->InterpretReference($referenceString);
+        }
+
+
+    if (defined $file)
+        {
+        $references{$referenceString}->AddDefinition($file);
+
+
+        # Add it to the file index.
+
+        if (!exists $files{$file})
+            {  $files{$file} = NaturalDocs::SymbolTable::File->New();  };
+
+        $files{$file}->AddReference($referenceString);
+
+
+        # Add it to the watched file, if necessary.
+
+        if (defined $watchedFileName)
+            {  $watchedFile->AddReference($referenceString);  };
+        };
+    };
+
+
+#
+#   Function: WatchFileForChanges
+#
+#   Tracks a file to see if any symbols or references were changed or deleted in ways that would require other files to be rebuilt.
+#   Assumes that after this function call, the entire file will be parsed again, and thus every symbol and reference will go through
+#   <AddSymbol()> and <AddReference()>.  Afterwards, call <AnalyzeChanges()> to handle any differences.
+#
+#   Parameters:
+#
+#       file - The <FileName> to watch.
+#
+sub WatchFileForChanges #(file)
+    {
+    my ($self, $file) = @_;
+
+    $watchedFile = NaturalDocs::SymbolTable::File->New();
+    $watchedFileName = $file;
+    %watchedFileSymbolDefinitions = ( );
+    };
+
+
+#
+#   Function: AnalyzeChanges
+#
+#   Handles any changes found when reparsing a file using <WatchFileForChanges()>.
+#
+sub AnalyzeChanges
+    {
+    my ($self) = @_;
+
+    if (exists $files{$watchedFileName})
+        {
+
+        # Go through the references and remove any that were deleted.  Ones that were added will have already been added to
+        # the table in AddReference().
+
+        my @references = $files{$watchedFileName}->References();
+        foreach my $reference (@references)
+            {
+            if (!$watchedFile->DefinesReference($reference))
+                {  $self->DeleteReference($reference, $watchedFileName);  };
+            };
+        };
+
+    # We have to check if the watched file exists again because DeleteReference() could have removed it.  I'm still not sure how a
+    # file could have references without symbols, but apparently it's happened in the real world because it's crashed on people.
+    if (exists $files{$watchedFileName})
+        {
+        # Go through the symbols.
+
+        my $rebuildFile;
+
+        my @symbols = $files{$watchedFileName}->Symbols();
+        foreach my $symbol (@symbols)
+            {
+            # Delete symbols that don't exist.
+
+            if (!$watchedFile->DefinesSymbol($symbol))
+                {
+                $self->DeleteSymbol($symbol, $watchedFileName);
+                $rebuildFile = 1;
+                }
+
+            else
+                {
+                my $symbolObject = $symbols{$symbol};
+                my $newSymbolDef = $watchedFileSymbolDefinitions{$symbol};
+
+                # Update symbols that changed.
+
+                if ( $symbolObject->TypeDefinedIn($watchedFileName) ne $newSymbolDef->Type() ||
+                     $symbolObject->PrototypeDefinedIn($watchedFileName) ne $newSymbolDef->Prototype() ||
+                     $symbolObject->SummaryDefinedIn($watchedFileName) ne $newSymbolDef->Summary() )
+                    {
+                    $self->OnIndexChange($symbolObject->TypeDefinedIn($watchedFileName));
+                    $self->OnIndexChange($newSymbolDef->Type());
+                    $rebuildFile = 1;
+
+                    $symbolObject->ChangeDefinition($watchedFileName, $newSymbolDef->Type(), $newSymbolDef->Prototype(),
+                                                                       $newSymbolDef->Summary());
+
+                    # If the symbol definition was the global one, we need to update all files that reference it.  If it wasn't, the only file
+                    # that could references it is itself, and the only way the symbol definition could change in the first place was if it was
+                    # itself changed.
+                    if ($symbolObject->GlobalDefinition() eq $watchedFileName)
+                        {
+                        # Rebuild the files that have references to this symbol
+                        my @references = $symbolObject->References();
+                        foreach my $reference (@references)
+                            {
+                            if ($references{$reference}->CurrentInterpretation() eq $symbol)
+                                {  $self->OnTargetSymbolChange($reference);  };
+                            }; # While references
+                        }; # If global definition is watched file
+                    }; # If the symbol definition changed
+                }; # If the symbol still exists
+            }; # foreach symbol in watched file
+
+        if ($rebuildFile)
+            {  NaturalDocs::Project->RebuildFile($watchedFileName);  };
+
+        };
+
+
+    $watchedFile = undef;
+    $watchedFileName = undef;
+    %watchedFileSymbolDefinitions = ( );
+    };
+
+
+#
+#   Function: DeleteReference
+#
+#   Deletes a reference from the table.
+#
+#   Be careful with this function, as deleting a reference means there are no more of them in the file at all.  The tables do not
+#   keep track of how many times references appear in a file.  In these cases you should instead call <WatchFileForChanges()>,
+#   reparse the file, thus readding all the references, and call <AnalyzeChanges()>.
+#
+#   <REFERENCE_TEXT> references should *always* be managed with <WatchFileForChanges()> and <AnalyzeChanges()>.
+#   This function should only be used externally for other types of references.
+#
+#   Parameters:
+#
+#       referenceString - The <ReferenceString>.
+#       file - The <FileName> where the reference is.  This is not required unless the type is <REFERENCE_TEXT>.
+#
+sub DeleteReference #(referenceString, file)
+    {
+    my ($self, $referenceString, $file) = @_;
+
+
+    # If the reference exists...
+    if (exists $references{$referenceString})
+        {
+        my $referenceObject = $references{$referenceString};
+
+        if (defined $file)
+            {  $referenceObject->DeleteDefinition($file);  };
+
+        # If there are no other definitions, or it doesn't use file definitions to begin with...
+        if (!$referenceObject->IsDefined())
+            {
+            my @interpretations = $referenceObject->Interpretations();
+            foreach my $interpretation (@interpretations)
+                {
+                $symbols{$interpretation}->DeleteReference($referenceString);
+                };
+
+            delete $references{$referenceString};
+            };
+
+
+        if (defined $file)
+            {
+            # Remove it from the file index.
+
+            $files{$file}->DeleteReference($referenceString);
+
+            if (!$files{$file}->HasAnything())
+                {  delete $files{$file};  };
+
+            # We don't need to worry about the watched file, since this function will only be called by AnalyzeChanges() and
+            # LoadAndPurge().
+            };
+        };
+    };
+
+
+#
+#   Function: RebuildAllIndexes
+#
+#   When called, it makes sure all indexes are listed as changed by <IndexChanged()>, regardless of whether they actually did
+#   or not.
+#
+#   This can be called at any time.
+#
+sub RebuildAllIndexes
+    {
+    my $self = shift;
+    $rebuildIndexes = 1;
+    };
+
+
+#
+#   Function: PurgeResolvingInfo
+#
+#   Purges unnecessary information from the symbol table after it is fully resolved.  This will reduce the memory footprint for the
+#   build stage.  After calling this function, you can only call the <Information Functions> and <Save()>.
+#
+sub PurgeResolvingInfo
+    {
+    my ($self) = @_;
+
+    # Go through the symbols.  We don't need to keep around potential symbols anymore, nor do we need what references can
+    # be interpreted as the defined ones.
+
+    while (my ($symbol, $symbolObject) = each %symbols)
+        {
+        if ($symbolObject->IsDefined())
+            {  $symbolObject->DeleteAllReferences();  }
+        else
+            {  delete $symbols{$symbol};  };
+        };
+
+
+    # Go through the references.  We don't need any of the interpretations except for the current.
+
+    foreach my $referenceObject (values %references)
+        {  $referenceObject->DeleteAllInterpretationsButCurrent();  };
+
+
+    # We don't need the information by file at all.
+
+    %files = ( );
+    };
+
+
+#
+#   Function: PurgeIndexes
+#
+#   Clears all generated indexes.
+#
+sub PurgeIndexes
+    {
+    my ($self) = @_;
+    %indexes = ( );
+    };
+
+
+###############################################################################
+# Group: Information Functions
+# These functions should not be called until the symbol table is fully resolved.
+
+
+#
+#   Function: References
+#
+#   Returns what the passed reference information resolve to, if anything.  Note that this only works if the reference had
+#   been previously added to the table via <AddReference()> with the exact same parameters.
+#
+#   Parameters:
+#
+#       type     - The <ReferenceType>.
+#       symbol - The reference <SymbolString>.
+#       scope   - The scope <SymbolString> the reference appears in, or undef if none.
+#       using    - An arrayref of scope <SymbolStrings> available to the reference via using statements.
+#       file       - The source <FileName> the reference appears in, or undef if none.
+#       resolvingFlags - The <Resolving Flags> of the reference.  Ignored if the type is <REFERENCE_TEXT>.
+#
+#   Alternate Parameters:
+#
+#       referenceString - The <ReferenceString> to resolve.
+#       file - The source <FileName> the reference appears in, or undef if none.
+#
+#   Returns:
+#
+#       A <NaturalDocs::SymbolTable::ReferenceTarget> object, or undef if the reference doesn't resolve to anything.
+#
+sub References #(type, symbol, scope, using, file, resolvingFlags) or (referenceString, file)
+    {
+    my ($self, $referenceString, $file);
+
+    if (scalar @_ <= 3)
+        {  ($self, $referenceString, $file) = @_;  }
+    else
+        {
+        my ($type, $symbol, $scope, $using, $resolvingFlags);
+        ($self, $type, $symbol, $scope, $using, $file, $resolvingFlags) = @_;
+
+        $referenceString = NaturalDocs::ReferenceString->MakeFrom($type, $symbol,
+                                                                                                  NaturalDocs::Languages->LanguageOf($file)->Name(),
+                                                                                                  $scope, $using, $resolvingFlags);
+        };
+
+    if (exists $references{$referenceString} && $references{$referenceString}->HasCurrentInterpretation())
+        {
+        my $targetSymbol = $references{$referenceString}->CurrentInterpretation();
+        my $targetObject = $symbols{$targetSymbol};
+
+        my $targetFile;
+        my $targetType;
+        my $targetPrototype;
+        my $targetSummary;
+
+        if (defined $file && $targetObject->IsDefinedIn($file))
+            {
+            $targetFile = $file;
+            $targetType = $targetObject->TypeDefinedIn($file);
+            $targetPrototype = $targetObject->PrototypeDefinedIn($file);
+            $targetSummary = $targetObject->SummaryDefinedIn($file);
+            }
+        else
+            {
+            $targetFile = $targetObject->GlobalDefinition();
+            $targetType = $targetObject->GlobalType();
+            $targetPrototype = $targetObject->GlobalPrototype();
+            $targetSummary = $targetObject->GlobalSummary();
+            };
+
+        return NaturalDocs::SymbolTable::ReferenceTarget->New($targetSymbol, $targetFile, $targetType, $targetPrototype,
+                                                                                             $targetSummary);
+        }
+
+    else
+        {  return undef;  };
+    };
+
+
+#
+#   Function: Lookup
+#
+#   Returns information on the passed <SymbolString>, if it exists.  Note that the symbol must be fully resolved.
+#
+#   Parameters:
+#
+#       symbol - The <SymbolString>.
+#       file - The source <FileName> the reference appears in, or undef if none.
+#
+#   Returns:
+#
+#       A <NaturalDocs::SymbolTable::ReferenceTarget> object, or undef if the symbol isn't defined.
+#
+sub Lookup #(symbol, file)
+    {
+    my ($self, $symbol, $file) = @_;
+
+    my $symbolObject = $symbols{$symbol};
+
+    if (defined $symbolObject)
+        {
+        my $targetFile;
+        my $targetType;
+        my $targetPrototype;
+        my $targetSummary;
+
+        if (defined $file && $symbolObject->IsDefinedIn($file))
+            {
+            $targetFile = $file;
+            $targetType = $symbolObject->TypeDefinedIn($file);
+            $targetPrototype = $symbolObject->PrototypeDefinedIn($file);
+            $targetSummary = $symbolObject->SummaryDefinedIn($file);
+            }
+        else
+            {
+            $targetFile = $symbolObject->GlobalDefinition();
+            $targetType = $symbolObject->GlobalType();
+            $targetPrototype = $symbolObject->GlobalPrototype();
+            $targetSummary = $symbolObject->GlobalSummary();
+            };
+
+        return NaturalDocs::SymbolTable::ReferenceTarget->New($symbol, $targetFile, $targetType, $targetPrototype,
+                                                                                             $targetSummary);
+        }
+
+    else
+        {  return undef;  };
+    };
+
+
+#
+#   Function: Index
+#
+#   Returns a symbol index.
+#
+#   Indexes are generated on demand, but they are stored so subsequent calls for the same index will be fast.  Call
+#   <PurgeIndexes()> to clear the generated indexes.
+#
+#   Parameters:
+#
+#       type  - The <TopicType> of symbol to limit the index to, or undef for none.
+#
+#   Returns:
+#
+#       An arrayref of sections.  The first represents all the symbols, the second the numbers, and the rest A through Z.
+#       Each section is a sorted arrayref of <NaturalDocs::SymbolTable::IndexElement> objects.  If a section has no content,
+#       it will be undef.
+#
+sub Index #(type)
+    {
+    my ($self, $type) = @_;
+
+    if (!exists $indexes{$type})
+        {  $indexes{$type} = $self->MakeIndex($type);  };
+
+    return $indexes{$type};
+    };
+
+
+#
+#   Function: HasIndexes
+#
+#   Determines which indexes out of a list actually have content.
+#
+#   Parameters:
+#
+#       types  - An existence hashref of the <TopicTypes> to check for indexes.
+#
+#   Returns:
+#
+#       An existence hashref of all the specified indexes that have content.  Will return an empty hashref if none.
+#
+sub HasIndexes #(types)
+    {
+    my ($self, $types) = @_;
+
+    # EliminationHash is a copy of all the types, and the types will be deleted as they are found.  This allows us to quit early if
+    # we've found all the types because the hash will be empty.  We'll later return the original hash minus what was left over
+    # in here, which are the ones that weren't found.
+    my %eliminationHash = %$types;
+
+    finddefs:
+    foreach my $symbolObject (values %symbols)
+        {
+        foreach my $definition ($symbolObject->Definitions())
+            {
+            delete $eliminationHash{ $symbolObject->TypeDefinedIn($definition) };
+            delete $eliminationHash{ ::TOPIC_GENERAL() };
+
+            if (!scalar keys %eliminationHash)
+                {  last finddefs;  };
+            };
+        };
+
+    my $result = { %$types };
+
+    foreach my $type (keys %eliminationHash)
+        {  delete $result->{$type};  };
+
+    return $result;
+    };
+
+
+#
+#   Function: IndexChanged
+#
+#   Returns whether the specified index has changed.
+#
+#   Parameters:
+#
+#       type  - The <TopicType> to limit the index to.
+#
+sub IndexChanged #(TopicType type)
+    {
+    my ($self, $type) = @_;
+    return ($rebuildIndexes || defined $indexChanges{$type});
+    };
+
+
+#
+#   Function: IndexSectionsWithContent
+#
+#   Returns an arrayref of whether each section of the specified index has content.  The first entry will be for symbols, the second
+#   for numbers, and the rest A-Z.  Do not change the arrayref.
+#
+sub IndexSectionsWithContent #(TopicType type)
+    {
+    my ($self, $type) = @_;
+
+    if (!exists $indexSectionsWithContent{$type})
+        {
+        # This is okay because Index() stores generated indexes.  It's not an expensive operation unless the index was never asked
+        # for before or it will never be asked for otherwise, and this shouldn't be the case.
+
+        my $index = $self->Index($type);
+        my $content = [ ];
+
+        for (my $i = 0; $i < 28; $i++)
+            {
+            push @$content, (defined $index->[$i] ? 1 : 0);
+            };
+
+        $indexSectionsWithContent{$type} = $content;
+        };
+
+    return $indexSectionsWithContent{$type};
+    };
+
+
+
+###############################################################################
+# Group: Event Handlers
+
+
+#
+#   Function: OnIndexChange
+#
+#   Called whenever a change happens to a symbol that would cause an index to be regenerated.
+#
+#   Parameters:
+#
+#       type - The <TopicType> of the symbol that caused the change.
+#
+sub OnIndexChange #(TopicType type)
+    {
+    my ($self, $type) = @_;
+
+    $indexChanges{$type} = 1;
+    $indexChanges{::TOPIC_GENERAL()} = 1;
+    delete $indexSectionsWithContent{$type};
+    };
+
+
+#
+#   Function: OnInterpretationChange
+#
+#   Called whenever the current interpretation of a reference changes, meaning it switched from one symbol to another.
+#
+#   Parameters:
+#
+#       referenceString - The <ReferenceString> whose current interpretation changed.
+#
+sub OnInterpretationChange #(referenceString)
+    {
+    my ($self, $referenceString) = @_;
+    my $referenceType = NaturalDocs::ReferenceString->TypeOf($referenceString);
+
+    if ($referenceType == ::REFERENCE_TEXT())
+        {
+        my @referenceDefinitions = $references{$referenceString}->Definitions();
+
+        foreach my $referenceDefinition (@referenceDefinitions)
+            {
+            NaturalDocs::Project->RebuildFile($referenceDefinition);
+            };
+        }
+
+    elsif (NaturalDocs::Constants->IsClassHierarchyReference($referenceType))
+        {
+        NaturalDocs::ClassHierarchy->OnInterpretationChange($referenceString);
+        };
+    };
+
+
+#
+#   Function: OnTargetSymbolChange
+#
+#   Called whenever the symbol that serves as the interpretation of a reference changes, but the reference still resolves to
+#   the same symbol.  This would happen if the type, prototype, summary, or which file serves as global definition of the symbol
+#   changes.
+#
+#   Parameters:
+#
+#       referenceString - The <ReferenceString> whose interpretation's symbol changed.
+#
+sub OnTargetSymbolChange #(referenceString)
+    {
+    my ($self, $referenceString) = @_;
+    my $referenceType = NaturalDocs::ReferenceString->TypeOf($referenceString);
+
+    if ($referenceType == ::REFERENCE_TEXT())
+        {
+        my @referenceDefinitions = $references{$referenceString}->Definitions();
+
+        foreach my $referenceDefinition (@referenceDefinitions)
+            {
+            NaturalDocs::Project->RebuildFile($referenceDefinition);
+            };
+        }
+
+    elsif (NaturalDocs::Constants->IsClassHierarchyReference($referenceType))
+        {
+        NaturalDocs::ClassHierarchy->OnTargetSymbolChange($referenceString);
+        };
+    };
+
+
+
+###############################################################################
+# Group: Support Functions
+
+
+#
+#   Function: DeleteSymbol
+#
+#   Removes a symbol definition from the table.  It will call <OnInterpretationChange()> for all references that have it as their
+#   current interpretation.
+#
+#   External code should not attempt to delete symbols using this function.  Instead it should call <WatchFileFoChanges()>,
+#   reparse the file, and call <AnalyzeChanges()>.
+#
+#   Parameters:
+#
+#       symbol - The <SymbolString>.
+#       file       - The <FileName> where the definition is.
+#
+sub DeleteSymbol #(symbol, file)
+    {
+    my ($self, $symbol, $file) = @_;
+
+
+    # If the symbol and definition exist...
+    if (exists $symbols{$symbol} && $symbols{$symbol}->IsDefinedIn($file))
+        {
+        my $symbolObject = $symbols{$symbol};
+        my $wasGlobal = ($symbolObject->GlobalDefinition() eq $file);
+
+        $self->OnIndexChange($symbolObject->TypeDefinedIn($file));
+
+        $symbolObject->DeleteDefinition($file);
+
+        # If this was one definition of many...
+        if ($symbolObject->IsDefined())
+            {
+
+            # If this was the global definition...
+            if ($wasGlobal)
+                {
+                # Update every file that referenced the global symbol; i.e. every file that doesn't have its own definition.
+
+                my @references = $symbolObject->References();
+
+                foreach my $reference (@references)
+                    {
+                    if ($references{$reference}->CurrentInterpretation() eq $symbol)
+                        {
+                        $self->OnTargetSymbolChange($reference);
+                        };
+                    };
+                }
+
+            # If this wasn't the global definition...
+            else
+                {
+                # It's a safe bet that we don't need to do anything here.  The only thing that we even need to look for here is if the
+                # file referenced its own symbol and thus should be rebuilt.  However, if the file is having a symbol deleted, it either
+                # changed or was itself deleted.  If it changed and still has other Natural Docs content, it should already be on the
+                # rebuild list.  If it was deleted or no longer has Natural Docs content, we certainly don't want to add it to the rebuild
+                # list.
+                };
+            }
+
+        # If this is the only definition...
+        else
+            {
+            # If this symbol is the interpretation of any references...
+            if ($symbolObject->HasReferences())
+                {
+                # If this was the current interpretation of any references, reinterpret them and rebuild their files.
+
+                my @references = $symbolObject->References();
+
+                foreach my $reference (@references)
+                    {
+                    if ($references{$reference}->CurrentInterpretation() eq $symbol)
+                        {
+                        $self->InterpretReference($reference);
+                        $self->OnInterpretationChange($reference);
+                        };
+                    };
+                }
+
+            # If there are no interpretations of the symbol...
+            else
+                {
+                # Delete the symbol entirely.
+                delete $symbols{$symbol};
+                };
+            };
+
+        # Remove it from the file index.
+
+        $files{$file}->DeleteSymbol($symbol);
+
+        if (!$files{$file}->HasAnything())
+            {  delete $files{$file};  };
+
+
+        # We don't need to worry about the watched file, since this function will only be called by AnalyzeChanges() and
+        # LoadAndPurge().
+        };
+    };
+
+
+#
+#   Function: GenerateInterpretations
+#
+#   Generates the list of interpretations for the passed reference.  Also creates potential symbols as necessary.
+#
+#   Parameters:
+#
+#       referenceString - The <ReferenceString> to generate the interpretations of.
+#
+sub GenerateInterpretations #(referenceString)
+    {
+    my ($self, $referenceString) = @_;
+
+    my ($type, $symbol, $languageName, $scope, $using, $resolvingFlags) =
+        NaturalDocs::ReferenceString->InformationOf($referenceString);
+
+    # RESOLVE_NOPLURAL is handled by having @singulars be empty.
+    my @singulars;
+    if (!($resolvingFlags & ::RESOLVE_NOPLURAL()))
+        {  @singulars = $self->SingularInterpretationsOf($symbol);  };
+
+    # Since higher scores are better, we'll start at a high number and decrement.
+    my $score = 50000;
+
+
+    # If RESOLVE_RELATIVE is set, we do all the scope relatives before the global.
+    if ($resolvingFlags & ::RESOLVE_RELATIVE())
+        {
+        $score = $self->GenerateRelativeInterpretations($referenceString, $symbol, \@singulars, $scope, $score);
+        }
+
+    # If neither RESOLVE_RELATIVE nor RESOLVE_ABSOLUTE is set, we only do the local before the global.
+    elsif (!($resolvingFlags & ::RESOLVE_ABSOLUTE()))
+        {
+        $self->AddInterpretation($referenceString, NaturalDocs::SymbolString->Join($scope, $symbol), $score);
+        $score--;
+
+        foreach my $singular (@singulars)
+            {
+            $self->AddInterpretation($referenceString, NaturalDocs::SymbolString->Join($scope, $singular), $score);
+            $score--;
+            };
+        };
+
+
+    # Do the global.
+
+    $self->AddInterpretation($referenceString, $symbol, $score);
+    $score--;
+
+    foreach my $singular (@singulars)
+        {
+        $self->AddInterpretation($referenceString, $singular, $score);
+        $score--;
+        };
+
+
+    # If neither RESOLVE_RELATIVE nor RESOLVE_ABSOLUTE is set, we need to do the rest of the scope relatives after the global.
+    if (!($resolvingFlags & ::RESOLVE_RELATIVE()) && !($resolvingFlags & ::RESOLVE_ABSOLUTE()))
+        {
+        $score = $self->GenerateRelativeInterpretations($referenceString, $symbol, \@singulars, $scope, $score, 1);
+        };
+
+
+    # Finally, if RESOLVE_NOUSING isn't set, go through the using scopes.
+    if (!($resolvingFlags & ::RESOLVE_NOUSING()) && defined $using)
+        {
+        foreach my $usingScope (@$using)
+            {
+            if ($resolvingFlags & ::RESOLVE_ABSOLUTE())
+                {
+                $self->AddInterpretation($referenceString, NaturalDocs::SymbolString->Join($usingScope, $symbol), $score);
+                $score--;
+
+                foreach my $singular (@singulars)
+                    {
+                    $self->AddInterpretation($referenceString, NaturalDocs::SymbolString->Join($usingScope, $singular), $score);
+                    $score--;
+                    };
+                }
+            else
+                {
+                $score = $self->GenerateRelativeInterpretations($referenceString, $symbol, \@singulars, $usingScope, $score);
+                };
+            };
+        };
+    };
+
+
+#
+#   Function: GenerateRelativeInterpretations
+#
+#   Generates the list of relative interpretations for the passed reference and packages.  Also creates potential symbols as
+#   necessary.
+#
+#   This function will _not_ create global interpretations.  It _will_ create a local interpretations (symbol + all packages) unless
+#   you set dontUseFull.
+#
+#   Parameters:
+#
+#       referenceString - The <ReferenceString> to generate interpretations for.
+#       symbol - The <SymbolString> to generate interpretations of.
+#       singulars - A reference to an array of singular <SymbolStrings> to also generate interpretations of.  Set to an empty array
+#                       if none.
+#       package - The package <SymbolString> to use.  May be undef.
+#       score - The starting score to apply.
+#       dontUseFull - Whether to not generate an interpretation including the full package identifier.  If set, generated interpretations
+#                           will start one level down.
+#
+#   Returns:
+#
+#       The next unused score.  This is basically the passed score minus the number of interpretations created.
+#
+sub GenerateRelativeInterpretations #(referenceString, symbol, singulars, package, score, dontUseFull)
+    {
+    my ($self, $referenceString, $symbol, $singulars, $package, $score, $dontUseFull) = @_;
+
+    my @packages = NaturalDocs::SymbolString->IdentifiersOf($package);
+
+    # The last package index to include.  This number is INCLUSIVE!
+    my $packageLevel = scalar @packages - 1;
+
+    if ($dontUseFull)
+        {  $packageLevel--;  };
+
+    while ($packageLevel >= 0)
+        {
+        $self->AddInterpretation($referenceString, NaturalDocs::SymbolString->Join(@packages[0..$packageLevel], $symbol),
+                                             $score);
+        $score--;
+
+        foreach my $singular (@$singulars)
+            {
+            $self->AddInterpretation($referenceString, NaturalDocs::SymbolString->Join(@packages[0..$packageLevel], $singular),
+                                                 $score);
+            $score--;
+            };
+
+        $packageLevel--;
+        };
+
+    return $score;
+    };
+
+
+#
+#   Function: SingularInterpretationsOf
+#
+#   Generates singular interpretations of a <SymbolString> if it can be interpreted as a plural.  Not all of them will be valid singular
+#   forms, but that doesn't matter since it's incredibly unlikely an invalid form would exist as a symbol.  What matters is that the
+#   legimate singular is present on the list.
+#
+#   Parameters:
+#
+#       symbol - The <SymbolString>.
+#
+#   Returns:
+#
+#       An array of potential singular interpretations as <SymbolStrings>, in no particular order.  If the symbol can't be interpreted
+#       as a plural, returns an empty array.
+#
+sub SingularInterpretationsOf #(symbol)
+    {
+    my ($self, $symbol) = @_;
+
+    my @identifiers = NaturalDocs::SymbolString->IdentifiersOf($symbol);
+    my $lastIdentifier = pop @identifiers;
+    my $preIdentifiers = NaturalDocs::SymbolString->Join(@identifiers);
+
+    my @results;
+
+    # First cut off any 's or ' at the end, since they can appear after other plural forms.
+    if ($lastIdentifier =~ s/\'s?$//i)
+        {
+        push @results, NaturalDocs::SymbolString->Join($preIdentifiers, $lastIdentifier);
+        };
+
+    # See http://www.gsu.edu/~wwwesl/egw/crump.htm for a good list of potential plural forms.  There are a couple more than
+    # listed below, but they're fairly rare and this is already seriously over-engineered.  This is split by suffix length to make
+    # comparisons more efficient.
+
+    # The fact that this will generate some impossible combinations (leaves => leave, leav, leaf, leafe) doesn't matter.  It's very
+    # unlikely that more than one will manage to match a defined symbol.  Even if they do (leave, leaf), it's incredibly unlikely
+    # that someone has defined an impossible one (leav, leafe).  So it's not so important that we remove impossible combinations,
+    # just that we include all the possible ones.
+
+    my @suffixGroups = ( [ 's', undef,  # boys => boy
+                                       'i', 'us',  # alumni => alumnus
+                                       'a', 'um', # errata => erratum
+                                       'a', 'on' ],  # phenomena => phenomenon
+
+                                    [ 'es', undef,  # foxes => fox
+                                      'ae', 'a' ],  # amoebae => amoeba
+
+                                    [ 'ies', 'y',  # pennies => penny
+                                      'ves', 'f',  # calves => calf
+                                      'ves', 'fe',  # knives => knife
+                                      'men', 'man',  # women => woman
+                                      'ice', 'ouse',  # mice => mouse
+                                      'oes', 'o',  # vetoes => veto
+                                      'ces', 'x',  # matrices => matrix
+                                      'xen', 'x' ],  # oxen => ox
+
+                                    [ 'ices', 'ex',  # indices => index
+                                      'feet', 'foot',  # feet => foot
+                                      'eese', 'oose',  # geese => goose
+                                      'eeth', 'ooth',  # teeth => tooth
+                                      'dren', 'd' ] );  # children => child
+
+    my $suffixLength = 1;
+
+    foreach my $suffixGroup (@suffixGroups)
+        {
+        my $identifierSuffix = lc( substr($lastIdentifier, 0 - $suffixLength) );
+        my $cutIdentifier = substr($lastIdentifier, 0, 0 - $suffixLength);
+
+        for (my $i = 0; $i + 1 < scalar @$suffixGroup; $i += 2)
+            {
+            my $suffix = $suffixGroup->[$i];
+            my $replacement = $suffixGroup->[$i + 1];
+
+            if ($identifierSuffix eq $suffix)
+                {
+                if (defined $replacement)
+                    {
+                    push @results, NaturalDocs::SymbolString->Join($preIdentifiers, $cutIdentifier . $replacement);
+                    push @results, NaturalDocs::SymbolString->Join($preIdentifiers, $cutIdentifier . uc($replacement));
+                    }
+                else
+                    {
+                    push @results, NaturalDocs::SymbolString->Join($preIdentifiers, $cutIdentifier);
+                    };
+                };
+            };
+
+        $suffixLength++;
+        };
+
+    return @results;
+    };
+
+
+#
+#   Function: AddInterpretation
+#
+#   Adds an interpretation to an existing reference.  Creates potential symbols as necessary.
+#
+#   Parameters:
+#
+#       referenceString - The <ReferenceString> to add the interpretation to.
+#       symbol - The <SymbolString> the reference can be interpreted as.
+#       score - The score of the interpretation.
+#
+sub AddInterpretation #(referenceString, symbol, score)
+    {
+    my ($self, $referenceString, $symbol, $score) = @_;
+
+    $references{$referenceString}->AddInterpretation($symbol, $score);
+
+    # Create a potential symbol if it doesn't exist.
+
+    if (!exists $symbols{$symbol})
+        {  $symbols{$symbol} = NaturalDocs::SymbolTable::Symbol->New();  };
+
+    $symbols{$symbol}->AddReference($referenceString, $score);
+    };
+
+
+#
+#   Function: InterpretReference
+#
+#   Interprets the passed reference, matching it to the defined symbol with the highest score.  If the symbol is already
+#   interpreted, it will reinterpret it.  If there are no matches, it will make it an undefined reference.
+#
+#   Parameters:
+#
+#       referenceString - The <ReferenceString> to interpret.
+#
+sub InterpretReference #(referenceString)
+    {
+    my ($self, $referenceString) = @_;
+
+    my $interpretation;
+    my $currentInterpretation;
+    my $score;
+    my $currentScore = -1;
+
+    my $referenceObject = $references{$referenceString};
+
+    my %interpretationsAndScores = $referenceObject->InterpretationsAndScores();
+    while ( ($interpretation, $score) = each %interpretationsAndScores )
+        {
+        if ($score > $currentScore && $symbols{$interpretation}->IsDefined())
+            {
+            $currentScore = $score;
+            $currentInterpretation = $interpretation;
+            };
+        };
+
+    if ($currentScore > -1)
+        {  $referenceObject->SetCurrentInterpretation($currentInterpretation);  }
+    else
+        {  $referenceObject->SetCurrentInterpretation(undef);  };
+    };
+
+
+#
+#   Function: MakeIndex
+#
+#   Generates a symbol index.
+#
+#   Parameters:
+#
+#       type  - The <TopicType> to limit the index to.
+#
+#   Returns:
+#
+#       An arrayref of sections.  The first represents all the symbols, the second the numbers, and the rest A through Z.
+#       Each section is a sorted arrayref of <NaturalDocs::SymbolTable::IndexElement> objects.  If a section has no content,
+#       it will be undef.
+#
+sub MakeIndex #(type)
+    {
+    my ($self, $type) = @_;
+
+
+    # Go through the symbols and generate IndexElements for any that belong in the index.
+
+    # Keys are the symbol strings, values are IndexElements.
+    my %indexSymbols;
+
+    while (my ($symbolString, $object) = each %symbols)
+        {
+        my ($symbol, $package) = $self->SplitSymbolForIndex($symbolString, $object->GlobalType());
+        my @definitions = $object->Definitions();
+
+        foreach my $definition (@definitions)
+            {
+            my $definitionType = $object->TypeDefinedIn($definition);
+
+            if ($type eq ::TOPIC_GENERAL() || $type eq $definitionType)
+                {
+                if (!exists $indexSymbols{$symbol})
+                    {
+                    $indexSymbols{$symbol} =
+                        NaturalDocs::SymbolTable::IndexElement->New($symbol, $package, $definition, $definitionType,
+                                                                                               $object->PrototypeDefinedIn($definition),
+                                                                                               $object->SummaryDefinedIn($definition) );
+                    }
+                else
+                    {
+                    $indexSymbols{$symbol}->Merge($package, $definition, $definitionType,
+                                                                       $object->PrototypeDefinedIn($definition),
+                                                                       $object->SummaryDefinedIn($definition) );
+                    };
+                }; # If type matches
+            }; # Each definition
+        }; # Each symbol
+
+
+    # Generate sortable symbols for each IndexElement, sort them internally, and divide them into sections.
+
+    my $sections = [ ];
+
+    foreach my $indexElement (values %indexSymbols)
+        {
+        $indexElement->Sort();
+        $indexElement->MakeSortableSymbol();
+
+        my $sectionNumber;
+
+        if ($indexElement->SortableSymbol() =~ /^([a-z])/i)
+            {  $sectionNumber = ord(lc($1)) - ord('a') + 2;  }
+        elsif ($indexElement->SortableSymbol() =~ /^[0-9]/)
+            {  $sectionNumber = 1;  }
+        else
+            {  $sectionNumber = 0;  };
+
+        if (!defined $sections->[$sectionNumber])
+            {  $sections->[$sectionNumber] = [ ];  };
+
+        push @{$sections->[$sectionNumber]}, $indexElement;
+        };
+
+
+    # Sort each section.
+
+    for (my $i = 0; $i < scalar @$sections; $i++)
+        {
+        if (defined $sections->[$i])
+            {
+            @{$sections->[$i]} = sort
+                {
+                my $result = ::StringCompare($a->SortableSymbol(), $b->SortableSymbol());
+
+                if ($result == 0)
+                    {  $result = ::StringCompare($a->IgnoredPrefix(), $b->IgnoredPrefix());  };
+
+                return $result;
+                }
+            @{$sections->[$i]};
+            };
+        };
+
+    return $sections;
+    };
+
+
+#
+#   Function: SplitSymbolForIndex
+#
+#   Splits a <SymbolString> into its symbol and package portions for indexing.
+#
+#   Parameters:
+#
+#       symbol - The <SymbolString>.
+#       type - Its <TopicType>.
+#
+#   Returns:
+#
+#       The array ( symbol, package ), which are both <SymbolStrings>.  If the symbol is global, package will be undef.
+#
+sub SplitSymbolForIndex #(symbol, type)
+    {
+    my ($self, $symbol, $type) = @_;
+
+    my $scope = NaturalDocs::Topics->TypeInfo($type)->Scope();
+
+    if ($scope == ::SCOPE_START() || $scope == ::SCOPE_ALWAYS_GLOBAL())
+        {  return ( $symbol, undef );  }
+    else
+        {
+        my @identifiers = NaturalDocs::SymbolString->IdentifiersOf($symbol);
+
+        $symbol = pop @identifiers;
+        my $package = NaturalDocs::SymbolString->Join(@identifiers);
+
+        return ( $symbol, $package );
+        };
+    };
+
+
+1;