Linked List of Symbol Tables
Consider this Scheme code:
(let ([x 5] [y 10]) (+ x y (let ([y 8]) (+ x y)) y) )
In this code, x
is always bound to 5 since it is
declared just once. y
is shadowed; two of its
references are when it's bound to 10, once while bound to 8.
To handle these different scopes, CITkit provides a linked list of symbol tables which I call "environments". Start with a "null environment" which contains no bindings at all. This is to trigger "identifier not found errors". From this null environment, you can add on useful environments for real bindings.
In the example above, the Scheme interpreter would probably
start with a null environment and an environment beyond that for
the global scope so that +
can be bound to the
addition primitive:
( ([+ #<addition primitive>]) )
The first let
extends the environment with another
symbol table:
( ([x 5] [y 10]) ([+ #<addition primitive>]) )
The second let
extends it another time:
( ([y 8]) ([x 5] [y 10]) ([+ #<addition primitive>]) )
When y
is requested, this first symbol table is
triggered to get the value 8. When x
is requested and
not found in the first symbol table, the second symbol table is
triggered, and 5 is returned.
When that inner let
is done executing, the top
environment is popped off, and the old value of y
is
restored!
Built in Factory for Testing
The IEnvironment<T>
interface requires all
environment implementers to know how to create a new symbol table
for the top of the environment list with a create()
method. This is primarily for easy testing since it's easier to
mock a method call than a call to a constructor.