Each entry in the AbTab symbol table is an AbSym instance. We use the term binding for the connection between a code and its
definition.
When a symbol table entry is initially created, it is said to be unbound. That is, we know the bird code but not what it stands for.
If we encounter a definition for an unbound symbol, we set its
.binding attribute to an instance of one of the
subclasses of the AbBind class that represents
its definition.
If the code is related to a scientific name, we use a StdBind instance. See Section 27, “class StdBind: Code bound to a
taxon”.
If the code is a direct equivalent of another code, we use
an EqBind instance. See Section 28, “class EqBind: Code equivalent to another
code”.
If the code is a collision form, it is bound to
the code as a CollBind instance;
see Section 29, “class CollBind: Cluster of colliding
codes”.
So, what happens if we encounter a new definition of an existing code? For the first two cases above, it is an error: one cannot relate a code to two different taxa or relate it to two direct equivalents.
However, a CollBind instance is a container for
all the different related disambiguated codes.
These three cases are handled by the .combine()
method of the concrete subclass of AbBind.
This method will fail if the new binding is incompatible
with the old binding. In the case where a new collision
is added to an existing CollBind instance,
however, the .combine() method will succeed.
Here is the class interface:
# - - - - - c l a s s A b S y m
class AbSym(object):
'''Represents one entry in the code symbol table.
Exports:
AbSym ( abbr ):
[ abbr is a stripped, uppercased bird code ->
return a new AbSym instance for abbr with no
binding ]
.abbr: [ as passed to constructor, read-only ]
.binding:
[ if self has no binding -> None
else ->
self's current binding as an AbBind instance ]
.bind ( abBind ):
[ abBind is an instance of AbBind ->
if self's binding can be combined with abBind ->
self := self with abBind added to its binding
else -> raise ValueError ]
'''