docs: function attribute() {
    var args = mlutil.asArray.apply(null, arguments);
    switch(args.length) {
    case 0:
      throw new Error('missing element and attribute');
    case 1:
      throw new Error('has element but missing attribute: '+mlutil.identify(args[0], true));
    case 2:
      return new AttributeDef(
        (args[0] instanceof QNameDef) ? args[0] :
          Array.isArray(args[0]) ? qname.call(null, args[0]) :
          new QNameDef(null, args[0]),
        (args[1] instanceof QNameDef) ? args[1] :
            Array.isArray(args[1]) ? qname.call(null, args[1]) :
            new QNameDef(null, args[1])
        );
    case 3:
      if (args[0] instanceof QNameDef) {
        return new AttributeDef(
          args[0],
          new QNameDef(args[1], args[2])
        );
      } else if (args[2] instanceof QNameDef) {
        return new AttributeDef(
          new QNameDef(args[0], args[1]),
          args[2]
        );
      } else if (Array.isArray(args[0])) {
        return new AttributeDef(
          qname.call(null, args[0]),
          new QNameDef(args[1], args[2])
        );
      } else if (Array.isArray(args[2])) {
        return new AttributeDef(
          new QNameDef(args[0], args[1]),
          qname.call(null, args[2])
        );
      }
      return new AttributeDef(
        new QNameDef(args[0], args[1]),
        new QNameDef(null,    args[2])
        );
    default:
      return new AttributeDef(
        new QNameDef(args[0], args[1]),
        new QNameDef(args[2], args[3])
        );
   }
  }