function matches(node, selector, ancestry) {
            var path, ancestor, i, l, p;
            if (!selector) { return true; }
            if (!node) { return false; }
            if (!ancestry) { ancestry = []; }

            switch(selector.type) {
                case 'wildcard':
                    return true;

                case 'identifier':
                    return selector.value.toLowerCase() === node.type.toLowerCase();

                case 'field':
                    path = selector.name.split('.');
                    ancestor = ancestry[path.length - 1];
                    return inPath(node, ancestor, path);

                case 'matches':
                    for (i = 0, l = selector.selectors.length; i < l; ++i) {
                        if (matches(node, selector.selectors[i], ancestry)) { return true; }
                    }
                    return false;

                case 'compound':
                    for (i = 0, l = selector.selectors.length; i < l; ++i) {
                        if (!matches(node, selector.selectors[i], ancestry)) { return false; }
                    }
                    return true;

                case 'not':
                    for (i = 0, l = selector.selectors.length; i < l; ++i) {
                        if (matches(node, selector.selectors[i], ancestry)) { return false; }
                    }
                    return true;

                case 'child':
                    if (matches(node, selector.right, ancestry)) {
                        return matches(ancestry[0], selector.left, ancestry.slice(1));
                    }
                    return false;

                case 'descendant':
                    if (matches(node, selector.right, ancestry)) {
                        for (i = 0, l = ancestry.length; i < l; ++i) {
                            if (matches(ancestry[i], selector.left, ancestry.slice(i + 1))) {
                                return true;
                            }
                        }
                    }
                    return false;

                case 'attribute':
                    p = getPath(node, selector.name);
                    switch (selector.operator) {
                        case null:
                        case void 0:
                            return p != null;
                        case '=':
                            switch (selector.value.type) {
                                case 'regexp': return selector.value.value.test(p);
                                case 'literal': return '' + selector.value.value === '' + p;
                                case 'type': return selector.value.value === typeof p;
                            }
                        case '!=':
                            switch (selector.value.type) {
                                case 'regexp': return !selector.value.value.test(p);
                                case 'literal': return '' + selector.value.value !== '' + p;
                                case 'type': return selector.value.value !== typeof p;
                            }
                        case '<=': return p <= selector.value.value;
                        case '<': return p < selector.value.value;
                        case '>': return p > selector.value.value;
                        case '>=': return p >= selector.value.value;
                    }

                case 'sibling':
                    return matches(node, selector.right, ancestry) &&
                        sibling(node, selector.left, ancestry) ||
                        matches(node, selector.left, ancestry) &&
                        sibling(node, selector.right, ancestry);

                case 'adjacent':
                    return matches(node, selector.right, ancestry) &&
                        adjacent(node, selector.left, ancestry) ||
                        matches(node, selector.left, ancestry) &&
                        adjacent(node, selector.right, ancestry);

                case 'nth-child':
                    return matches(node, selector.right, ancestry) &&
                        nthChild(node, ancestry, function (length) {
                            return selector.index.value - 1;
                        });

                case 'nth-last-child':
                    return matches(node, selector.right, ancestry) &&
                        nthChild(node, ancestry, function (length) {
                            return length - selector.index.value;
                        });

                case 'class':
                    if(!node.type) return false;
                    switch(selector.name.toLowerCase()){
                        case 'statement':
                            if(node.type.slice(-9) === 'Statement') return true;
                            // fallthrough: interface Declaration <: Statement { }
                        case 'declaration':
                            return node.type.slice(-11) === 'Declaration';
                        case 'pattern':
                            if(node.type.slice(-7) === 'Pattern') return true;
                            // fallthrough: interface Expression <: Node, Pattern { }
                        case 'expression':
                            return node.type.slice(-10) === 'Expression' ||
                                node.type === 'Literal' ||
                                node.type === 'Identifier';
                        case 'function':
                            return node.type.slice(0, 8) === 'Function' ||
                                node.type === 'ArrowFunctionExpression';
                    }
                    throw new Error('Unknown class name: ' + selector.name);
            }

            throw new Error('Unknown selector type: ' + selector.type);
        }
Given a `node` and its ancestors, determine if `node` is matched by `selector`.