Improvements
Some improvements to the implementation! :)
Wow, this is a lot of stuff. I'll probably defer pulling in the changes to the parser and perhaps the REPL because my goal for the first version is to keep the code as small as possible and roughly mirror Norvig's original Python implementation (as is reasonably possible). I'm intending to write a blog post which parallels the original as a sort of 'how to do this in Rust' one, then perhaps another one expanding on it with more optimisation (eg. the parser) and niceties (eg. error backtraces, improvements to the REPL)
Hah, sorry, got carried away. It was fun though! I've spent too much time in library code recently - nice to do some application stuff. Understandable that you want to keep it pretty similar. Did you see my old lispy thing from 3 years ago? It's a fun time capsule. I borrowed some of the parser stuff from it.
Feel free to take ideas from this, and iterate on it yourself. I'm perfectly cool with you breaking the merge irrevocably.
Some things like the stringly typed bools might be something to watch for. It's idiomatic in python, but not so much in Rust. Try to use the type system to show your intent, and don't allocate unless really necessary.
Also, try to use Results for errors that a user might encounter, and reserve panic!s for internal logic errors. That way you can provide better error reporting. Note that some functions will panic!, for example Vec::remove but may have variants that return Options.
Also note, that if you are storing a reference to an Rc, you should demote it to a Weak pointer to prevent cycles from occurring.