Build a parse tree before compiling

Adds a discrete parse step to the compilation process, allowing us to deal with a parse tree rather than a stream of tokens when compiling the code.

This makes it a lot simpler to implement new block types without resorting to nasty hacks and heuristics.