docs: function(sel) {
    var sel = sel.replace(/^\s+|\s+$/g, '')
      , test
      , filter = []
      , buff = []
      , subject
      , qname
      , cap
      , op
      , ref;
  
    while (sel) {
      if (cap = rules.qname.exec(sel)) {
        sel = sel.substring(cap[0].length);
        qname = cap[1];
        buff.push(tok(qname, true));
      } else if (cap = rules.simple.exec(sel)) {
        sel = sel.substring(cap[0].length);
        qname = '*';
        buff.push(tok(qname, true));
        buff.push(tok(cap));
      } else {
        throw new Error('Invalid selector.');
      }
  
      while (cap = rules.simple.exec(sel)) {
        sel = sel.substring(cap[0].length);
        buff.push(tok(cap));
      }
  
      if (sel[0] === '!') {
        sel = sel.substring(1);
        subject = makeSubject();
        subject.qname = qname;
        buff.push(subject.simple);
      }
  
      if (cap = rules.ref.exec(sel)) {
        sel = sel.substring(cap[0].length);
        ref = combinators.ref(makeSimple(buff), cap[1]);
        filter.push(ref.combinator);
        buff = [];
        continue;
      }
  
      if (cap = rules.combinator.exec(sel)) {
        sel = sel.substring(cap[0].length);
        op = cap[1] || cap[2] || cap[3];
        if (op === ',') {
          filter.push(combinators.noop(makeSimple(buff)));
          break;
        }
      } else {
        op = 'noop';
      }
  
      filter.push(combinators[op](makeSimple(buff)));
      buff = [];
    }
  
    test = makeTest(filter);
    test.qname = qname;
    test.sel = sel;
  
    if (subject) {
      subject.lname = test.qname;
  
      subject.test = test;
      subject.qname = subject.qname;
      subject.sel = test.sel;
      test = subject;
    }
  
    if (ref) {
      ref.test = test;
      ref.qname = test.qname;
      ref.sel = test.sel;
      test = ref;
    }
  
    return test;
  }