Changes made. The latest commit adds a lot of complexity, but nice type inference. I also realized when doing some of this that it's not super clear when a return value is necessary, and when modifying a value by reference works. For example, the callback to tree.match must return a Node (or RawNode), but the plugin itself doesn't have to return the tree. It's unclear what would happen if a plugin both modified the tree by reference and returned a new one.

Originally posted by @caseyWebb in #291 (comment)