docs: def compile(schema, pointer, context, scope=None):
      """
      Compiles schema with `JSON Schema`_ draft-04.
  
      :param schema: obj to compile
      :type schema: Mapping
      :param pointer: uri of the schema
      :type pointer: Pointer, str
      :param context: context of this schema
      :type context: Context
  
      .. _`JSON Schema`: http://json-schema.org
      """
  
      schm = deepcopy(schema)
  
      scope = urljoin(scope or str(pointer), schm.pop('id', None))
  
      if '$ref' in schema:
          return ReferenceValidator(urljoin(scope, schema['$ref']), context)
  
      attrs = {}
  
      if 'additionalItems' in schm:
          subpointer = pointer_join(pointer, 'additionalItems')
          attrs['additional_items'] = schm.pop('additionalItems')
          if isinstance(attrs['additional_items'], dict):
              compiled = compile(attrs['additional_items'],
                                 subpointer,
                                 context,
                                 scope)
              attrs['additional_items'] = compiled
          elif not isinstance(attrs['additional_items'], bool):
              raise CompilationError('wrong type for {}'.format('additional_items'), schema)  # noqa
  
      if 'additionalProperties' in schm:
          subpointer = pointer_join(pointer, 'additionalProperties')
          attrs['additional_properties'] = schm.pop('additionalProperties')
          if isinstance(attrs['additional_properties'], dict):
              compiled = compile(attrs['additional_properties'],
                                 subpointer,
                                 context,
                                 scope)
              attrs['additional_properties'] = compiled
          elif not isinstance(attrs['additional_properties'], bool):
              raise CompilationError('wrong type for {}'.format('additional_properties'), schema)  # noqa
  
      if 'allOf' in schm:
          subpointer = pointer_join(pointer, 'allOf')
          attrs['all_of'] = schm.pop('allOf')
          if isinstance(attrs['all_of'], (list, tuple)):
              attrs['all_of'] = [compile(element, subpointer, context, scope) for element in attrs['all_of']]  # noqa
          else:
              # should be a boolean
              raise CompilationError('wrong type for {}'.format('allOf'), schema)  # noqa
  
      if 'anyOf' in schm:
          subpointer = pointer_join(pointer, 'anyOf')
          attrs['any_of'] = schm.pop('anyOf')
          if isinstance(attrs['any_of'], (list, tuple)):
              attrs['any_of'] = [compile(element, subpointer, context, scope) for element in attrs['any_of']]  # noqa
          else:
              # should be a boolean
              raise CompilationError('wrong type for {}'.format('anyOf'), schema)  # noqa
  
      if 'default' in schm:
          attrs['default'] = schm.pop('default')
  
      if 'dependencies' in schm:
          attrs['dependencies'] = schm.pop('dependencies')
          if not isinstance(attrs['dependencies'], dict):
              raise CompilationError('dependencies must be an object', schema)
          for key, value in attrs['dependencies'].items():
              if isinstance(value, dict):
                  subpointer = pointer_join(pointer, 'dependencies', key)
                  attrs['dependencies'][key] = compile(value,
                                                       subpointer,
                                                       context,
                                                       scope)
              elif not isinstance(value, sequence_types):
                  raise CompilationError('dependencies must be an array or object', schema)  # noqa
  
      if 'enum' in schm:
          attrs['enum'] = schm.pop('enum')
          if not isinstance(attrs['enum'], sequence_types):
              raise CompilationError('enum must be a sequence', schema)
  
      if 'exclusiveMaximum' in schm:
          attrs['exclusive_maximum'] = schm.pop('exclusiveMaximum')
          if not isinstance(attrs['exclusive_maximum'], bool):
              raise CompilationError('exclusiveMaximum must be a boolean', schema)  # noqa
  
      if 'exclusiveMinimum' in schm:
          attrs['exclusive_minimum'] = schm.pop('exclusiveMinimum')
          if not isinstance(attrs['exclusive_minimum'], bool):
              raise CompilationError('exclusiveMinimum must be a boolean', schema)  # noqa
  
      if 'format' in schm:
          attrs['format'] = schm.pop('format')
          if not isinstance(attrs['format'], string_types):
              raise CompilationError('format must be a string', schema)
  
      if 'items' in schm:
          subpointer = pointer_join(pointer, 'items')
          attrs['items'] = schm.pop('items')
          if isinstance(attrs['items'], (list, tuple)):
              # each value must be a json schema
              attrs['items'] = [compile(element, subpointer, context, scope) for element in attrs['items']]  # noqa
          elif isinstance(attrs['items'], dict):
              # value must be a json schema
              attrs['items'] = compile(attrs['items'], subpointer, context, scope)  # noqa
          else:
              # should be a boolean
              raise CompilationError('wrong type for {}'.format('items'), schema)  # noqa
  
      if 'maximum' in schm:
          attrs['maximum'] = schm.pop('maximum')
          if not isinstance(attrs['maximum'], number_types):
              raise CompilationError('maximum must be a number', schema)
  
      if 'maxItems' in schm:
          attrs['max_items'] = schm.pop('maxItems')
          if not isinstance(attrs['max_items'], integer_types):
              raise CompilationError('maxItems must be integer', schema)
  
      if 'maxLength' in schm:
          attrs['max_length'] = schm.pop('maxLength')
          if not isinstance(attrs['max_length'], integer_types):
              raise CompilationError('maxLength must be integer', schema)
  
      if 'maxProperties' in schm:
          attrs['max_properties'] = schm.pop('maxProperties')
          if not isinstance(attrs['max_properties'], integer_types):
              raise CompilationError('maxProperties must be integer', schema)
  
      if 'minimum' in schm:
          attrs['minimum'] = schm.pop('minimum')
          if not isinstance(attrs['minimum'], number_types):
              raise CompilationError('minimum must be a number', schema)
  
      if 'minItems' in schm:
          attrs['min_items'] = schm.pop('minItems')
          if not isinstance(attrs['min_items'], integer_types):
              raise CompilationError('minItems must be integer', schema)
  
      if 'minLength' in schm:
          attrs['min_length'] = schm.pop('minLength')
          if not isinstance(attrs['min_length'], integer_types):
              raise CompilationError('minLength must be integer', schema)
  
      if 'minProperties' in schm:
          attrs['min_properties'] = schm.pop('minProperties')
          if not isinstance(attrs['min_properties'], integer_types):
              raise CompilationError('minProperties must be integer', schema)
  
      if 'multipleOf' in schm:
          attrs['multiple_of'] = schm.pop('multipleOf')
          if not isinstance(attrs['multiple_of'], number_types):
              raise CompilationError('multipleOf must be a number', schema)
  
      if 'not' in schm:
          attrs['not'] = schm.pop('not')
          if not isinstance(attrs['not'], dict):
              raise CompilationError('not must be an object', schema)
          subpointer = pointer_join(pointer, 'not')
          attrs['not'] = compile(attrs['not'], subpointer, context, scope)
  
      if 'oneOf' in schm:
          subpointer = pointer_join(pointer, 'oneOf')
          attrs['one_of'] = schm.pop('oneOf')
          if isinstance(attrs['one_of'], (list, tuple)):
              # each value must be a json schema
              attrs['one_of'] = [compile(element, subpointer, context, scope) for element in attrs['one_of']]  # noqa
          else:
              # should be a boolean
              raise CompilationError('wrong type for {}'.format('oneOf'), schema)
  
      if 'pattern' in schm:
          attrs['pattern'] = schm.pop('pattern')
          if not isinstance(attrs['pattern'], string_types):
              raise CompilationError('pattern must be a string', schema)
  
      if 'properties' in schm:
          attrs['properties'] = schm.pop('properties')
          if not isinstance(attrs['properties'], dict):
              raise CompilationError('properties must be an object', schema)
          for subname, subschema in attrs['properties'].items():
              subpointer = pointer_join(pointer, subname)
              compiled = compile(subschema, subpointer, context, scope)
              attrs['properties'][subname] = compiled
  
      if 'patternProperties' in schm:
          attrs['pattern_properties'] = schm.pop('patternProperties')
          if not isinstance(attrs['pattern_properties'], dict):
              raise CompilationError('patternProperties must be an object', schema)  # noqa
          for subname, subschema in attrs['pattern_properties'].items():
              subpointer = pointer_join(pointer, 'patternProperties', subname)
              compiled = compile(subschema, subpointer, context, scope)
              attrs['pattern_properties'][subname] = compiled
  
      if 'required' in schm:
          attrs['required'] = schm.pop('required')
          if not isinstance(attrs['required'], list):
              raise CompilationError('required must be a list', schema)
          if len(attrs['required']) < 1:
              raise CompilationError('required cannot be empty', schema)
  
      if 'type' in schm:
          attrs['type'] = schm.pop('type')
          if isinstance(attrs['type'], string_types):
              attrs['type'] = [attrs['type']]
          elif not isinstance(attrs['type'], sequence_types):
              raise CompilationError('type must be string or sequence', schema)
  
      if 'uniqueItems' in schm:
          attrs['unique_items'] = schm.pop('uniqueItems')
          if not isinstance(attrs['unique_items'], bool):
              raise CompilationError('type must be boolean', schema)
  
      return Draft04Validator(attrs, str(pointer), context.formats)