feat: add pair-observing helper

@b-laporte here is the first shot of the observable-pairs detecting algorithm. There are still some TODOs as I've got a couple of questions but the main idea should be here. For now the whole algorithm is < 50 LOC and I don't see much reason for it getting any bigger.

It would be great of you could have a look at the associated unit-tests and let me know if I got it more-or-less right. I've got also number of questions:

### Undefined values in property access paths

Given an expression like `foo.bar.baz` it might happen that any of `foo`, `foo.bar` is null / undefined / not an object. How do you deal with those? Assuming that `foo` is null would you return `[[scope, 'foo']]` or `[[scope, 'foo'], [undefined, 'bar']]`? I guess the first one but just to be sure....

### Observable paths and function calls

Given an expression like `foo.bar() + foo.baz` we need to observe the whole `foo` object for function calls and its `baz` property. The question is - are you doing any optimizations while building observable pairs? The ideal return value should be `[[scope, 'foo'], [scope.foo, null]]` but I guess it is easier to return  [[scope, 'foo'], [scope.foo, null], [scope.foo, 'baz']]` and deal with duplicates when installing observers. WDYT?