class Foo { public static function main () { trace( tokenize("div a[href] input") ); } static function tokenize(str:String) { str += ' x'; // Maybe a dirty hack? It probably saves alot of code var blocks : Array; var char : Int; var prevChar : Int; var prev : Selector; var curr : Selector; var text = ''; var attSelector = false; var pseudoSelector = false; var inQuotes = false; var inCombinator = false; for(x in 0 ... str.length) { prevChar = char; char = str.charCodeAt(x); // Handle strings in quotes (for attribute selectors) if(inQuotes) { if(char == 34 && prevChar != 92) // """ && != "\" inQuotes = false; else text += str.charAt(x); } else if(inCombinator) { trace(str.charAt(x)); switch(char) { case 9,10,13,32: // \t,\n,\r," " // Ignore multiple whitespace chars switch(prevChar){ case 9,10,13,32: continue; } case 62: // ">" blocks.push(ChildOf(curr)); case 43: // "+" blocks.push(ImmediatelyPrecededBy(curr)); case 126: // "~" blocks.push(PrecededBy(curr)); default: // New word inCombinator = false; if(blocks == null) blocks = [Tag(curr)]; else blocks.push(DecendantOf(curr)); curr = prev = null; } } if(!inCombinator) switch(char) { case 9,10,13,32: // \t,\n,\r," " // Ignore multiple whitespace chars switch(prevChar){ case 9,10,13,32: continue; } if(prev == null) curr = Element(text, None); inCombinator = true; text = ''; case 42: // "*" if(!attSelector) { curr = Any(None); continue; } case 91: // "[" if(prev == null) prev = Element(text, None); else throw 'Parse error'; text = ''; attSelector = true; case 93: // "]" if(curr == null) { curr = AttExists(text); switch(prev) { case Any(s): curr = Any(curr); case Element(t, s): curr = Element(t, curr); default: throw 'Parse error'; } } else trace(curr); text = null; attSelector = false; case 61: // "=" if(!attSelector || prev == null) throw 'Parse error'; switch(prev) { case Any(s): prev = Any(AttEquals(text, null)); case Element(t, s): prev = Element(t, AttEquals(text, null)); default: throw 'Parse error'; } text = ''; case 34: // """ inQuotes = true; case 126: // "~" if(!attSelector) throw 'Parse error'; case 94: // "^" if(!attSelector) throw 'Parse error'; case 36: // "$" case 124: // "|" case 58: // ":" case 40: // "(" case 41: // ")" case 35: // "#" default: text += str.charAt(x); } } return blocks; } static function setNextSelector() { } } enum Combinator { Tag(selector:Selector); /** E F **/ DecendantOf(selector:Selector); /** E > F **/ ChildOf(selector:Selector); /** E + F **/ ImmediatelyPrecededBy(selector:Selector); /** E ~ F **/ PrecededBy(selector:Selector); } enum Selector { None; // For filling (yet to be defined) selector arguments in "Any" and "Element" /** * **/ Any(selector:Selector); /** E **/ Element(type:String, selector:Selector); /** E[foo] **/ AttExists(name:String); /** E[foo="bar"] **/ AttEquals(name:String, value:String); /** E[foo~="bar"] **/ AttHasListed(name:String, value:String); /** E[foo^="bar"] **/ AttStartsWith(name:String, value:String); /** E[foo$="bar"] **/ AttEndsWith(name:String, value:String); /** E[foo*="bar"] **/ AttContains(name:String, value:String); /** E[hreflang|="en"] or E[hreflang|="en-US"] **/ AttStartsWith_OptionallyFollowedByHyphen(name:String, value:String); // Rename please :-S /** E:root **/ DocumentRoot; /** E:nth-child(n) **/ NthChild(n:Int); /** E:nth-last-child(n) **/ NthLastChild_CountFromLast(n:Int); /** E:nth-of-type(n) **/ NthOfType(n:Int); /** E:nth-last-of-type(n) **/ NthOfType_CountFromLast(n:Int); /** E:first-child **/ FirstChild; /** E:last-child **/ LastChild; /** E:first-of-type **/ FirstSiblingOfType; /** E:last-of-type **/ LastSiblingOfType; /** E:only-child **/ OnlyChild; /** E:only-of-type **/ OnlySiblingOfType; /** E:empty **/ Empty; /** E:link **/ Link; /** E:visited **/ isVisitedLink; /** E:active **/ isActive; /** E:hover **/ isHovered; /** E:focus **/ hasFocus; /** E:target **/ linkTarget; /** E:lang(fr) **/ Language(name:String); /** E:enabled **/ Enabled; /** E:disabled **/ Disabled; /** E:checked **/ Checked; /** E::first-line **/ FirstLine; /** E::first-letter **/ FirstLetter; /** E::selection **/ Selection; /** E::before **/ Before; /** E::after **/ After; /** E.warning **/ Class(name:String); /** E#myid **/ ID(name:String); /** E:not(s) **/ Not(selector:Selector); }