public final function matches($matcher)
    {
        // Handle negations of matchers.
        if (is_string($matcher) && substr($matcher, 0, 3) === 'NO_') {
            return ! $this->matches(substr($matcher, 3));
        }

        // Handle some special cases.
        if ($matcher === 'WHITESPACE_OR_COMMENT') {
            return $this->matches(T_WHITESPACE) || $this->matches(T_COMMENT)
                        || $this->matches(T_DOC_COMMENT);
        } else if ($matcher === 'COMMENT') {
            return $this->matches(T_COMMENT) || $this->matches(T_DOC_COMMENT);
        } else if ($matcher === 'WHITESPACE') {
            return $this->matches(T_WHITESPACE);
        } else if ($matcher === 'END_OF_LINE') {
            return $this->isLastTokenOnLine();
        } else if ($matcher === 'END_OF_NAME') {
            return ! $this->matches(T_STRING)
                        && ! $this->matches(T_NS_SEPARATOR);
        } else if ($matcher === 'END_OF_CALL') {
            $opened = 0;
            $matcher = function(AbstractToken $token) use (&$opened) {
                switch (true) {
                    case $token->matches(')'):
                        return 0 === ($opened--);

                    case $token->matches('('):
                        $opened += 1;
                    // fall through

                    default:
                        return false;
                }
            };
        }

        if ($matcher instanceof \Closure) {
            return $matcher($this);
        }

        return $this->matchesInternal($matcher);
    }