code: @Override
      public Object visitSelpart(CSSParser.SelpartContext ctx) {
          logEnter("selpart");
          String ident;
          if (ctx.HASH() != null) {
              ident = extractIdUnescaped(ctx.HASH().getText());
              if (ident != null) {
                  selector_stack.peek().s.add(rf.createID(ident));
              } else {
                  combined_selector_stack.peek().invalid = true;
              }
  
          } else if (ctx.CLASSKEYWORD() != null) {
              selector_stack.peek().s.add(rf.createClass(extractTextUnescaped(ctx.CLASSKEYWORD().getText())));
          } else if (ctx.attribute() != null) {
              Selector.ElementAttribute ea = visitAttribute(ctx.attribute());
              selector_stack.peek().s.add(ea);
  
          } else if (ctx.pseudo() != null) {
              Selector.SelectorPart p = visitPseudo(ctx.pseudo());
              if (p != null) {
                  if (p instanceof PseudoElement && selector_stack.peek().s.getPseudoElementType() != null) {
                      log.warn("Invalid selector with multiple pseudo-elements");
                      combined_selector_stack.peek().invalid = true;
                  }
                  else
                      selector_stack.peek().s.add(p);
              } else {
                  combined_selector_stack.peek().invalid = true;
              }
  
          } else {
              combined_selector_stack.peek().invalid = true;
          }
          logLeave("selpart");
          //returns null
          return null;
      }
docs: selpart
  : HASH
  | CLASSKEYWORD
  | LBRACKET S* attribute RBRACKET
  | pseudo
  | INVALID_SELPART // invalid selpart