I have a library that's parsing some expressions, part of which is a CSS selector.  More accurately, it's a jQuery-compatible selector.  The selector itself is opaque to my library - I don't need to pick it apart, and I don't need to verify that tag and attribute names are valid HTML, or that its pseudo-selectors exist in the current spec.  For now, I just need it to match all syntactically valid CSS selectors and not match the rest of the expression, which is delimited by characters that would not be valid in a selector unless escaped or put in a string in an attribute selector.  I would use a RegExp and split the string if CSS had no escapes or quoted values, but it started looking write-only really fast.Does this PEG.js grammar cover all the other edge cases as defined by the CSS standard and jQuery documentation?  Could it be more readable?  Better in some other way?{/*  * glom the array contents returned by the parser * into a string recursively. */function collapse(stuff) {    if (typeof stuff === string) {        return stuff;    } else if (stuff instanceof Array) {        return stuff.reduce(function (a, item) {            return a.concat(collapse(item));        }, )    } else {        return ;    }}/* * Replace superfluous white space with a single space. */function trimWs(present) {    if (present) {        if (present instanceof Array) {            return !!present.length ?   : ;        } else {            return  ;        }    } else {        return ;    }}}start = jqSelectorws white space =     whites:(  / \r / \n / \t / \f)+{ return collapse(whites); }iws ignored white space = ws? { return ; }cws collapsed white space = ws:ws { return trimWs(ws); }jqSelector jquery-compatible selector =    element:(tagIdClassSelector / cssFunctional / cssAttrExpr)+    more:(iws [\+>~,] iws jqSelector / cws jqSelector)?  { return collapse([element, more]); }tagIdClassSelector tag, id, or class selector =    [#\.]? cssIdentifier / *cssIdentifier CSS identifier =     start:cssIdStart rest:cssIdChar* {    return collapse([start, rest]);}cssIdStart start of CSS identifier =    [a-zA-Z_]  / escapedChar  / - ([a-zA-Z_] / escapedChar)cssIdChar rest of CSS identifier =    [-a-zA-Z0-9_] / escapedCharescapedChar escape sequence =    \\ (escapedUnicode / .)escapedUnicode 1-6 hexadecimal digits (unicode escape) =     d0:hexd (d1:hexd (d2:hexd (d3:hexd (d4:hexd d5:hexd?)?)?)?)?{    return [d0, d1, d2, d3, d4, d5].join();}hexd hexadecimal digit =    [0-9a-fA-F]cssFunctional pseudo-selector or functional selector =    : cssIdentifier ( ( cssArg ) )?cssAttrExpr attribute selector =    [ cssIdentifier (        [|~\*^$]? = (cssIdentifier / quotedString)    )? ]quotedString quoted string =     ' (escapedChar / [^\'])* '  / '' (escapedChar / [^\])* ''cssArg functional selector argument =     anpbOddEven / uint / jqSelectoranpbOddEven 'an+b' expression, 'odd',  'even', or integer =     [-\+]? uint? [nN] (ws* [-\+] ws* uint)?   / [-\+] uint   / even  / odduint unsigned integer = [1-9][0-9]* / 0Compiled JS parser & minimal interactive tester is here: http://jsfiddle.net/np6BD/UPDATEIncorporating suggestions regarding the JS:{/*  * glom the array contents returned by the parser * into a string recursively. * Elses aren't logically necessary to perform this function, * but if I use code folding in my IDE, it still looks like it * does what it does if I include the elses. * Changed name from collapse to prevent confusion about * the other sense of the word in the cws token. * Changed reduce/concat to map/join. */function serialize(stuff) {    if (typeof stuff === string) {        return stuff;    } else if (stuff instanceof Array) {        return stuff.map(serialize).join();    } else {        return ;    }}/* * Replace superfluous white space with a single space. * Function moved to the cws token */}start = jqSelectorws white space =     whites:(  / \r / \n / \t / \f)+{ return serialize(whites); }iws ignored white space = ws? { return ; }cws collapsed white space = ws:ws {     if (ws) {        if (ws instanceof Array) {            return !!ws.length ?   : ;        } else {            return  ;        }    } else {        return ;    }}jqSelector jquery-compatible selector =    element:(tagIdClassSelector / cssFunctional / cssAttrExpr)+    more:(iws [\+>~,] iws jqSelector / cws jqSelector)?{    return serialize([element, more]);}tagIdClassSelector tag, id, or class selector =    [#\.]? cssIdentifier / *cssIdentifier CSS identifier =     start:cssIdStart rest:cssIdChar* {    return serialize([start, rest]);}cssIdStart start of CSS identifier =    [a-zA-Z_]  / escapedChar  / - ([a-zA-Z_] / escapedChar)cssIdChar rest of CSS identifier =    [-a-zA-Z0-9_] / escapedCharescapedChar escape sequence =    \\ (escapedUnicode / .)escapedUnicode 1-6 hexadecimal digits (unicode escape) =     d0:hexd (d1:hexd (d2:hexd (d3:hexd (d4:hexd d5:hexd?)?)?)?)?{    return [d0, d1, d2, d3, d4, d5].join();}hexd hexadecimal digit =    [0-9a-fA-F]cssFunctional pseudo-selector or functional selector =    : cssIdentifier ( ( cssArg ) )?cssAttrExpr attribute selector =    [ cssIdentifier (        [|~\*^$]? = (cssIdentifier / quotedString)    )? ]quotedString quoted string =     ' (escapedChar / [^\'])* '  / '' (escapedChar / [^\])* ''cssArg functional selector argument =     anpbOddEven / uint / jqSelectoranpbOddEven 'an+b' expression, 'odd',  'even', or integer =     [-\+]? uint? [nN] (ws* [-\+] ws* uint)?   / [-\+] uint   / even  / odduint unsigned integer = [1-9][0-9]* / 0
PEG.js grammar for parsing CSS selectors
javascript;css;parsing;grammar;peg.js