compiler: Rework expression tree bindings

Split et.Bind in to et.TopLevelDefinition and et.InternalDefinition.
These have the exact same definitions as R7RS  - top level definitions
assign storage locations to a value for the rest of the program's
execution while internal definitions are only bound for the definition's
body.

While this has the side effect of making our expression tree more
closely match Scheme it has some very practical benefits for the
reducer:

- Previously when resolving a storage location to an initializer it
  wasn't known which values were live for that initializer. For example,
  any lambda arguments would be completely unknown. Now only top level
  definitions (which shouldn't have unknown values) are handled this
  way. Internal definitions are treated as if they were simple
  self-executing lambdas which will inject the correct known values in
  while reducing body. This increases the number of values that are
  known to the reducer without resorting to more complicated methods
  like abstract interpreters

- Previously we couldn't pre-reduce initializers because we didn't know
  if they're were going to be re-evaluated later in a context where more
  known values were used. This means every value was re-reduced when
  calling e.g. PartialValueForExpression. Now top-level definitions are
  reduced once during setup by ReduceExpressions and internal
  definitions are resolved before reducing their body. This should
  greatly reduce the amount of duplicated work the reducer has to
  perform.

- Lambdas previously had all of their argument references renamed while
  they were being inlined to ensure they didn't stomp on any the values
  of any outer inlined version. Now that the storage location bindings
  are scoped with InternalDefinition and the planner will ensure they're
  restored we can avoid renaming completely.

- Due to the renaming performed it wasn't safe for the reducer to apply
  an already reduced expression. This is because the new renamed values
  wouldn't show up in AnalysisResult.usedVars and would be aggressively
  removed on the second reduction. Because knownValues were always
  pre-reduced this means it was impossible to apply a known value - e.g.
  inlining a procedure passed to another procedure. Now known values can
  be freely applied

The code changes to the frontend and planner are minimal. Most of the
code changes are in the reducer where it has generally simplified logic.
One exception is the addition of code to partially evaluate an
InternalDefinition but this represents a useful reduction that was
impossible before.