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.
I like this better than earlier version but creating `KeywordRecommendationFinder.format_recommendations` method only when this method is called isn't optimal. It's not out of the question in general, but because there are other solutions I would like to try them:

1) Because `RecommendationFinder.format_recommendations` is static in its nature, we could just add this to `KeywordRecommendationFinder`:

```python
def format_recommendations(self, message, recommendations):
    return RecommendationFinder().format_recommendations(message, recommendations)
```

Obviously could make `RecommendationFinder.format_recommendations` static for real and use just `RecommendationFinder.format_recommendations(message, recommendations)` above too. Marking the method static with `@staticmethod` would also make it explicit that it is really a static helper not related to the data passed to `__init__` or `find_recommendations`.

2) We could change `KeywordRecommendationFinder` so that it extends `RecommendationFinder` and get `format_recommendations` for free. This would probably require other bigger changes, though, and this feels like a place where composition works better than inheritance in general.