Make A Lisp (step 3)
Continued from step 2. This step gives the environment a little more functionality and introduces two special forms for manipulation of the environment.
The environment from step 2 was a simple map from keyword to Mal-type, pre-hydrated with functions for integer arithmetic. I implemented it as a php array. In step 3 this in built out to a dedicated class with get and set methods. Also, the environments can now have an "outer" environment. If a symbol is not found (using the "get" method) in the current environment, the search continues in the outer environment until there are no outer environment.
The guide recommends implementing this search as a recursive call to to the outer environment's "get", but I did it with a very simple while-loop instead.
The evaluator now also has to handle the special forms "def!" and "let*".
def! associates a symbol with a value in the current environment (the ! at the end of the symbol name signifies that this form mutates the environment).
let* is a little more involved: It creates a whole new environment with the current as its "outer". It also sets any number of symbols in this new environment, and finally evaluates it's last argument in the context of the new environment. The evaluated result is returned and the new environment is discarded.
The only ambiguity in this step what to do when setting a value. The evaluator demands that something is returned and the guide does not explicitly states what to return. It's clear from the repl examples presented though that the value that is set should also be returned.
This step required pretty minor changes to the source, and once I could get the repl running again, I tried the tests. All passed right away, witch is a first for me with this project.