Compute the specificity of a CSS selector Today, your task is to write a program (or a function) that accepts a string and outputs (or returns) four integers.



Input

The input string is a CSS3 selector, and can contain basically any Unicode character.



Output

The output represents the CSS specificity of this selector.


The first number is always 0 (because it's used for inline styles, and this exercise doesn't apply to inline styles)
The second number is the number of ids (#foo) present in the selector.
The third number is the number of classes (.foo), attributes ([bar]) and pseudo-classes present in the selector.
The fourth number is the number of elements (biz)  and pseudo-elements present in the selector.




Notes:


Universal selector (*) isn't counted anywhere
The pseudo-elements ::before and ::after can also be written with a single ":" (legacy notation)
Input can use the :not(selector) pseudo-class. The selector inside doesn't count, even if it contains ids, classes, elements, ...)
The "bricks" of the selector are separated by combinators (spaces/tabs, +, &gt;, ~, ex: body &gt; div+a ~*), but they can also be cumulated (ex: div#foo.bar[baz]:hover::before)
You also have to handle CSS escape sequences ( \ followed by 1 to 6 hexadecimal numbers, followed by a space), and escaped special characters (\ followed by any of these: !"#$%&amp;'()*+,-./:;&lt;=&gt;?@[\]^`{|}~) properly. Those escapes can be part of any brick of the selector (id, class, etc).
You don't need to do anything particular if you receive an invalid selector or a CSS4 selector. Don't bother implementing a CSS3 selector validator.
Here are a few links to learn more about CSS specificiy:


CSS-tricks article: http://css-tricks.com/specifics-on-css-specificity/
A tool (incomplete) that counts it: http://specificity.keegan.st/





Examples



// Universal

*                       => 0,0,0,0

// ID

#id                     => 0,1,0,0

// Class

.class                  => 0,0,1,0

// Attributes

[foo]                   => 0,0,1,0
[foo="bar"]             => 0,0,1,0
[foo~="bar"]            => 0,0,1,0
[foo^="bar"]            => 0,0,1,0
[foo$="bar"]            => 0,0,1,0
[foo*="bar"]            => 0,0,1,0
[foo|="bar"]            => 0,0,1,0
[ foo  =  bar ]         => 0,0,1,0
[foo  =  'bar']         => 0,0,1,0

(NB: brackets [] can contain anything except an unescaped "]")

// Pseudo-classes

:root                   => 0,0,1,0
:nth-child(n)           => 0,0,1,0
:nth-last-child(n)      => 0,0,1,0
:nth-of-type(n)         => 0,0,1,0
:nth-last-of-type(n)    => 0,0,1,0
:first-child            => 0,0,1,0
:last-child             => 0,0,1,0
:first-of-type          => 0,0,1,0
:last-of-type           => 0,0,1,0
:only-child             => 0,0,1,0
:only-of-type           => 0,0,1,0
:empty                  => 0,0,1,0
:link                   => 0,0,1,0
:visited                => 0,0,1,0
:active                 => 0,0,1,0
:hover                  => 0,0,1,0
:focus                  => 0,0,1,0
:target                 => 0,0,1,0
:lang(fr)               => 0,0,1,0
:enabled                => 0,0,1,0
:disabled               => 0,0,1,0
:checked                => 0,0,1,0
:not(selector)          => 0,0,1,0

(NB: the keyword after ":" can be anything except a pseudo-element)


// Elements

body                    => 0,0,0,1

// Pseudo-elements

:before                 => 0,0,0,1
:after                  => 0,0,0,1
::before                => 0,0,0,1
::after                 => 0,0,0,1
::first-line            => 0,0,0,1
::first-letter          => 0,0,0,1

(NB: parenthesis () can contain anything except an unescaped ")" )



(to be continued)



If you have questions or need examples or test data, please ask in the comments. 

Shortest code (in bytes) wins.

Good luck!
Javascript ES6 453 430 bytes
Here goes my shot!
a=&gt;(s){var n=z=[],c=[0,0,0,0],e=s.split(/[\s\+\&gt;\~]+/);return e.forEach((s)=&gt;{n=n.concat(s.replace(/\((.*)\)/,&quot;&quot;).split(/(?=\[|::|\.)/))}),n.forEach((s)=&gt;{if(0==s.indexOf(&quot;::&quot;))return z.push(s);var n=s.split(/(?=:)/);z=z.concat(n[0].split(/(?=[\#])/)),/before|after/.test(n[1])?z.push(&quot;:&quot;+n[1]):n[1]&amp;&amp;z.push(n[1])}),z.forEach((s)=&gt;{/^[a-z]+$/gi.test(s)||&quot;::&quot;==s[0]+s[1]?c[3]++:-1!=&quot;:.[&quot;.indexOf(s[0])?c[2]++:&quot;#&quot;==s[0]&amp;&amp;c[1]++}),c}

@UPDATE improved code, now it handles :not and :before/:after selectors better, nevertheless haven't tested CSS escape sequences.