docs: def compile(schema, pointer, context, scope=None):
      """
      Compiles schema with `JSON Schema`_ draft-03.
  
      :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:
          attrs['additional_properties'] = schm.pop('additionalProperties')
          if isinstance(attrs['additional_properties'], dict):
              subpointer = pointer_join(pointer, 'additionalProperties')
              value = attrs['additional_properties']
              attrs['additional_properties'] = compile(value,
                                                       subpointer,
                                                       context,
                                                       scope)
          elif not isinstance(attrs['additional_properties'], bool):
              raise CompilationError('additionalProperties must be an object or boolean', schema)  # noqa
  
      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 isinstance(value, sequence_types):
                  continue
              elif not isinstance(value, string_types):
                  raise CompilationError('dependencies must be an array, object or string', schema)  # noqa
  
      if 'disallow' in schm:
          attrs['disallow'] = schm.pop('disallow')
          if isinstance(attrs['disallow'], sequence_types):
              for index, value in enumerate(attrs['disallow']):
                  if isinstance(value, dict):
                      subpointer = pointer_join(pointer, 'disallow', index)
                      attrs['disallow'][index] = compile(value,
                                                         subpointer,
                                                         context,
                                                         scope)
                  elif not isinstance(value, string_types):
                      raise CompilationError('disallow must be an object or string', schema)  # noqa
          elif not isinstance(attrs['disallow'], string_types):
              raise CompilationError('disallow must be an array or string', schema)  # noqa
  
      if 'divisibleBy' in schm:
          attrs['divisible_by'] = schm.pop('divisibleBy')
          if not isinstance(attrs['divisible_by'], number_types):
              raise CompilationError('divisibleBy must be a number', schema)
  
      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 'extends' in schm:
          attrs['extends'] = schm.pop('extends')
          subpointer = pointer_join(pointer, 'extends')
          if isinstance(attrs['extends'], dict):
              attrs['extends'] = compile(attrs['extends'],
                                         subpointer,
                                         context,
                                         scope)
          elif isinstance(attrs['extends'], sequence_types):
              for index, value in enumerate(attrs['extends']):
                  attrs['extends'][index] = compile(value,
                                                    subpointer,
                                                    context,
                                                    scope)
          else:
              raise CompilationError('extends must be an object or array', 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('enum must be an integer', schema)
  
      if 'maxItems' in schm:
          attrs['max_items'] = schm.pop('maxItems')
          if not isinstance(attrs['max_items'], integer_types):
              raise CompilationError('maxItems must be an 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 'minimum' in schm:
          attrs['minimum'] = schm.pop('minimum')
          if not isinstance(attrs['minimum'], number_types):
              raise CompilationError('enum 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 an 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 'pattern' in schm:
          attrs['pattern'] = schm.pop('pattern')
          if not isinstance(attrs['pattern'], string_types):
              raise CompilationError('pattern must be a string', schema)
  
      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 name, value in attrs['pattern_properties'].items():
              subpointer = pointer_join(pointer, 'patternProperties', name)
              attrs['pattern_properties'][name] = compile(value,
                                                          subpointer,
                                                          context,
                                                          scope)
  
      if 'properties' in schm:
          attrs['properties'] = schm.pop('properties')
          if not isinstance(attrs['properties'], dict):
              raise CompilationError('properties must be an object', schema)
          for name, value in attrs['properties'].items():
              subpointer = pointer_join(pointer, 'properties', name)
              attrs['properties'][name] = compile(value,
                                                  subpointer,
                                                  context,
                                                  scope)
  
      if 'required' in schm:
          attrs['required'] = schm.pop('required')
          if not isinstance(attrs['required'], bool):
              raise CompilationError('required must be a boolean', schema)
  
      if 'type' in schm:
          attrs['type'] = schm.pop('type')
          if isinstance(attrs['type'], sequence_types):
              for index, value in enumerate(attrs['type']):
                  if isinstance(value, dict):
                      subpointer = pointer_join(pointer, 'type', index)
                      attrs['type'][index] = compile(value,
                                                     subpointer,
                                                     context,
                                                     scope)
                  elif not isinstance(value, string_types):
                      raise CompilationError('type must be an object or string', schema)  # noqa
          elif not isinstance(attrs['type'], string_types):
              raise CompilationError('type must be an array or string', schema)  # noqa
  
      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 Draft03Validator(attrs, scope, context.formats)