Resolvers

**not ready for merge, work in progress**

This is a spin-off from #1374. I found it rather cumbersome to deal with the various different types of resolver while creating explicit component mappings. There are a few places in the codebase where we have a lot of if...else stuff going on around whether a reference is an index reference or a regular reference, etc.

Now that we've got special references (`@index`, `@key` and `@keypath`) involved as well, it all adds up to quite a bit of code you need to write in order to resolve a reference. So I'm trying to encapsulate all the reference resolution logic in one place.

Fundamentally, index refs and special refs are a different beast to regular references - they don't resolve to keypaths, they resolve to *values*, and that's why they need different treatment. The big idea in this pull request is to treat them all as keypaths, but use the keypaths that index/special refs resolve to *encode* the value. So `{{@index}}` or `{{i}}` might resolve to `@0` or `@42`, while `{{@keypath}}` might resolve to `@foo.bar.baz`. This minimises the number of places we need to worry about treating different references differently - `viewmodel.get('@42')` returns `42`, and `viewmodel.get('@foo.bar.baz')` returns `"foo.bar.baz"`.

This probably all sounds a bit silly, but I have a good feeling about it - it's meant removing a fair bit of complex code already, and there's a few more places where the same should hopefully be true.