WIP/RFC: Refactored dict.jl


This is a refactor of dict.jl which also takes into account
DataStructures.jl/OrderedDict.  The reason I did this is because I
needed a WeakObjectIdDict for PR #5572 (which I did there with an ugly
copy-paste).  PR #5572 is for issue #3988 which should lay the
foundation for user-defined help.

Dictionaries have quite extensive internal mechanics and small changes
in those internals make new types of dicts. For instance, whether to
take a straight hash or use an object-id in `hashindex` makes a `Dict`
vs `ObjectIdDict`.  This means that when making new dictionaries by
simply wrapping the standard `Dict` and defining new `setindex!`,
`getindex`, etc. one has to replicate lots of those inner mechanics
because dispatch cannot do its magic on inner functions.  This PR is a
go at writing `dict.jl` in a more composable manner.

This refactor can accommodate quite a few different hash-table based
dicts.  Implemented are `Dict`, `ObjectIdDict2` (a new variation),
`WeakKeyDict` (now works for immutables too), `WeakObjectIdDict` (new),
and `OrderedDict` (essentially as in `DataStructures.jl` package).

Performance is as before except for `ObjectIdDict2` which is 25% slower
than `ObjectIdDict` (but has the complete interface).  This is because
`ObjectIdDict` is implemented differently to `Dict` whereas `ObjectIdDict2`
is within the same framework.

I'm not sure the style is in line with standard practices.  The
concrete subtypes and constructors of `HashDictionary` are constructed
with a macro.  This is reminiscent of the approach of @kmsquire in
PR #2548 which was not liked by @JeffBezanson...

If this is not the way to cleanly implement a WeakObjectIdDict (and it
turns out its needed to solve #3988), then any comments on how to
implement it instead are welcome!

What I did:
- Introduced a new abstract type HashDictionary which is the
  supertype of all implemented dicts at the moment and ought to be
  the supertype of all hash-table based dicts.
- I moved some of the `Dict` specific methods to `Associative`.
- I introduced key and value getters and setters (`gkey`, `skey!`,
  ...).  These allow, for instance, keys to be transformed to
  `WeakRef`'s and back.
- `hashindex` now is dispatched on the type so, for instance,
  ObjectIdDict can change it to use `object_id` instead.
- I added a purging of `WeakRef`, even for immutables, if their object
  get gc-ed with the function `topurge`.
- the concrete types are then constructed with `@makeHashDictionary`
  and some of above internals are specialized.  For instance to make
  an object-id dict all it takes is:
```julia
  @makeHashDictionary(ObjectIdDict2, K, K, V, V, Unordered)
  hashindex(::ObjectIdDict2, key, sz) = (int(object_id(key)) & (sz-1)) + 1
```

Note 95% of this is a refactor of good code others wrote, thanks to
those others!

TODO:
- check code-coverage and add tests
- think about constructors
- see whether ObjectIdDict2 can be made compatible with ObjectIdDict

TODO irrespective of this PR:
- currently there is an inconsistency between key conversion in `get`
  and `getindex`.  (some do an explicit conversion others don't).
- at the moment the top-level abstract type is called `Associative`.
  Maybe `Dictionary` would be more consistent? 
- equality: == when keys and values are equal or also when type of
  dict is equal?  Except of ObjectIdDict, what are the exceptions
  then?
- could the standard `Dict` be made as performant as `ObjectIdDict`,
  i.e. 25% faster?

Consider just using `EMPTY`, `FILLED`, `MISSING`.  Most other uses of `is*` in Julia are predicates.

(To prevent leakage of common names, you could put this file in its own module.)