Missing/misspelled variable name recommendations

This is the equivalent of PR #1776 but for variables instead of keywords. 

I've moved some of the reusable recommendation code from `namespace.py` to `utils/match.py` in a new `FuzzyMatcher` class. Trying to balance code duplication and complexity in this refactoring was tricky, so criticism/ideas are welcome.

There are a lot of special cases for variable naming, including accessing variable indexes, accessing extended variables, and accessing nested variables. I have only partially handled these special cases - recommendations for misspellings of the base variable name will generally work, but anything more complex will not work, and the recommendations will not include the index or extended variable syntax. 

I reworded some of the missing variable error messages, just to make them subjectively sound better. For example, `Non-existing variable '%s'` is now `Variable '%s' not found`. This breaks many tests throughout `atest`, but in this PR I have not fixed those tests yet. I'll fix them if this rewording/rephrasing is approved. 

For example recommendations, please see the new test cases I've added.
If `self._normalize` is used only here, it would probably be a better idea to define it here as a nested function or lambda. It would then have access to `candidate` automatically:

```python
normalizer = lambda name: candidates.get(name, name).lower().replace('_', ' ')
finder = utils.RecommendationFinder(normalizer)
```