text: #include "flecs_meta.h"
  #ifndef FLECS_META_PARSER_H
  #define FLECS_META_PARSER_H
  
  
  #define ECS_META_IDENTIFIER_LENGTH (256)
  
  #define ecs_meta_error(ctx, ptr, ...)\
      ecs_parser_error((ctx)->name, (ctx)->decl, ptr - (ctx)->decl, __VA_ARGS__)
  
  typedef char ecs_meta_token_t[ECS_META_IDENTIFIER_LENGTH];
  
  typedef struct ecs_meta_parse_ctx_t {
      const char *name;
      const char *decl;
  } ecs_meta_parse_ctx_t;
  
  typedef struct ecs_meta_type_t {
      ecs_meta_token_t type;
      ecs_meta_token_t params;
      bool is_const;
      bool is_ptr;
  } ecs_meta_type_t;
  
  typedef struct ecs_meta_member_t {
      ecs_meta_type_t type;
      ecs_meta_token_t name;
      int64_t count;
      bool is_partial;
  } ecs_meta_member_t;
  
  typedef struct ecs_meta_constant_t {
      ecs_meta_token_t name;
      int64_t value;
      bool is_value_set;
  } ecs_meta_constant_t;
  
  typedef struct ecs_meta_params_t {
      ecs_meta_type_t key_type;
      ecs_meta_type_t type;
      int64_t count;
      bool is_key_value;
      bool is_fixed_size;
  } ecs_meta_params_t;
  
  const char* ecs_meta_parse_constant(
      const char *ptr,
      ecs_meta_constant_t *token_out,
      ecs_meta_parse_ctx_t *ctx);
  
  const char* ecs_meta_parse_member(
      const char *ptr,
      ecs_meta_member_t *token_out,
      ecs_meta_parse_ctx_t *ctx);
  
  void ecs_meta_parse_params(
      const char *ptr,
      ecs_meta_params_t *token_out,
      ecs_meta_parse_ctx_t *ctx);
  
  #endif
  
  static
  ecs_vector_t* serialize_type(
      ecs_world_t *world,
      ecs_entity_t entity,
      ecs_vector_t *ops,
      int32_t offset,
      FlecsMeta *module);
  
  static
  ecs_size_t ecs_get_primitive_size(
      ecs_primitive_kind_t kind) 
  {
      switch(kind) {
      case EcsBool: return sizeof(bool);
      case EcsChar: return sizeof(char);
      case EcsByte: return sizeof(char);
      case EcsU8: return sizeof(uint8_t);
      case EcsU16: return sizeof(uint16_t);
      case EcsU32: return sizeof(uint32_t);
      case EcsU64: return sizeof(uint64_t);
      case EcsI8: return sizeof(int8_t);
      case EcsI16: return sizeof(int16_t);
      case EcsI32: return sizeof(int32_t);
      case EcsI64: return sizeof(int64_t);
      case EcsF32: return sizeof(float);
      case EcsF64: return sizeof(double);
      case EcsIPtr: return sizeof(intptr_t);
      case EcsUPtr: return sizeof(uintptr_t);
      case EcsString: return sizeof(char*);
      case EcsEntity: return sizeof(ecs_entity_t);
      default:
          ecs_abort(ECS_INTERNAL_ERROR, NULL);
      }
  }
  
  static
  int16_t ecs_get_primitive_alignment(
      ecs_primitive_kind_t kind) 
  {
      switch(kind) {
      case EcsBool: return ECS_ALIGNOF(bool);
      case EcsChar: return ECS_ALIGNOF(char);
      case EcsByte: return ECS_ALIGNOF(char);
      case EcsU8: return ECS_ALIGNOF(uint8_t);
      case EcsU16: return ECS_ALIGNOF(uint16_t);
      case EcsU32: return ECS_ALIGNOF(uint32_t);
      case EcsU64: return ECS_ALIGNOF(uint64_t);
      case EcsI8: return ECS_ALIGNOF(int8_t);
      case EcsI16: return ECS_ALIGNOF(int16_t);
      case EcsI32: return ECS_ALIGNOF(int32_t);
      case EcsI64: return ECS_ALIGNOF(int64_t);
      case EcsF32: return ECS_ALIGNOF(float);
      case EcsF64: return ECS_ALIGNOF(double);
      case EcsIPtr: return ECS_ALIGNOF(intptr_t);
      case EcsUPtr: return ECS_ALIGNOF(uintptr_t);
      case EcsString: return ECS_ALIGNOF(char*);
      case EcsEntity: return ECS_ALIGNOF(ecs_entity_t);
      default:
          ecs_abort(ECS_INTERNAL_ERROR, NULL);
      }
  }
  
  static
  ecs_vector_t* serialize_primitive(
      ecs_world_t *world,
      ecs_entity_t entity,
      const EcsPrimitive *type,
      ecs_vector_t *ops,
      FlecsMeta *module)
  {
      (void)world;
      (void)entity;
      (void)module;
  
      ecs_type_op_t *op;
      if (!ops) {
          op = ecs_vector_add(&ops, ecs_type_op_t);
          *op = (ecs_type_op_t) {
              .kind = EcsOpHeader,
              .size = ecs_get_primitive_size(type->kind),
              .alignment = ecs_get_primitive_alignment(type->kind)
          };
      }
  
      op = ecs_vector_add(&ops, ecs_type_op_t);
  
      *op = (ecs_type_op_t) {
          .kind = EcsOpPrimitive,
          .size = ecs_get_primitive_size(type->kind),
          .alignment = ecs_get_primitive_alignment(type->kind),
          .count = 1,
          .is.primitive = type->kind
      };
  
      return ops;
  }
  
  static
  ecs_vector_t* serialize_enum(
      ecs_world_t *world,
      ecs_entity_t entity,
      const EcsEnum *type,
      ecs_vector_t *ops,
      FlecsMeta *module)
  {    
      (void)type;
  
      FlecsMetaImportHandles(*module);
  
      ecs_type_op_t *op;
      if (!ops) {
          op = ecs_vector_add(&ops, ecs_type_op_t);
          *op = (ecs_type_op_t) {
              .kind = EcsOpHeader,
              .size = sizeof(int32_t),
              .alignment = ECS_ALIGNOF(int32_t)
          };
      }
  
      op = ecs_vector_add(&ops, ecs_type_op_t);
  
      ecs_ref_t ref = {0};
      ecs_get_ref(world, &ref, entity, EcsEnum);
  
      *op = (ecs_type_op_t) {
          .kind = EcsOpEnum,
          .size = sizeof(int32_t),
          .alignment = ECS_ALIGNOF(int32_t),
          .count = 1,
          .is.constant = ref
      };
  
      return ops;
  }
  
  static
  ecs_vector_t* serialize_bitmask(
      ecs_world_t *world,
      ecs_entity_t entity,
      const EcsBitmask *type,
      ecs_vector_t *ops,
      FlecsMeta *module)
  {    
      (void)type;
  
      FlecsMetaImportHandles(*module);
  
      ecs_type_op_t *op;
      if (!ops) {
          op = ecs_vector_add(&ops, ecs_type_op_t);
          *op = (ecs_type_op_t) {
              .kind = EcsOpHeader,
              .size = sizeof(int32_t),
              .alignment = ECS_ALIGNOF(int32_t)
          };
      }
  
      op = ecs_vector_add(&ops, ecs_type_op_t);
  
      ecs_ref_t ref = {0};
      ecs_get_ref(world, &ref, entity, EcsBitmask);
  
      *op = (ecs_type_op_t) {
          .kind = EcsOpBitmask,
          .size = sizeof(int32_t),
          .alignment = ECS_ALIGNOF(int32_t),
          .count = 1,
          .is.constant = ref
      };
  
      return ops;
  }
  
  static
  ecs_vector_t* serialize_struct(
      ecs_world_t *world,
      ecs_entity_t entity,
      const EcsStruct *type,
      ecs_vector_t *ops,
      int32_t offset,
      FlecsMeta *module)
  {
      FlecsMetaImportHandles(*module);
  
      ecs_type_op_t *op_header = NULL;
      if (!ops) {
          op_header = ecs_vector_add(&ops, ecs_type_op_t);
      }
  
      int32_t push_op = ecs_vector_count(ops);
  
      ecs_type_op_t *op = ecs_vector_add(&ops, ecs_type_op_t);
      *op = (ecs_type_op_t) {
          .kind = EcsOpPush
      };
  
      ecs_size_t size = 0;
      int16_t alignment = 0;
  
      EcsMember *members = ecs_vector_first(type->members, EcsMember);
      int32_t i, count = ecs_vector_count(type->members);
  
      for (i = 0; i < count; i ++) {
          /* Add type operations of member to struct ops */
          int32_t prev_count = ecs_vector_count(ops);
          ops = serialize_type(world, members[i].type, ops, offset + size, module);
          int32_t op_count = ecs_vector_count(ops);
  
          /* At least one op should be added */
          ecs_assert(prev_count != op_count, ECS_INTERNAL_ERROR, NULL);
          ecs_assert(ops != NULL, ECS_INTERNAL_ERROR, NULL);
  
          op = ecs_vector_get(ops, ecs_type_op_t, prev_count);
          op->name = members[i].name;
  
          const EcsMetaType *meta_type = ecs_get(world, members[i].type, EcsMetaType);
          ecs_size_t member_size = meta_type->size * op->count;
          int16_t member_alignment = meta_type->alignment;
  
          ecs_assert(member_size != 0, ECS_INTERNAL_ERROR, op->name);
          ecs_assert(member_alignment != 0, ECS_INTERNAL_ERROR, op->name);
  
          size = ECS_ALIGN(size, member_alignment);
          op->offset = offset + size;
  
          size += member_size;
  
          if (member_alignment > alignment) {
              alignment = member_alignment;
          }
      }
  
      /* Align struct size to struct alignment */
      size = ECS_ALIGN(size, alignment);
  
      /* Size and alignment are ordinarily determined by ECS_STRUCT and should be
       * the same as the values computed here. However, there are two exceptions.
       * The first exception is when an application defines a type by populating
       * the EcsStruct component directly and does not provide size and alignment
       * values for EcsMetaType. The second scenario is when the type definition 
       * contains an ECS_PRIVATE, in which case the type may contain
       * members that are not described.
       *
       * In the first case the computed values should be set in EcsMetaType. In the
       * second case the values from EcsMetaType should be assigned to the type
       * operation. */
      bool is_added;
      EcsMetaType *base_type = ecs_get_mut(world, entity, EcsMetaType, &is_added);
      ecs_assert(base_type != NULL, ECS_INTERNAL_ERROR, NULL);
      if (!is_added) {
          if (!type->is_partial) {
              /* EcsMetaType existed already, and this is not a partial type. This
               * means that computed size and alignment should match exactly. */
              if (base_type->size) {
                  ecs_assert(base_type->size == size, ECS_INTERNAL_ERROR, NULL);
              }
  
              if (base_type->alignment) {
                  ecs_assert(
                      base_type->alignment == alignment, ECS_INTERNAL_ERROR, NULL);
              }
          } else {
              /* EcsMetaType exists, and this is a partial type. In this case the
               * computed values only apply to the members described in EcsStruct
               * but not to the type as a whole. Use the values from EcsMetaType. Note
               * that it is not allowed to have a partial type for which no size
               * and alignment are specified in EcsMetaType. */
              ecs_assert(base_type->size != 0, ECS_INVALID_PARAMETER, NULL);
              ecs_assert(base_type->alignment != 0, ECS_INVALID_PARAMETER, NULL);
  
              size = base_type->size;
              alignment = base_type->alignment;
          }
      } else {
          /* If EcsMetaType was not set yet, initialize descriptor to NULL since it
           * it won't be used here */
          base_type->descriptor = NULL;
      }
  
      base_type->kind = EcsStructType;
      base_type->size = size;
      base_type->alignment = alignment;
  
      op = ecs_vector_add(&ops, ecs_type_op_t);
      *op = (ecs_type_op_t) {
          .kind = EcsOpPop,
      };
  
      if (op_header) {
          op_header = ecs_vector_first(ops, ecs_type_op_t);
          *op_header = (ecs_type_op_t) {
              .kind = EcsOpHeader,
              .size = size,
              .alignment = alignment
          };
      }
  
      ecs_type_op_t *op_push = ecs_vector_get(ops, ecs_type_op_t, push_op);
      ecs_assert(op_push->kind == EcsOpPush, ECS_INTERNAL_ERROR, NULL);
      op_push->size = size;
      op_push->alignment = alignment;
      op_push->count = 1;
  
      return ops;
  }
  
  static
  ecs_vector_t* serialize_array(
      ecs_world_t *world,
      ecs_entity_t entity,
      const EcsArray *type,
      ecs_vector_t *ops,
      FlecsMeta *handles)
  {
      (void)entity;
  
      FlecsMetaImportHandles(*handles);
  
      ecs_type_op_t *op_header = NULL;
      if (!ops) {
          op_header = ecs_vector_add(&ops, ecs_type_op_t);
      }
  
      const EcsMetaType *element_type = ecs_get(world, type->element_type, EcsMetaType);
      ecs_assert(element_type != NULL, ECS_INTERNAL_ERROR, NULL);
  
      ecs_ref_t ref = {0};
      ecs_get_ref(world, &ref, type->element_type, EcsMetaTypeSerializer);
  
      ecs_type_op_t *op = ecs_vector_add(&ops, ecs_type_op_t);
      *op = (ecs_type_op_t){
          .kind = EcsOpArray, 
          .count = type->count,
          .size = element_type->size,
          .alignment = element_type->alignment,
          .is.collection = ref
      };
  
      if (op_header) {
          op_header = ecs_vector_first(ops, ecs_type_op_t);
          *op_header = (ecs_type_op_t) {
              .kind = EcsOpHeader,
              .size = op->size,
              .alignment = op->alignment
          };            
      }        
  
      return ops;
  }
  
  static
  ecs_vector_t* serialize_vector(
      ecs_world_t *world,
      ecs_entity_t entity,
      const EcsVector *type,
      ecs_vector_t *ops,
      FlecsMeta *handles)
  {
      (void)entity;
  
      FlecsMetaImportHandles(*handles);
  
      ecs_type_op_t *op = NULL;
      if (!ops) {
          op = ecs_vector_add(&ops, ecs_type_op_t);
          *op = (ecs_type_op_t) {
              .kind = EcsOpHeader,
              .size = sizeof(ecs_vector_t*),
              .alignment = ECS_ALIGNOF(ecs_vector_t*)
          };         
      }
  
      const EcsMetaType *element_type = ecs_get(world, type->element_type, EcsMetaType);
      ecs_assert(element_type != NULL, ECS_INTERNAL_ERROR, NULL);
  
      ecs_ref_t ref = {0};
      ecs_get_ref(world, &ref, type->element_type, EcsMetaTypeSerializer);
  
      op = ecs_vector_add(&ops, ecs_type_op_t);
      *op = (ecs_type_op_t){
          .kind = EcsOpVector, 
          .count = 1,
          .size = element_type->size,
          .alignment = element_type->alignment,
          .is.collection = ref
      };
  
      return ops;
  }
  
  static
  ecs_vector_t* serialize_map(
      ecs_world_t *world,
      ecs_entity_t entity,
      const EcsMap *type,
      ecs_vector_t *ops,
      FlecsMeta *handles)
  {
      FlecsMetaImportHandles(*handles);
  
      ecs_type_op_t *op = NULL;
      if (!ops) {
          op = ecs_vector_add(&ops, ecs_type_op_t);
          *op = (ecs_type_op_t) {
              .kind = EcsOpHeader,
              .size = sizeof(ecs_map_t*),
              .alignment = ECS_ALIGNOF(ecs_map_t*)
          };         
      }
  
      const EcsMetaTypeSerializer *key_cache = ecs_get(world, type->key_type, EcsMetaTypeSerializer);
      ecs_assert(key_cache != NULL, ECS_INTERNAL_ERROR, NULL);
      ecs_assert(key_cache->ops != NULL, ECS_INTERNAL_ERROR, NULL);
      ecs_assert(ecs_vector_count(key_cache->ops) != 0, ECS_INTERNAL_ERROR, NULL);
  
      /* Make sure first op is the header */
      ecs_type_op_t *key_op = ecs_vector_first(key_cache->ops, ecs_type_op_t);
      ecs_assert(key_op->kind == EcsOpHeader, ECS_INTERNAL_ERROR, NULL);
  
      if (ecs_vector_count(key_cache->ops) != 2) {
          const EcsMetaType *ptr = ecs_get(world, entity, EcsMetaType);
          ecs_assert(ptr != NULL, ECS_INTERNAL_ERROR, NULL);
  
          ecs_meta_parse_ctx_t ctx = {
              .name = ecs_get_name(world, entity),
              .decl = ptr->descriptor
          };
  
          ecs_meta_error( &ctx, ctx.decl, 
              "invalid key type '%s' for map", ecs_get_name(world, type->key_type));
      }
  
      key_op = ecs_vector_get(key_cache->ops, ecs_type_op_t, 1);
      ecs_assert(key_op != NULL, ECS_INTERNAL_ERROR, NULL);
  
      if (key_op->count != 1) {
          const EcsMetaType *ptr = ecs_get(world, entity, EcsMetaType);
          ecs_assert(ptr != NULL, ECS_INTERNAL_ERROR, NULL);
  
          ecs_meta_parse_ctx_t ctx = {
              .name = ecs_get_name(world, entity),
              .decl = ptr->descriptor
          };        
          ecs_meta_error( &ctx, ctx.decl, "array type invalid for key type");
      }
  
      ecs_ref_t key_ref = {0};
      ecs_get_ref(world, &key_ref, type->key_type, EcsMetaTypeSerializer);
  
      ecs_ref_t element_ref = {0};
      ecs_get_ref(world, &element_ref, type->element_type, EcsMetaTypeSerializer);
  
      op = ecs_vector_add(&ops, ecs_type_op_t);
      *op = (ecs_type_op_t){
          .kind = EcsOpMap, 
          .count = 1,
          .size = sizeof(ecs_map_t*),
          .alignment = ECS_ALIGNOF(ecs_map_t*),
          .is.map = {
              .key = key_ref,
              .element = element_ref
          }
      };
  
      return ops;
  }
  
  static
  ecs_vector_t* serialize_type(
      ecs_world_t *world,
      ecs_entity_t entity,
      ecs_vector_t *ops,
      int32_t offset,
      FlecsMeta *module)
  {
      FlecsMetaImportHandles(*module);
  
      const EcsMetaType *type = ecs_get(world, entity, EcsMetaType);
      ecs_assert(type != NULL, ECS_INVALID_PARAMETER, NULL);
  
      switch(type->kind) {
      case EcsPrimitiveType: {
          const EcsPrimitive *t = ecs_get(world, entity, EcsPrimitive);
          ecs_assert(t != NULL, ECS_INTERNAL_ERROR, NULL);
          return serialize_primitive(world, entity, t, ops, module);
      }
  
      case EcsEnumType: {
          const EcsEnum *t = ecs_get(world, entity, EcsEnum);
          ecs_assert(t != NULL, ECS_INTERNAL_ERROR, NULL);
          return serialize_enum(world, entity, t, ops, module);
      }
  
      case EcsBitmaskType: {
          const EcsBitmask *t = ecs_get(world, entity, EcsBitmask);
          ecs_assert(t != NULL, ECS_INTERNAL_ERROR, NULL);
          return serialize_bitmask(world, entity, t, ops, module);
      }    
  
      case EcsStructType: {
          const EcsStruct *t = ecs_get(world, entity, EcsStruct);
          ecs_assert(t != NULL, ECS_INTERNAL_ERROR, NULL);
          return serialize_struct(world, entity, t, ops, offset, module);
      }    
  
      case EcsArrayType: {
          const EcsArray *t = ecs_get(world, entity, EcsArray);
          ecs_assert(t != NULL, ECS_INTERNAL_ERROR, NULL);
          return serialize_array(world, entity, t, ops, module);
      }
  
      case EcsVectorType: {
          const EcsVector *t = ecs_get(world, entity, EcsVector);
          ecs_assert(t != NULL, ECS_INTERNAL_ERROR, NULL);
          return serialize_vector(world, entity, t, ops, module);
      }    
  
      case EcsMapType: {
          const EcsMap *t = ecs_get(world, entity, EcsMap);
          ecs_assert(t != NULL, ECS_INTERNAL_ERROR, NULL);
          return serialize_map(world, entity, t, ops, module);
      }
  
      default:
          break;
      }
  
      return NULL;
  }
  
  void EcsSetPrimitive(ecs_iter_t *it) {
      EcsPrimitive *type = ecs_column(it, EcsPrimitive, 1);
      ECS_IMPORT_COLUMN(it, FlecsMeta, 2);
  
      ecs_world_t *world = it->world;
  
      int i;
      for (i = 0; i < it->count; i ++) {
          ecs_entity_t e = it->entities[i];
  
          /* Size and alignment for primitive types can only be set after we know
           * what kind of primitive type it is. Set values in case they haven't
           * been set already */
          bool is_added;
          EcsMetaType *base_type = ecs_get_mut(world, e, EcsMetaType, &is_added);
          ecs_assert(base_type != NULL, ECS_INTERNAL_ERROR, NULL);
          
          base_type->size = ecs_get_primitive_size(type[i].kind);
          base_type->alignment = ecs_get_primitive_alignment(type[i].kind);
  
          ecs_set(world, e, EcsMetaTypeSerializer, {
              serialize_primitive(
                  world, e, &type[i], NULL, &ecs_module(FlecsMeta))
          });
      }
  }
  
  void EcsSetEnum(ecs_iter_t *it) {
      EcsEnum *type = ecs_column(it, EcsEnum, 1);
      ECS_IMPORT_COLUMN(it, FlecsMeta, 2);
  
      ecs_world_t *world = it->world;
  
      int i;
      for (i = 0; i < it->count; i ++) {
          ecs_entity_t e = it->entities[i];
  
          ecs_set(it->world, e, EcsMetaTypeSerializer, { 
              serialize_enum(world, e, &type[i], NULL, &ecs_module(FlecsMeta))
          });
      }
  }
  
  void EcsSetBitmask(ecs_iter_t *it) {
      EcsBitmask *type = ecs_column(it, EcsBitmask, 1);
      ECS_IMPORT_COLUMN(it, FlecsMeta, 2);
  
      ecs_world_t *world = it->world;
  
      int i;
      for (i = 0; i < it->count; i ++) {
          ecs_entity_t e = it->entities[i];
          ecs_set(it->world, e, EcsMetaTypeSerializer, { 
              serialize_bitmask(world, e, &type[i], NULL, &ecs_module(FlecsMeta))
          });
      }
  }
  
  void EcsSetStruct(ecs_iter_t *it) {
      EcsStruct *type = ecs_column(it, EcsStruct, 1);
      ECS_IMPORT_COLUMN(it, FlecsMeta, 2);
  
      ecs_world_t *world = it->world;
  
      int i;
      for (i = 0; i < it->count; i ++) {
          ecs_entity_t e = it->entities[i];
          ecs_set(it->world, e, EcsMetaTypeSerializer, { 
              serialize_struct(world, e, &type[i], NULL, 0, &ecs_module(FlecsMeta))
          });
      }
  }
  
  void EcsSetArray(ecs_iter_t *it) {
      EcsArray *type = ecs_column(it, EcsArray, 1);
      ECS_IMPORT_COLUMN(it, FlecsMeta, 2);
  
      ecs_world_t *world = it->world;
  
      int i;
      for (i = 0; i < it->count; i ++) {
          ecs_entity_t e = it->entities[i];
          ecs_set(it->world, e, EcsMetaTypeSerializer, { 
              serialize_array(world, e, &type[i], NULL, &ecs_module(FlecsMeta))
          });
      }
  }
  
  void EcsSetVector(ecs_iter_t *it) {
      EcsVector *type = ecs_column(it, EcsVector, 1);
      ECS_IMPORT_COLUMN(it, FlecsMeta, 2);
  
      ecs_world_t *world = it->world;
  
      int i;
      for (i = 0; i < it->count; i ++) {
          ecs_entity_t e = it->entities[i];
          ecs_set(it->world, e, EcsMetaTypeSerializer, { 
              serialize_vector(world, e, &type[i], NULL, &ecs_module(FlecsMeta))
          });
      }
  }
  
  void EcsSetMap(ecs_iter_t *it) {
      EcsMap *type = ecs_column(it, EcsMap, 1);
      ECS_IMPORT_COLUMN(it, FlecsMeta, 2);
  
      ecs_world_t *world = it->world;
  
      int i;
      for (i = 0; i < it->count; i ++) {
          ecs_entity_t e = it->entities[i];
          ecs_set(it->world, e, EcsMetaTypeSerializer, { 
              serialize_map(world, e, &type[i], NULL, &ecs_module(FlecsMeta))
          });
      }
  }
  
  char* ecs_chresc(
      char *out, 
      char in, 
      char delimiter) 
  {
      char *bptr = out;
      switch(in) {
      case '\a':
          *bptr++ = '\\';
          *bptr = 'a';
          break;
      case '\b':
          *bptr++ = '\\';
          *bptr = 'b';
          break;
      case '\f':
          *bptr++ = '\\';
          *bptr = 'f';
          break;
      case '\n':
          *bptr++ = '\\';
          *bptr = 'n';
          break;
      case '\r':
          *bptr++ = '\\';
          *bptr = 'r';
          break;
      case '\t':
          *bptr++ = '\\';
          *bptr = 't';
          break;
      case '\v':
          *bptr++ = '\\';
          *bptr = 'v';
          break;
      case '\\':
          *bptr++ = '\\';
          *bptr = '\\';
          break;
      default:
          if (in == delimiter) {
              *bptr++ = '\\';
              *bptr = delimiter;
          } else {
              *bptr = in;
          }
          break;
      }
  
      *(++bptr) = '\0';
  
      return bptr;
  }
  
  const char* ecs_chrparse(
      const char *in, 
      char *out) 
  {
      const char *result = in + 1;
      char ch;
  
      if (in[0] == '\\') {
          result ++;
  
          switch(in[1]) {
          case 'a':
              ch = '\a';
              break;
          case 'b':
              ch = '\b';
              break;
          case 'f':
              ch = '\f';
              break;
          case 'n':
              ch = '\n';
              break;
          case 'r':
              ch = '\r';
              break;
          case 't':
              ch = '\t';
              break;
          case 'v':
              ch = '\v';
              break;
          case '\\':
              ch = '\\';
              break;
          case '"':
              ch = '"';
              break;
          case '0':
              ch = '\0';
              break;
          case ' ':
              ch = ' ';
              break;
          case '$':
              ch = '$';
              break;
          default:
              goto error;
          }
      } else {
          ch = in[0];
      }
  
      if (out) {
          *out = ch;
      }
  
      return result;
  error:
      return NULL;
  }
  
  ecs_size_t ecs_stresc(
      char *out, 
      ecs_size_t n, 
      char delimiter, 
      const char *in) 
  {
      const char *ptr = in;
      char ch, *bptr = out, buff[3];
      ecs_size_t written = 0;
      while ((ch = *ptr++)) {
          if ((written += (ecs_size_t)(ecs_chresc(buff, ch, delimiter) - buff)) <= n) {
              *bptr++ = buff[0];
              if ((ch = buff[1])) {
                  *bptr = ch;
                  bptr++;
              }
          }
      }
  
      if (bptr) {
          while (written < n) {
              *bptr = '\0';
              bptr++;
              written++;
          }
      }
      return written;
  }
  
  /* Simple serializer to turn values into strings. Use this code as a template
   * for when implementing a new serializer. */
  
  static
  int str_ser_type(
      ecs_world_t *world,
      ecs_vector_t *ser, 
      const void *base, 
      ecs_strbuf_t *str);
  
  static
  int str_ser_type_op(
      ecs_world_t *world,
      ecs_type_op_t *op, 
      const void *base,
      ecs_strbuf_t *str);
  
  /* Serialize a primitive value */
  static
  void str_ser_primitive(
      ecs_world_t *world,
      ecs_type_op_t *op, 
      const void *base, 
      ecs_strbuf_t *str) 
  {
      const char *bool_str[] = { "false", "true" };
  
      switch(op->is.primitive) {
      case EcsBool:
          ecs_strbuf_appendstr(str, bool_str[(int)*(bool*)base]);
          break;
      case EcsChar: {
          char chbuf[3];
          ecs_chresc(chbuf, *(char*)base, '\'');
  
          ecs_strbuf_appendstrn(str, "'", 1);
          ecs_strbuf_appendstr(str, chbuf);
          ecs_strbuf_appendstrn(str, "'", 1);
          break;
      }
      case EcsByte:
          ecs_strbuf_append(str, "0x%x", *(uint8_t*)base);
          break;
      case EcsU8:
          ecs_strbuf_append(str, "%u", *(uint8_t*)base);
          break;
      case EcsU16:
          ecs_strbuf_append(str, "%u", *(uint16_t*)base);
          break;
      case EcsU32:
          ecs_strbuf_append(str, "%u", *(uint32_t*)base);
          break;
      case EcsU64:
          ecs_strbuf_append(str, "%llu", *(uint64_t*)base);
          break;
      case EcsI8:
          ecs_strbuf_append(str, "%d", *(int8_t*)base);
          break;
      case EcsI16:
          ecs_strbuf_append(str, "%d", *(int16_t*)base);
          break;
      case EcsI32:
          ecs_strbuf_append(str, "%d", *(int32_t*)base);
          break;
      case EcsI64:
          ecs_strbuf_append(str, "%lld", *(int64_t*)base);
          break;
      case EcsF32:
          ecs_strbuf_append(str, "%f", *(float*)base);
          break;
      case EcsF64:
          ecs_strbuf_append(str, "%f", *(double*)base);
          break;
      case EcsIPtr:
          ecs_strbuf_append(str, "%i", *(intptr_t*)base);
          break;
      case EcsUPtr:
          ecs_strbuf_append(str, "%u", *(uintptr_t*)base);
          break;
      case EcsString: {
          char *value = *(char**)base;
          if (value) {
              ecs_size_t length = ecs_stresc(NULL, 0, '"', value);
              if (length == ecs_os_strlen(value)) {
                  ecs_strbuf_appendstrn(str, "\"", 1);
                  ecs_strbuf_appendstr(str, value);
                  ecs_strbuf_appendstrn(str, "\"", 1);
              } else {
                  char *out = ecs_os_malloc(length + 3);
                  ecs_stresc(out + 1, length, '"', value);
                  out[0] = '"';
                  out[length + 1] = '"';
                  out[length + 2] = '\0';
                  ecs_strbuf_appendstr_zerocpy(str, out);
              }
          } else {
              ecs_strbuf_appendstr(str, "nullptr");
          }
          break;
      }
      case EcsEntity: {
          ecs_entity_t e = *(ecs_entity_t*)base;
          const char *name = ecs_get_name(world, e);
          if (name) {
              ecs_strbuf_appendstr(str, name);
          } else {
              ecs_strbuf_append(str, "%u", e);
          }
          break;
      }
      }
  }
  
  /* Serialize enumeration */
  static
  int str_ser_enum(
      ecs_world_t *world,
      ecs_type_op_t *op, 
      const void *base, 
      ecs_strbuf_t *str) 
  {
      const EcsEnum *enum_type = ecs_get_ref_w_entity(world, &op->is.constant, 0, 0);
      ecs_assert(enum_type != NULL, ECS_INVALID_PARAMETER, NULL);
  
      int32_t value = *(int32_t*)base;
      
      /* Enumeration constants are stored in a map that is keyed on the
       * enumeration value. */
      char **constant = ecs_map_get(enum_type->constants, char*, value);
      if (!constant) {
          return -1;
      }
  
      ecs_strbuf_appendstr(str, *constant);
  
      return 0;
  }
  
  /* Serialize bitmask */
  static
  int str_ser_bitmask(
      ecs_world_t *world,
      ecs_type_op_t *op, 
      const void *base, 
      ecs_strbuf_t *str) 
  {
      const EcsBitmask *bitmask_type = ecs_get_ref_w_entity(world, &op->is.constant, 0, 0);
      ecs_assert(bitmask_type != NULL, ECS_INVALID_PARAMETER, NULL);
  
      uint32_t value = *(uint32_t*)base;
      ecs_map_key_t key;
      char **constant;
      int count = 0;
  
      ecs_strbuf_list_push(str, "", " | ");
  
      /* Multiple flags can be set at a given time. Iterate through all the flags
       * and append the ones that are set. */
      ecs_map_iter_t it = ecs_map_iter(bitmask_type->constants);
      while ((constant = ecs_map_next(&it, char*, &key))) {
          if ((value & key) == key) {
              ecs_strbuf_list_appendstr(str, *constant);
              count ++;
          }
      }
  
      if (!count) {
          ecs_strbuf_list_appendstr(str, "0");
      }
  
      ecs_strbuf_list_pop(str, "");
  
      return 0;
  }
  
  /* Serialize elements of a contiguous array */
  static
  int str_ser_elements(
      ecs_world_t *world,
      ecs_vector_t *elem_ops, 
      const void *base, 
      int32_t elem_count, 
      int32_t elem_size,
      ecs_strbuf_t *str)
  {
      ecs_strbuf_list_push(str, "[", ", ");
  
      const void *ptr = base;
  
      int i;
      for (i = 0; i < elem_count; i ++) {
          ecs_strbuf_list_next(str);
          if (str_ser_type(world, elem_ops, ptr, str)) {
              return -1;
          }
          ptr = ECS_OFFSET(ptr, elem_size);
      }
  
      ecs_strbuf_list_pop(str, "]");
  
      return 0;
  }
  
  /* Serialize array */
  static
  int str_ser_array(
      ecs_world_t *world,
      ecs_type_op_t *op, 
      const void *base, 
      ecs_strbuf_t *str) 
  {
      const EcsMetaTypeSerializer *ser = ecs_get_ref_w_entity(world, &op->is.collection, 0, 0);
      ecs_assert(ser != NULL, ECS_INTERNAL_ERROR, NULL);
  
      return str_ser_elements(
          world, ser->ops, base, op->count, op->size, str);
  }
  
  /* Serialize vector */
  static
  int str_ser_vector(
      ecs_world_t *world,
      ecs_type_op_t *op, 
      const void *base, 
      ecs_strbuf_t *str) 
  {
      ecs_vector_t *value = *(ecs_vector_t**)base;
      if (!value) {
          ecs_strbuf_appendstr(str, "nullptr");
          return 0;
      }
      
      const EcsMetaTypeSerializer *ser = ecs_get_ref_w_entity(world, &op->is.collection, 0, 0);
      ecs_assert(ser != NULL, ECS_INTERNAL_ERROR, NULL);
  
      int32_t count = ecs_vector_count(value);
      void *array = ecs_vector_first_t(value, op->size, op->alignment);
      ecs_vector_t *elem_ops = ser->ops;
      
      ecs_type_op_t *elem_op_hdr = (ecs_type_op_t*)ecs_vector_first(elem_ops, ecs_type_op_t);
      ecs_assert(elem_op_hdr != NULL, ECS_INTERNAL_ERROR, NULL);
      ecs_assert(elem_op_hdr->kind == EcsOpHeader, ECS_INTERNAL_ERROR, NULL);
      ecs_size_t elem_size = elem_op_hdr->size;
  
      /* Serialize contiguous buffer of vector */
      return str_ser_elements(world, elem_ops, array, count, elem_size, str);
  }
  
  /* Serialize map */
  static
  int str_ser_map(
      ecs_world_t *world,
      ecs_type_op_t *op, 
      const void *base, 
      ecs_strbuf_t *str) 
  {
      ecs_map_t *value = *(ecs_map_t**)base;
  
      const EcsMetaTypeSerializer *key_ser = ecs_get_ref_w_entity(world, &op->is.map.key, 0, 0);
      ecs_assert(key_ser != NULL, ECS_INTERNAL_ERROR, NULL);
  
      const EcsMetaTypeSerializer *elem_ser = ecs_get_ref_w_entity(world, &op->is.map.element, 0, 0);
      ecs_assert(elem_ser != NULL, ECS_INTERNAL_ERROR, NULL);
  
      /* 2 instructions, one for the header */
      ecs_assert(ecs_vector_count(key_ser->ops) == 2, ECS_INTERNAL_ERROR, NULL);
  
      ecs_type_op_t *key_op = ecs_vector_first(key_ser->ops, ecs_type_op_t);
      ecs_assert(key_op->kind == EcsOpHeader, ECS_INTERNAL_ERROR, NULL);
      key_op = &key_op[1];
  
      ecs_map_iter_t it = ecs_map_iter(value);  
      ecs_map_key_t key; 
      void *ptr;
  
      ecs_strbuf_list_push(str, "{", ", ");
  
      while ((ptr = _ecs_map_next(&it, 0, &key))) {
          ecs_strbuf_list_next(str);
          if (str_ser_type_op(world, key_op, (void*)&key, str)) {
              return -1;
          }
  
          ecs_strbuf_appendstr(str, " = ");
          
          if (str_ser_type(world, elem_ser->ops, ptr, str)) {
              return -1;
          }
  
          key = 0;
      }
  
      ecs_strbuf_list_pop(str, "}");
  
      return 0;
  }
  
  /* Forward serialization to the different type kinds */
  static
  int str_ser_type_op(
      ecs_world_t *world,
      ecs_type_op_t *op, 
      const void *base,
      ecs_strbuf_t *str) 
  {
      switch(op->kind) {
      case EcsOpHeader:
      case EcsOpPush:
      case EcsOpPop:
          /* Should not be parsed as single op */
          ecs_abort(ECS_INVALID_PARAMETER, NULL);
          break;
      case EcsOpPrimitive:
          str_ser_primitive(world, op, ECS_OFFSET(base, op->offset), str);
          break;
      case EcsOpEnum:
          if (str_ser_enum(world, op, ECS_OFFSET(base, op->offset), str)) {
              return -1;
          }
          break;
      case EcsOpBitmask:
          if (str_ser_bitmask(world, op, ECS_OFFSET(base, op->offset), str)) {
              return -1;
          }
          break;
      case EcsOpArray:
          if (str_ser_array(world, op, ECS_OFFSET(base, op->offset), str)) {
              return -1;
          }
          break;
      case EcsOpVector:
          if (str_ser_vector(world, op, ECS_OFFSET(base, op->offset), str)) {
              return -1;
          }
          break;
      case EcsOpMap:
          if (str_ser_map(world, op, ECS_OFFSET(base, op->offset), str)) {
              return -1;
          }
          break;
      }
  
      return 0;
  }
  
  /* Iterate over the type ops of a type */
  static
  int str_ser_type(
      ecs_world_t *world,
      ecs_vector_t *ser, 
      const void *base, 
      ecs_strbuf_t *str) 
  {
      ecs_type_op_t *ops = (ecs_type_op_t*)ecs_vector_first(ser, ecs_type_op_t);
      int32_t count = ecs_vector_count(ser);
  
      for (int i = 0; i < count; i ++) {
          ecs_type_op_t *op = &ops[i];
  
          if (op->name) {
              if (op->kind != EcsOpHeader)
              {
                  ecs_strbuf_list_next(str);
              }
  
              ecs_strbuf_append(str, "%s = ", op->name);
          }
  
          switch(op->kind) {
          case EcsOpHeader:
              break;
          case EcsOpPush:
              ecs_strbuf_list_push(str, "{", ", ");
              break;
          case EcsOpPop:
              ecs_strbuf_list_pop(str, "}");
              break;
          default:
              if (str_ser_type_op(world, op, base, str)) {
                  goto error;
              }
              break;
          }
      }
  
      return 0;
  error:
      ecs_strbuf_reset(str);
      return -1;
  }
  
  char* ecs_ptr_to_str(
      ecs_world_t *world, 
      ecs_entity_t type, 
      void* ptr)
  {
      ecs_entity_t ecs_entity(EcsMetaTypeSerializer) = ecs_lookup_fullpath(world, "flecs.meta.MetaTypeSerializer");
      const EcsMetaTypeSerializer *ser = ecs_get(world, type, EcsMetaTypeSerializer);
      ecs_assert(ser != NULL, ECS_INVALID_PARAMETER, NULL);
  
      ecs_strbuf_t str = ECS_STRBUF_INIT;
      if (str_ser_type(world, ser->ops, ptr, &str)) {
          return NULL;
      }
  
      return ecs_strbuf_get(&str);
  }
  
  char* ecs_entity_to_str(
      ecs_world_t *world, 
      ecs_entity_t entity)
  {
      ecs_type_t type = ecs_get_type(world, entity);
      ecs_entity_t *ids = (ecs_entity_t*)ecs_vector_first(type, ecs_entity_t);
      int32_t count = ecs_vector_count(type);
      
      ecs_entity_t ecs_entity(EcsMetaTypeSerializer) = ecs_lookup_fullpath(world, "flecs.meta.MetaTypeSerializer");
      ecs_strbuf_t str = ECS_STRBUF_INIT;
  
      const char *name = ecs_get_name(world, entity);
      if (name) {
          ecs_strbuf_append(&str, "%s: ", name);
      }
  
      ecs_strbuf_appendstr(&str, "{\n");
  
      int i, comps_serialized = 0;
      for (i = 0; i < count; i ++) {
          const EcsMetaTypeSerializer *ser = ecs_get(world, ids[i], EcsMetaTypeSerializer);
          if (ser) {
              const void *ptr = ecs_get_w_entity(world, entity, ids[i]);
              ecs_strbuf_append(&str, "    %s: ", ecs_get_name(world, ids[i]));
              if (str_ser_type(world, ser->ops, ptr, &str)) {
                  goto error;
              }
  
              ecs_strbuf_appendstr(&str, "\n");
              comps_serialized ++;
          }
      }
  
      ecs_strbuf_appendstr(&str, "}");
  
      return ecs_strbuf_get(&str);  
  error:
      ecs_strbuf_reset(&str);
      return NULL;
  }
  #ifndef FLECS_META_SERIALIZER_H
  #define FLECS_META_SERIALIZER_H
  
  
  void EcsAddStruct(
      ecs_iter_t *it);
  
  void EcsSetPrimitive(
      ecs_iter_t *it);
  
  void EcsSetEnum(
      ecs_iter_t *it);
  
  void EcsSetBitmask(
      ecs_iter_t *it);
  
  void EcsSetStruct(
      ecs_iter_t *it);
  
  void EcsSetArray(
      ecs_iter_t *it);
  
  void EcsSetVector(
      ecs_iter_t *it);
  
  void EcsSetMap(
      ecs_iter_t *it);
  
  #endif
  #ifndef FLECS_META_TYPE_H
  #define FLECS_META_TYPE_H
  
  
  ecs_entity_t ecs_meta_lookup_array(
      ecs_world_t *world,
      ecs_entity_t e,
      const char *params_decl,
      ecs_meta_parse_ctx_t *ctx);
  
  ecs_entity_t ecs_meta_lookup_vector(
      ecs_world_t *world,
      ecs_entity_t e,
      const char *params_decl,
      ecs_meta_parse_ctx_t *ctx);
  
  ecs_entity_t ecs_meta_lookup_map(
      ecs_world_t *world,
      ecs_entity_t e,
      const char *params_decl,
      ecs_meta_parse_ctx_t *ctx);
  
  ecs_entity_t ecs_meta_lookup(
      ecs_world_t *world,
      ecs_meta_type_t *token,
      const char *ptr,
      int64_t count,
      ecs_meta_parse_ctx_t *ctx);
  
  #endif
  
  ECS_CTOR(EcsStruct, ptr, {
      ptr->members = NULL;
      ptr->is_partial = false;
  })
  
  ECS_DTOR(EcsStruct, ptr, {
      ecs_vector_each(ptr->members, EcsMember, m, {
          ecs_os_free(m->name);
      });
      ecs_vector_free(ptr->members);
  })
  
  ECS_CTOR(EcsEnum, ptr, {
      ptr->constants = NULL;
  })
  
  ECS_DTOR(EcsEnum, ptr, {
      ecs_map_each(ptr->constants, char*, key, c_ptr, {
          ecs_os_free(*c_ptr);
      })
      ecs_map_free(ptr->constants);
  })
  
  ECS_CTOR(EcsBitmask, ptr, {
      ptr->constants = NULL;
  })
  
  ECS_DTOR(EcsBitmask, ptr, {
      ecs_map_each(ptr->constants, char*, key, c_ptr, {
          ecs_os_free(*c_ptr);
      })    
      ecs_map_free(ptr->constants);
  })
  
  ECS_CTOR(EcsMetaTypeSerializer, ptr, {
      ptr->ops = NULL;
  })
  
  ECS_DTOR(EcsMetaTypeSerializer, ptr, {
      ecs_vector_free(ptr->ops);
  })
  
  static
  void ecs_set_primitive(
      ecs_world_t *world, 
      ecs_entity_t e, 
      EcsMetaType *type) 
  {
      ecs_assert(world != NULL, ECS_INTERNAL_ERROR, NULL);
      ecs_assert(e != 0, ECS_INTERNAL_ERROR, NULL);
      ecs_assert(type != NULL, ECS_INTERNAL_ERROR, NULL);
  
      ecs_entity_t ecs_entity(EcsPrimitive) = ecs_lookup_fullpath(world, "flecs.meta.Primitive");
      ecs_assert(ecs_entity(EcsPrimitive) != 0, ECS_INTERNAL_ERROR, NULL);
  
      const char *descr = type->descriptor;
  
      if (!strcmp(descr, "bool")) {
          ecs_set(world, e, EcsPrimitive, {EcsBool});
      } else
      if (!strcmp(descr, "char")) {
          ecs_set(world, e, EcsPrimitive, {EcsChar});
      } else    
      if (!strcmp(descr, "u8")) {
          ecs_set(world, e, EcsPrimitive, {EcsU8});
      } else
      if (!strcmp(descr, "u8")) {
          ecs_set(world, e, EcsPrimitive, {EcsU8});
      } else
      if (!strcmp(descr, "u16")) {
          ecs_set(world, e, EcsPrimitive, {EcsU16});
      } else
      if (!strcmp(descr, "u32")) {
          ecs_set(world, e, EcsPrimitive, {EcsU32});
      } else
      if (!strcmp(descr, "u64")) {
          ecs_set(world, e, EcsPrimitive, {EcsU64});
      } else
      if (!strcmp(descr, "i8")) {
          ecs_set(world, e, EcsPrimitive, {EcsI8});
      } else
      if (!strcmp(descr, "i16")) {
          ecs_set(world, e, EcsPrimitive, {EcsI16});
      } else
      if (!strcmp(descr, "i32")) {
          ecs_set(world, e, EcsPrimitive, {EcsI32});
      } else
      if (!strcmp(descr, "f32")) {
          ecs_set(world, e, EcsPrimitive, {EcsF32});
      } else
      if (!strcmp(descr, "f64")) {
          ecs_set(world, e, EcsPrimitive, {EcsF64});
      } else
      if (!strcmp(descr, "iptr")) {
          ecs_set(world, e, EcsPrimitive, {EcsIPtr});
      } else    
      if (!strcmp(descr, "uptr")) {
          ecs_set(world, e, EcsPrimitive, {EcsUPtr});
      } else
      if (!strcmp(descr, "string")) {
          ecs_set(world, e, EcsPrimitive, {EcsString});
      } else
      if (!strcmp(descr, "entity")) {
          ecs_set(world, e, EcsPrimitive, {EcsEntity});
      }
  }
  
  static
  void ecs_set_constants(
      ecs_world_t *world, 
      ecs_entity_t e, 
      ecs_entity_t comp,
      bool is_bitmask,
      EcsMetaType *type) 
  {
      ecs_assert(world != NULL, ECS_INTERNAL_ERROR, NULL);
      ecs_assert(e != 0, ECS_INTERNAL_ERROR, NULL);
      ecs_assert(type != NULL, ECS_INTERNAL_ERROR, NULL);
  
      const char *ptr = type->descriptor;
      const char *name = ecs_get_name(world, e);
  
      ecs_meta_parse_ctx_t ctx = {
          .name = name,
          .decl = ptr
      };
  
      ecs_map_t *constants = ecs_map_new(char*, 1);
      ecs_meta_constant_t token;
      int64_t last_value = 0;
  
      while ((ptr = ecs_meta_parse_constant(ptr, &token, &ctx))) {
          if (token.is_value_set) {
              last_value = token.value;
          } else if (is_bitmask) {
              ecs_meta_error(&ctx, ptr, 
                  "bitmask requires explicit value assignment");
          }
  
          char *constant_name = ecs_os_strdup(token.name);
          ecs_map_set(constants, last_value, &constant_name);
  
          last_value ++;
      }
  
      ecs_set_ptr_w_entity(world, e, comp, sizeof(EcsEnum), &(EcsEnum){
          .constants = constants
      });
  }
  
  static
  void ecs_set_bitmask(
      ecs_world_t *world, 
      ecs_entity_t e, 
      EcsMetaType *type) 
  {
      ecs_entity_t comp = ecs_lookup_fullpath(world, "flecs.meta.Bitmask");
      ecs_assert(comp != 0, ECS_INTERNAL_ERROR, NULL);
      ecs_set_constants(world, e, comp, true, type);
  }
  
  static
  void ecs_set_enum(
      ecs_world_t *world, 
      ecs_entity_t e, 
      EcsMetaType *type) 
  {
      ecs_entity_t comp = ecs_lookup_fullpath(world, "flecs.meta.Enum");
      ecs_assert(comp != 0, ECS_INTERNAL_ERROR, NULL);    
      ecs_set_constants(world, e, comp, false, type);
  }
  
  static
  void ecs_set_struct(
      ecs_world_t *world, 
      ecs_entity_t e, 
      EcsMetaType *type) 
  {
      ecs_assert(world != NULL, ECS_INTERNAL_ERROR, NULL);
      ecs_assert(e != 0, ECS_INTERNAL_ERROR, NULL);
      ecs_assert(type != NULL, ECS_INTERNAL_ERROR, NULL);
  
      const char *ptr = type->descriptor;
      const char *name = ecs_get_name(world, e);
      bool is_partial = false;
  
      ecs_meta_parse_ctx_t ctx = {
          .name = name,
          .decl = ptr
      };
  
      ecs_vector_t *members = NULL;
      ecs_meta_member_t token;
  
      while ((ptr = ecs_meta_parse_member(ptr, &token, &ctx))) {
          EcsMember *m = ecs_vector_add(&members, EcsMember);
          m->name = ecs_os_strdup(token.name);
          m->type = ecs_meta_lookup(world, &token.type, ptr, token.count, &ctx);
          ecs_assert(type != 0, ECS_INTERNAL_ERROR, NULL);
      }
  
      is_partial = token.is_partial;
  
      ecs_entity_t ecs_entity(EcsStruct) = ecs_lookup_fullpath(world, "flecs.meta.Struct");
      ecs_assert(ecs_entity(EcsStruct) != 0, ECS_INTERNAL_ERROR, NULL);
      ecs_set(world, e, EcsStruct, {members, is_partial});
  }
  
  static
  void ecs_set_array(
      ecs_world_t *world, 
      ecs_entity_t e, 
      EcsMetaType *type) 
  {
      ecs_assert(world != NULL, ECS_INTERNAL_ERROR, NULL);
      ecs_assert(e != 0, ECS_INTERNAL_ERROR, NULL);
      ecs_assert(type != NULL, ECS_INTERNAL_ERROR, NULL);
  
      const char *ptr = type->descriptor;
      const char *name = ecs_get_name(world, e);
  
      ecs_meta_parse_ctx_t ctx = {
          .name = name,
          .decl = ptr
      };
  
      ecs_meta_lookup_array(world, e, type->descriptor, &ctx);
  }
  
  static
  void ecs_set_vector(
      ecs_world_t *world, 
      ecs_entity_t e, 
      EcsMetaType *type) 
  {
      ecs_assert(world != NULL, ECS_INTERNAL_ERROR, NULL);
      ecs_assert(e != 0, ECS_INTERNAL_ERROR, NULL);
      ecs_assert(type != NULL, ECS_INTERNAL_ERROR, NULL);
  
      const char *ptr = type->descriptor;
      const char *name = ecs_get_name(world, e);
  
      ecs_meta_parse_ctx_t ctx = {
          .name = name,
          .decl = ptr
      };
  
      ecs_meta_lookup_vector(world, e, type->descriptor, &ctx);
  }
  
  static
  void ecs_set_map(
      ecs_world_t *world, 
      ecs_entity_t e, 
      EcsMetaType *type) 
  {
      ecs_assert(world != NULL, ECS_INTERNAL_ERROR, NULL);
      ecs_assert(e != 0, ECS_INTERNAL_ERROR, NULL);
      ecs_assert(type != NULL, ECS_INTERNAL_ERROR, NULL);
  
      const char *ptr = type->descriptor;
      const char *name = ecs_get_name(world, e);
  
      ecs_meta_parse_ctx_t ctx = {
          .name = name,
          .decl = ptr
      };
  
      ecs_meta_lookup_map(world, e, type->descriptor, &ctx);
  }
  
  static
  void EcsSetType(ecs_iter_t *it) {
      ECS_COLUMN(it, EcsMetaType, type, 1);
  
      ecs_world_t *world = it->world;
  
      int i;
      for(i = 0; i < it->count; i ++) {
          ecs_entity_t e = it->entities[i];
  
          /* If type does not contain a descriptor, application will have to
           * manually initialize type specific data */
          if (!type[i].descriptor) {
  
              /* For some types we can set the size and alignment automatically */
              if (!type[i].size || !type[i].alignment) {
                  switch(type[i].kind) {
                  case EcsBitmaskType:
                  case EcsEnumType:
                      type[i].size = sizeof(int32_t);
                      type[i].alignment = ECS_ALIGNOF(int32_t);
                      break;
                  case EcsVectorType:
                      type[i].size = sizeof(ecs_vector_t*);
                      type[i].alignment = ECS_ALIGNOF(ecs_vector_t*);
                      break;
                  case EcsMapType:
                      type[i].size = sizeof(ecs_map_t*);
                      type[i].alignment = ECS_ALIGNOF(ecs_map_t*);
                      break;                    
                  default:
                      break;
                  }
              }
  
              continue;
          }
  
          switch(type[i].kind) {
          case EcsPrimitiveType:
              ecs_set_primitive(world, e, type);
              break;
          case EcsBitmaskType:
              ecs_set_bitmask(world, e, type);
              break;
          case EcsEnumType:
              ecs_set_enum(world, e, type);
              break;
          case EcsStructType:
              ecs_set_struct(world, e, type);
              break;
          case EcsArrayType:
              ecs_set_array(world, e, type);
              break;
          case EcsVectorType:
              ecs_set_vector(world, e, type);
              break;
          case EcsMapType:
              ecs_set_map(world, e, type);
              break;
          }
      }
  }
  
  static
  void ctor_initialize_0(
      ecs_world_t *world,
      ecs_entity_t component,
      const ecs_entity_t *entities,
      void *ptr,
      size_t size,
      int32_t count,
      void *ctx)
  {
      (void)world;
      (void)component;
      (void)entities;
      (void)ctx;
      memset(ptr, 0, size * (size_t)count);
  }
  
  void ecs_new_meta(
      ecs_world_t *world,
      ecs_entity_t component,
      EcsMetaType *meta_type)
  {
      ecs_entity_t ecs_entity(EcsMetaType) = 
          ecs_lookup_fullpath(world, "flecs.meta.MetaType");
      ecs_assert(ecs_entity(EcsMetaType) != 0, ECS_MODULE_UNDEFINED, "flecs.meta");
  
      ecs_set_ptr(world, component, EcsMetaType, meta_type);\
      ecs_set_component_actions_w_entity(world, component, &(EcsComponentLifecycle){
          .ctor = ctor_initialize_0
      });
  }
  
  /* Utility macro to insert meta data for type with meta descriptor */
  #define ECS_COMPONENT_TYPE(world, type)\
      ecs_set_ptr(world, ecs_entity(type), EcsMetaType, &__##type##__)
  
  /* Utility macro to insert metadata for primitive type */
  #define ECS_COMPONENT_PRIMITIVE(world, type, kind)\
      ECS_COMPONENT(world, type);\
      ecs_set(world, ecs_entity(type), EcsMetaType, {EcsPrimitiveType, 0, 0, NULL});\
      ecs_set(world, ecs_entity(type), EcsPrimitive, {kind})
  
  void FlecsMetaImport(
      ecs_world_t *world)
  {
      ECS_MODULE(world, FlecsMeta);
  
      ecs_set_name_prefix(world, "Ecs");
  
      ECS_COMPONENT(world, EcsPrimitive);
      ECS_COMPONENT(world, EcsEnum);
      ECS_COMPONENT(world, EcsBitmask);
      ECS_COMPONENT(world, EcsMember);
      ECS_COMPONENT(world, EcsStruct);
      ECS_COMPONENT(world, EcsArray);
      ECS_COMPONENT(world, EcsVector);
      ECS_COMPONENT(world, EcsMap);
      ECS_COMPONENT(world, EcsMetaType);
      ECS_COMPONENT(world, ecs_type_op_kind_t);
      ECS_COMPONENT(world, ecs_type_op_t);
      ECS_COMPONENT(world, EcsMetaTypeSerializer);
  
      ECS_SYSTEM(world, EcsSetType, EcsOnSet, EcsMetaType);
  
      ecs_set_component_actions(world, EcsStruct, {
          .ctor = ecs_ctor(EcsStruct),
          .dtor = ecs_dtor(EcsStruct)
      });
  
      ecs_set_component_actions(world, EcsEnum, {
          .ctor = ecs_ctor(EcsEnum),
          .dtor = ecs_dtor(EcsEnum)
      });    
  
      ecs_set_component_actions(world, EcsBitmask, {
          .ctor = ecs_ctor(EcsBitmask),
          .dtor = ecs_dtor(EcsBitmask)
      });
  
      ecs_set_component_actions(world, EcsMetaTypeSerializer, {
          .ctor = ecs_ctor(EcsMetaTypeSerializer),
          .dtor = ecs_dtor(EcsMetaTypeSerializer)
      });    
  
      ECS_SYSTEM(world, EcsSetPrimitive, EcsOnSet, Primitive, flecs.meta:flecs.meta);
      ECS_SYSTEM(world, EcsSetEnum, EcsOnSet, Enum, flecs.meta:flecs.meta);
      ECS_SYSTEM(world, EcsSetBitmask, EcsOnSet, Bitmask, flecs.meta:flecs.meta);
      ECS_SYSTEM(world, EcsSetStruct, EcsOnSet, Struct, flecs.meta:flecs.meta);
      ECS_SYSTEM(world, EcsSetArray, EcsOnSet, Array, flecs.meta:flecs.meta);
      ECS_SYSTEM(world, EcsSetVector, EcsOnSet, Vector, flecs.meta:flecs.meta);
      ECS_SYSTEM(world, EcsSetMap, EcsOnSet, Map, flecs.meta:flecs.meta);
  
      ECS_EXPORT_COMPONENT(EcsPrimitive);
      ECS_EXPORT_COMPONENT(EcsEnum);
      ECS_EXPORT_COMPONENT(EcsBitmask);
      ECS_EXPORT_COMPONENT(EcsStruct);
      ECS_EXPORT_COMPONENT(EcsArray);
      ECS_EXPORT_COMPONENT(EcsVector);
      ECS_EXPORT_COMPONENT(EcsMap);
      ECS_EXPORT_COMPONENT(EcsMetaType);
      ECS_EXPORT_COMPONENT(EcsMetaTypeSerializer);  
  
      /* -- Initialize builtin primitive types -- */
      ecs_entity_t old_scope = ecs_set_scope(world, EcsFlecsCore);
      ECS_COMPONENT_PRIMITIVE(world, bool, EcsBool);
      ECS_COMPONENT_PRIMITIVE(world, char, EcsChar);
      ECS_COMPONENT_PRIMITIVE(world, ecs_byte_t, EcsByte);
      ECS_COMPONENT_PRIMITIVE(world, uint8_t, EcsU8);
      ECS_COMPONENT_PRIMITIVE(world, uint16_t, EcsU16);
      ECS_COMPONENT_PRIMITIVE(world, uint32_t, EcsU32);
      ECS_COMPONENT_PRIMITIVE(world, uint64_t, EcsU64);
      ECS_COMPONENT_PRIMITIVE(world, uintptr_t, EcsUPtr);
      ECS_COMPONENT_PRIMITIVE(world, int8_t, EcsI8);
      ECS_COMPONENT_PRIMITIVE(world, int16_t, EcsI16);
      ECS_COMPONENT_PRIMITIVE(world, int32_t, EcsI32);
      ECS_COMPONENT_PRIMITIVE(world, int64_t, EcsI64);
      ECS_COMPONENT_PRIMITIVE(world, intptr_t, EcsIPtr);
      ECS_COMPONENT_PRIMITIVE(world, size_t, EcsUPtr);
      ECS_COMPONENT_PRIMITIVE(world, float, EcsF32);
      ECS_COMPONENT_PRIMITIVE(world, double, EcsF64);
      ECS_COMPONENT_PRIMITIVE(world, ecs_size_t, EcsI32);
      ECS_COMPONENT_PRIMITIVE(world, ecs_string_t, EcsString);
      ECS_COMPONENT_PRIMITIVE(world, ecs_entity_t, EcsEntity);
  
      /* If stdbool is included, the above bool declaration will have been
       * registered with the name _Bool. To make sure meta also knows the type by
       * its regular name, check and register if necessary */
      if (!ecs_lookup(world, "bool")) {
          ecs_entity_t type = ecs_new_component(
              world, 0, "bool", sizeof(bool), ECS_ALIGNOF(bool));
          ecs_set(world, type, EcsMetaType, {
              EcsPrimitiveType, 0, 0, NULL});
          ecs_set(world, type, EcsPrimitive, {EcsBool});
      }
  
      ecs_set_scope(world, old_scope);
  
      /* -- Initialize builtin meta components -- */
      ecs_set_ptr(world, ecs_set(world, 0,
          EcsName, {"ecs_primitive_kind_t", NULL, NULL}),
          EcsMetaType, &__ecs_primitive_kind_t__);
  
      ecs_set(world, ecs_set(world, 0,
          EcsName, {"ecs_type_kind_t", NULL, NULL}),
          EcsMetaType, {
              EcsEnumType, 
              sizeof(ecs_type_kind_t), 
              ECS_ALIGNOF(ecs_type_kind_t), 
              __ecs_type_kind_t__
          });
  
      /* Insert meta definitions for other types */
      ECS_COMPONENT_TYPE(world, EcsPrimitive);
      ECS_COMPONENT_TYPE(world, EcsEnum);
      ECS_COMPONENT_TYPE(world, EcsBitmask);
      ECS_COMPONENT_TYPE(world, EcsMember);
      ECS_COMPONENT_TYPE(world, EcsStruct);
      ECS_COMPONENT_TYPE(world, EcsArray);
      ECS_COMPONENT_TYPE(world, EcsVector);
      ECS_COMPONENT_TYPE(world, EcsMap);
      ECS_COMPONENT_TYPE(world, EcsMetaType);
      ECS_COMPONENT_TYPE(world, ecs_type_op_kind_t);
      ECS_COMPONENT_TYPE(world, ecs_type_op_t);
      ECS_COMPONENT_TYPE(world, EcsMetaTypeSerializer);
  
      /* -- Initialize metadata for public Flecs core components -- */
      ecs_set(world, ecs_entity(EcsName), EcsMetaType, {
          .kind = EcsStructType, 
          .size = sizeof(EcsName),
          .alignment = ECS_ALIGNOF(EcsName),
          .descriptor = "{ecs_string_t value; ECS_PRIVATE; }"
      });
  
      ecs_set(world, ecs_entity(EcsComponent), EcsMetaType, {
          .kind = EcsStructType, 
          .size = sizeof(EcsComponent),
          .alignment = ECS_ALIGNOF(EcsComponent),
          .descriptor = "{int32_t size; int32_t alignment;}"
      });
  
      ecs_set(world, ecs_entity(EcsSignatureExpr), EcsMetaType, {
          .kind = EcsStructType, 
          .size = sizeof(EcsSignatureExpr),
          .alignment = ECS_ALIGNOF(EcsSignatureExpr),
          .descriptor = "{const char *expr;}"
      });
  }
  
  ecs_entity_t ecs_meta_lookup_array(
      ecs_world_t *world,
      ecs_entity_t e,
      const char *params_decl,
      ecs_meta_parse_ctx_t *ctx)
  {    
      ecs_meta_parse_ctx_t param_ctx = {
          .name = ctx->name,
          .decl = params_decl
      };
  
      ecs_meta_params_t params;
      ecs_meta_parse_params(params_decl, &params, &param_ctx);
      if (!params.is_fixed_size) {
          ecs_meta_error(ctx, params_decl, "missing size for array");
      }
  
      if (!params.count) {
          ecs_meta_error(ctx, params_decl, "invalid array size");
      }
  
      ecs_entity_t element_type = ecs_lookup_symbol(world, params.type.type);
      if (!element_type) {
          ecs_meta_error(ctx, params_decl, "unknown element type '%s'", 
              params.type.type);
      }
  
      if (!e) {
          ecs_entity_t ecs_entity(EcsMetaType) = ecs_lookup_fullpath(world, "flecs.meta.MetaType");
          ecs_assert(ecs_entity(EcsMetaType) != 0, ECS_INTERNAL_ERROR, NULL);
  
          const EcsMetaType *elem_type = ecs_get(world, element_type, EcsMetaType);
          ecs_assert(elem_type != NULL, ECS_INTERNAL_ERROR, NULL);
  
          ecs_assert(elem_type->size * params.count <= INT32_MAX, 
              ECS_INVALID_PARAMETER, NULL);
  
          e = ecs_set(world, 0, EcsMetaType, {
              EcsArrayType, (int32_t)(elem_type->size * params.count), 
              elem_type->alignment, NULL
          });
      }
  
      ecs_entity_t ecs_entity(EcsArray) = ecs_lookup_fullpath(world, "flecs.meta.Array");
      ecs_assert(ecs_entity(EcsArray) != 0, ECS_INTERNAL_ERROR, NULL);
  
      ecs_assert(params.count <= INT32_MAX, ECS_INVALID_PARAMETER, NULL);
  
      return ecs_set(world, e, EcsArray, { element_type, (int32_t)params.count });
  }
  
  ecs_entity_t ecs_meta_lookup_vector(
      ecs_world_t *world,
      ecs_entity_t e,
      const char *params_decl,
      ecs_meta_parse_ctx_t *ctx)
  {    
      ecs_meta_parse_ctx_t param_ctx = {
          .name = ctx->name,
          .decl = params_decl
      };
  
      ecs_meta_params_t params;
      ecs_meta_parse_params(params_decl, &params, &param_ctx);
      if (params.is_key_value) {
          ecs_meta_error(ctx, params_decl, 
              "unexpected key value parameters for vector");
      }
  
      ecs_entity_t element_type = ecs_meta_lookup(
          world, &params.type, params_decl, 1, &param_ctx);
  
      if (!e) {
          ecs_entity_t ecs_entity(EcsMetaType) = ecs_lookup_fullpath(world, "flecs.meta.MetaType");
          ecs_assert(ecs_entity(EcsMetaType) != 0, ECS_INTERNAL_ERROR, NULL);
  
          e = ecs_set(world, 0, EcsMetaType, {EcsVectorType, 0, 0, NULL});
      }
  
      ecs_entity_t ecs_entity(EcsVector) = ecs_lookup_fullpath(world, "flecs.meta.Vector");
      ecs_assert(ecs_entity(EcsVector) != 0, ECS_INTERNAL_ERROR, NULL);
  
      return ecs_set(world, e, EcsVector, { element_type });
  }
  
  ecs_entity_t ecs_meta_lookup_map(
      ecs_world_t *world,
      ecs_entity_t e,    
      const char *params_decl,
      ecs_meta_parse_ctx_t *ctx)
  {    
      ecs_meta_parse_ctx_t param_ctx = {
          .name = ctx->name,
          .decl = params_decl
      };
  
      ecs_meta_params_t params;
      ecs_meta_parse_params(params_decl, &params, &param_ctx);
      if (!params.is_key_value) {
          ecs_meta_error(ctx, params_decl, 
              "missing key type for map");
      }
  
      ecs_entity_t key_type = ecs_meta_lookup(
          world, &params.key_type, params_decl, 1, &param_ctx);
  
      ecs_entity_t element_type = ecs_meta_lookup(
          world, &params.type, params_decl, 1, &param_ctx);
      
      if (!e) {
          ecs_entity_t ecs_entity(EcsMetaType) = ecs_lookup_fullpath(world, "flecs.meta.MetaType");
          ecs_assert(ecs_entity(EcsMetaType) != 0, ECS_INTERNAL_ERROR, NULL);
          
          e = ecs_set(world, 0, EcsMetaType, {EcsMapType, 0, 0, NULL});
      }
  
      ecs_entity_t ecs_entity(EcsMap) = ecs_lookup_fullpath(world, "flecs.meta.Map");
      ecs_assert(ecs_entity(EcsMap) != 0, ECS_INTERNAL_ERROR, NULL);
  
      return ecs_set(world, e, EcsMap, { key_type, element_type });
  }
  
  ecs_entity_t ecs_meta_lookup_bitmask(
      ecs_world_t *world,
      ecs_entity_t e,
      const char *params_decl,
      ecs_meta_parse_ctx_t *ctx)
  {
      (void)e;
  
      ecs_meta_parse_ctx_t param_ctx = {
          .name = ctx->name,
          .decl = params_decl
      };
  
      ecs_meta_params_t params;
      ecs_meta_parse_params(params_decl, &params, &param_ctx);
      if (params.is_key_value) {
          ecs_meta_error(ctx, params_decl, 
              "unexpected key value parameters for bitmask");
      }
  
      if (params.is_fixed_size) {
          ecs_meta_error(ctx, params_decl, 
              "unexpected size for bitmask");
      }
  
      ecs_entity_t bitmask_type = ecs_meta_lookup(
          world, &params.type, params_decl, 1, &param_ctx);
      ecs_assert(bitmask_type != 0, ECS_INVALID_PARAMETER, NULL);
  
      /* Make sure this is a bitmask type */
      ecs_entity_t ecs_entity(EcsMetaType) = ecs_lookup_fullpath(world, "flecs.meta.MetaType");
      ecs_assert(ecs_entity(EcsMetaType) != 0, ECS_INTERNAL_ERROR, NULL);
  
      const EcsMetaType *type_ptr = ecs_get(world, bitmask_type, EcsMetaType);
      ecs_assert(type_ptr != NULL, ECS_INVALID_PARAMETER, NULL);
      ecs_assert(type_ptr->kind == EcsBitmaskType, ECS_INVALID_PARAMETER, NULL);
  
      return bitmask_type;
  }
  
  ecs_entity_t ecs_meta_lookup(
      ecs_world_t *world,
      ecs_meta_type_t *token,
      const char *ptr,
      int64_t count,
      ecs_meta_parse_ctx_t *ctx)
  {
      ecs_assert(world != NULL, ECS_INTERNAL_ERROR, NULL);
      ecs_assert(token != NULL, ECS_INTERNAL_ERROR, NULL);
      ecs_assert(ptr != NULL, ECS_INTERNAL_ERROR, NULL);
      ecs_assert(ctx != NULL, ECS_INTERNAL_ERROR, NULL);
  
      const char *typename = token->type;
      ecs_entity_t type = 0;
  
      /* Parse vector type */
      if (!strcmp(typename, "ecs_array")) {
          type = ecs_meta_lookup_array(world, 0, token->params, ctx);
  
      } else if (!strcmp(typename, "ecs_vector") || !strcmp(typename, "flecs::vector")) {
          type = ecs_meta_lookup_vector(world, 0, token->params, ctx);
  
      } else if (!strcmp(typename, "ecs_map") | !strcmp(typename, "flecs::map")) {
          type = ecs_meta_lookup_map(world, 0, token->params, ctx);
  
      } else if (!strcmp(typename, "flecs::bitmask")) {
          type = ecs_meta_lookup_bitmask(world, 0, token->params, ctx);
  
      } else if (!strcmp(typename, "flecs::byte")) {
          type = ecs_lookup(world, "ecs_byte_t");
  
      } else {
          if (token->is_ptr && !strcmp(typename, "char")) {
              typename = "ecs_string_t";
          } else
          if (token->is_ptr) {
              typename = "uintptr_t";
          } else        
          if (!strcmp(typename, "char*") || !strcmp(typename, "flecs::string")) {
              typename = "ecs_string_t";
          }
  
          type = ecs_lookup_symbol(world, typename);
          if (!type) {
              ecs_meta_error(ctx, ptr, "unknown type '%s'", typename);
              return 0;
          }
      }
  
      if (count != 1) {
          /* If count is not 1, insert array type. First lookup EcsMetaType of the
           * element type to get the size and alignment. Then create a new
           * entity for the array type, and assign it to the member type. */
          ecs_entity_t ecs_entity(EcsMetaType) = ecs_lookup_fullpath(world, "flecs.meta.MetaType");
          ecs_entity_t ecs_entity(EcsArray) = ecs_lookup_fullpath(world, "flecs.meta.Array");
          const EcsMetaType *type_ptr = ecs_get(world, type, EcsMetaType);
  
          ecs_assert(count <= INT32_MAX, ECS_INVALID_PARAMETER, NULL);
  
          type = ecs_set(world, ecs_set(world, 0, 
              EcsMetaType, {EcsArrayType, type_ptr->size, type_ptr->alignment, NULL}),
              EcsArray, {type, (int32_t)count});
      }
  
      return type;
  }
  
  static
  ecs_meta_scope_t* get_scope(
      ecs_meta_cursor_t *cursor)
  {
      ecs_assert(cursor != NULL, ECS_INVALID_PARAMETER, NULL);
      return &cursor->scope[cursor->depth];
  }
  
  static
  ecs_type_op_t* get_op(
      ecs_meta_scope_t *scope)
  {
      ecs_type_op_t *ops = ecs_vector_first(scope->ops, ecs_type_op_t);
      ecs_assert(ops != NULL, ECS_INVALID_PARAMETER, NULL);
      return &ops[scope->cur_op];
  }
  
  static
  ecs_type_op_t* get_ptr(
      ecs_meta_scope_t *scope)
  {
      ecs_type_op_t *op = get_op(scope);
  
      if (scope->vector) {
          _ecs_vector_set_min_count(&scope->vector, ECS_VECTOR_U(op->size, op->alignment), scope->cur_elem + 1);
          scope->base = ecs_vector_first_t(scope->vector, op->size, op->alignment);
      }
  
      return ECS_OFFSET(scope->base, op->offset + op->size * scope->cur_elem);
  }
  
  ecs_meta_cursor_t ecs_meta_cursor(
      ecs_world_t *world,
      ecs_entity_t type, 
      void *base)
  {
      ecs_assert(world != NULL, ECS_INVALID_PARAMETER, NULL);
      ecs_assert(type != 0, ECS_INVALID_PARAMETER, NULL);
      ecs_assert(base != NULL, ECS_INVALID_PARAMETER, NULL);
      
      ecs_meta_cursor_t result;
      ecs_entity_t ecs_entity(EcsMetaTypeSerializer) = 
          ecs_lookup_fullpath(world, "flecs.meta.MetaTypeSerializer");
      ecs_assert(ecs_entity(EcsMetaTypeSerializer) != 0, ECS_INVALID_PARAMETER, NULL);
  
      const EcsMetaTypeSerializer *ser = ecs_get(world, type, EcsMetaTypeSerializer);
      ecs_assert(ser != NULL, ECS_INVALID_PARAMETER, NULL);
  
      ecs_type_op_t *ops = ecs_vector_first(ser->ops, ecs_type_op_t);
      ecs_assert(ops != NULL, ECS_INVALID_PARAMETER, NULL);
      ecs_assert(ops[0].kind == EcsOpHeader, ECS_INVALID_PARAMETER, NULL);
  
      result.world = world;
      result.depth = 0;
      result.scope[0].type = type;
      result.scope[0].ops = ser->ops;
      result.scope[0].start = 1;
      result.scope[0].cur_op = 1;
      result.scope[0].cur_elem = 0;
      result.scope[0].base = base;
      result.scope[0].is_collection = false;
      result.scope[0].count = 0;
      result.scope[0].vector = NULL;
  
      return result;
  }
  
  void* ecs_meta_get_ptr(
      ecs_meta_cursor_t *cursor)
  {
      return get_ptr(cursor->scope);
  }
  
  int ecs_meta_next(
      ecs_meta_cursor_t *cursor)
  {
      ecs_meta_scope_t *scope = get_scope(cursor);
      int32_t ops_count = ecs_vector_count(scope->ops);
  
      if (scope->count) {
          if (scope->cur_op >= scope->count) {
              return -1;
          }
      } else {
          if (scope->cur_op >= ops_count) {
              return -1;
          }
      }
  
      if (scope->is_collection) {
          scope->cur_op = 1;
          scope->cur_elem ++;
  
          if (scope->count) {
              if (scope->cur_elem >= scope->count) {
                  return -1;
              }
          }        
      } else {
          scope->cur_op ++;
      }
  
      return 0;
  }
  
  int ecs_meta_move(
      ecs_meta_cursor_t *cursor,
      int32_t pos)
  {
      ecs_meta_scope_t *scope = get_scope(cursor);
      int32_t ops_count = ecs_vector_count(scope->ops);
  
      if (pos >= ops_count) {
          return -1;
      }
  
      scope->cur_op = pos;
  
      return 0;
  }
  
  int ecs_meta_move_name(
      ecs_meta_cursor_t *cursor,
      const char *name)
  {
      ecs_meta_scope_t *scope = get_scope(cursor);
      ecs_type_op_t *ops = ecs_vector_first(scope->ops, ecs_type_op_t);
      int32_t i, ops_count = ecs_vector_count(scope->ops);
      int32_t depth = 1;
  
      for (i = scope->start; i < ops_count; i ++) {
          ecs_type_op_t *op = &ops[i];
  
          if (depth <= 1) {
              if (op->name && !strcmp(op->name, name)) {
                  scope->cur_op = i;
                  return 0;
              }
          }
  
          if (op->kind == EcsOpPush) {
              depth ++;
          }
  
          if (op->kind == EcsOpPop) {
              depth --;
              if (depth < 0) {
                  return -1;
              }
          }
      }
  
      return -1;
  }
  
  int ecs_meta_push(
      ecs_meta_cursor_t *cursor)
  {
      ecs_meta_scope_t *scope = get_scope(cursor);
      ecs_type_op_t *op = get_op(scope);
  
      if (scope->vector) {
          /* This makes sure the vector has enough space for the pushed element */
          get_ptr(scope);
      }
      
      scope->cur_op ++;
      cursor->depth ++;
      ecs_meta_scope_t *child_scope = get_scope(cursor);
      child_scope->cur_elem = 0;
  
      switch(op->kind) {
      case EcsOpPush: {
          child_scope->base = ECS_OFFSET(scope->base, op->size * scope->cur_elem);
          child_scope->start = scope->cur_op;
          child_scope->cur_op = scope->cur_op;
          child_scope->ops = scope->ops;
          child_scope->is_collection = false;
          child_scope->count = 0;
          child_scope->vector = NULL;
          break;
      }
      case EcsOpArray:
      case EcsOpVector: {
          void *ptr = ECS_OFFSET(scope->base, op->offset);
          const EcsMetaTypeSerializer *ser = ecs_get_ref_w_entity(cursor->world, 
              &op->is.collection, 0, 0);
          ecs_assert(ser != NULL, ECS_INTERNAL_ERROR, NULL);
  
          ecs_vector_t *ops = ser->ops;
  
          if (op->kind == EcsOpArray) {
              child_scope->base = ptr;
              child_scope->count = op->count;
              child_scope->vector = NULL;
          } else {
              ecs_vector_t *v = *(ecs_vector_t**)ptr;
              if (!v) {
                  v = ecs_vector_new_t(op->size, op->alignment, 2);
              } else {
                  ecs_vector_set_count_t(&v, op->size, op->alignment, 0);
              }
              
              child_scope->base = ecs_vector_first_t(v, op->size, op->alignment);
              child_scope->count = 0;
              child_scope->vector = v;
          }
          child_scope->start = 1;
          child_scope->cur_op = 1;
          child_scope->ops = ops;
          child_scope->is_collection = true;
  #ifndef NDEBUG
          ecs_type_op_t *hdr = ecs_vector_first(child_scope->ops, ecs_type_op_t);
          ecs_assert(hdr->kind == EcsOpHeader, ECS_INTERNAL_ERROR, NULL);
  #endif
          }
          break;
      default:
          return -1;
      }
  
      return 0;
  }
  
  int ecs_meta_pop(
      ecs_meta_cursor_t *cursor)
  {
      ecs_meta_scope_t *scope = get_scope(cursor);
      ecs_type_op_t *ops = ecs_vector_first(scope->ops, ecs_type_op_t);
      int32_t i, ops_count = ecs_vector_count(scope->ops);
  
      if (scope->is_collection) {
          cursor->depth --;
          if (scope->vector) {
              /* Vector ptr may have changed, so reassign vector field */
              ecs_meta_scope_t *parent_scope = get_scope(cursor);
              parent_scope->cur_op --;
              void *ptr = get_ptr(parent_scope);
              *(ecs_vector_t**)ptr = scope->vector;
              parent_scope->cur_op ++;
          }         
          return 0;
      } else {
          for (i = scope->cur_op; i < ops_count; i ++) {
              ecs_type_op_t *op = &ops[i];
              if (op->kind == EcsOpPop) {
                  cursor->depth -- ;                
                  ecs_meta_scope_t *parent_scope = get_scope(cursor);
                  if (parent_scope->is_collection) {
                      parent_scope->cur_op = 1;
                      parent_scope->cur_elem ++;
                  } else {
                      parent_scope->cur_op = i;                   
                  }
                  return 0;
              }
          }
      }
  
      return -1;
  }
  
  int ecs_meta_set_bool(
      ecs_meta_cursor_t *cursor,
      bool value)
  {
      ecs_meta_scope_t *scope = get_scope(cursor);
      ecs_type_op_t *op = get_op(scope);
      
      if (op->kind != EcsOpPrimitive || op->is.primitive != EcsBool) {
          return -1;
      } else {
          void *ptr = get_ptr(scope);
          *(bool*)ptr = value;
          return 0;
      }
  }
  
  int ecs_meta_set_char(
      ecs_meta_cursor_t *cursor,
      char value)
  {
      ecs_meta_scope_t *scope = get_scope(cursor);
      ecs_type_op_t *op = get_op(scope);
      
      if (op->kind != EcsOpPrimitive || op->is.primitive != EcsChar) {
          return -1;
      } else {
          void *ptr = get_ptr(scope);
          *(char*)ptr = value;
          return 0;
      }
  }
  
  int ecs_meta_set_int(
      ecs_meta_cursor_t *cursor,
      int64_t value)
  {
      ecs_meta_scope_t *scope = get_scope(cursor);
      ecs_type_op_t *op = get_op(scope);
  
      
      if (op->kind != EcsOpPrimitive) {
          return -1;
      } else {
          void *ptr = get_ptr(scope);
  
          switch(op->is.primitive) {
          case EcsI8:
              if (value > INT8_MAX) {
                  return -1;
              }
              *(int8_t*)ptr = (int8_t)value;
              break;
          case EcsI16:
              if (value > INT16_MAX) {
                  return -1;
              }
              *(int16_t*)ptr = (int16_t)value;
              break;
          case EcsI32:
              if (value > INT32_MAX) {
                  return -1;
              }
              *(int32_t*)ptr = (int32_t)value;
              break;
          case EcsI64:
              if (value > INT64_MAX) {
                  return -1;
              }
              *(int64_t*)ptr = (int64_t)value;
              break;
          case EcsIPtr:
              if (value > INTPTR_MAX) {
                  return -1;
              }
              *(intptr_t*)ptr = (intptr_t)value;
              break;
          default:
              return -1;
              break;
          }
  
          return 0;
      }
  }
  
  int ecs_meta_set_uint(
      ecs_meta_cursor_t *cursor,
      uint64_t value)
  {
      ecs_meta_scope_t *scope = get_scope(cursor);
      ecs_type_op_t *op = get_op(scope);
      
      if (op->kind != EcsOpPrimitive) {
          return -1;
      } else {
          void *ptr = get_ptr(scope);
  
          switch(op->is.primitive) {
          case EcsU8:
          case EcsByte:
              if (value > UINT8_MAX) {
                  return -1;
              }
              *(uint8_t*)ptr = (uint8_t)value;
              break;
          case EcsU16:
              if (value > UINT16_MAX) {
                  return -1;
              }
              *(uint16_t*)ptr = (uint16_t)value;
              break;
          case EcsU32:
              if (value > UINT32_MAX) {
                  return -1;
              }
              *(uint32_t*)ptr = (uint32_t)value;
              break;
          case EcsU64:
              if (value > UINT64_MAX) {
                  return -1;
              }
              *(uint64_t*)ptr = (uint64_t)value;
              break;
          case EcsUPtr:
              if (value > UINTPTR_MAX) {
                  return -1;
              }
              *(uintptr_t*)ptr = (uintptr_t)value;
              break;
          default:
              return -1;
              break;
          }
  
          return 0;
      }
  }
  
  int ecs_meta_set_float(
      ecs_meta_cursor_t *cursor,
      double value)
  {
      ecs_meta_scope_t *scope = get_scope(cursor);
      ecs_type_op_t *op = get_op(scope);
      
      if (op->kind != EcsOpPrimitive) {
          return -1;
      } else {
          void *ptr = get_ptr(scope);
  
          switch(op->is.primitive) {
          case EcsF32:
              *(float*)ptr = (float)value;
              break;
          case EcsF64:
              *(double*)ptr = value;
              break;
          default:
              return -1;
              break;
          }
  
          return 0;
      }
  }
  
  int ecs_meta_set_string(
      ecs_meta_cursor_t *cursor,
      const char *value)
  {
      ecs_meta_scope_t *scope = get_scope(cursor);
      ecs_type_op_t *op = get_op(scope);
      
      if (op->kind != EcsOpPrimitive) {
          return -1;
      } else {
          void *ptr = get_ptr(scope);
  
          switch(op->is.primitive) {
          case EcsString:
              if (*(char**)ptr) {
                  ecs_os_free(*(char**)ptr);
              }
              if (value) {
                  *(char**)ptr = ecs_os_strdup(value);
              } else {
                  *(char**)ptr = NULL;
              }
              break;
          default:
              return -1;
              break;
          }
  
          return 0;
      }
  }
  
  int ecs_meta_set_entity(
      ecs_meta_cursor_t *cursor,
      ecs_entity_t value)
  {
      ecs_meta_scope_t *scope = get_scope(cursor);
      ecs_type_op_t *op = get_op(scope);
      
      if (op->kind != EcsOpPrimitive) {
          return -1;
      } else {
          void *ptr = get_ptr(scope);
  
          switch(op->is.primitive) {
          case EcsEntity:
              *(ecs_entity_t*)ptr = value;
              break;
          default:
              return -1;
              break;
          }
  
          return 0;
      }
  }
  
  int ecs_meta_set_null(
      ecs_meta_cursor_t *cursor)
  {
      ecs_meta_scope_t *scope = get_scope(cursor);
      ecs_type_op_t *op = get_op(scope);
  
      switch(op->kind) {
      case EcsOpPrimitive: {
          if (op->is.primitive != EcsString) {
              return -1;
          }
  
          void *ptr = get_ptr(scope);
          char *str = *(char**)ptr;
          if (str) {
              ecs_os_free(str);
          }
  
          *(char**)ptr = NULL;
          break;
      }
  
      case EcsOpVector: {
          void *ptr = get_ptr(scope);
          ecs_vector_t *vec = *(ecs_vector_t**)ptr;
          if (vec) {
              ecs_vector_free(vec);
          }
  
          *(ecs_vector_t**)ptr = NULL;
          break;
      }
  
      default:
          return -1;
          break;
      }
  
      return 0;
  }
  #include <stdio.h>
  #include <ctype.h>
  
  static
  const char* skip_ws(const char *ptr) {
      while (isspace(*ptr)) {
          ptr ++;
      }
  
      return ptr;
  }
  
  static
  const char* skip_scope(const char *ptr, ecs_meta_parse_ctx_t *ctx) {
      /* Keep track of which characters were used to open the scope */
      char stack[256];
      int32_t sp = 0;
      char ch;
  
      while ((ch = *ptr)) {
          if (ch == '(') {
              stack[sp++] = ch;
          } else if (ch == '<') {
              stack[sp++] = ch;
          } else if (ch == '>') {
              if (stack[--sp] != '<') {
                  ecs_meta_error(ctx, ptr, "mismatching < > in type definition");
              }
          } else if (ch == ')') {
              if (stack[--sp] != '(') {
                  ecs_meta_error(ctx, ptr, "mismatching ( ) in type definition");
              }            
          }
  
          ptr ++;
  
          if (!sp) {
              break;
          }
      }
  
      return ptr;
  }
  
  static
  const char* parse_digit(
      const char *ptr,
      int64_t *value_out,
      ecs_meta_parse_ctx_t *ctx)
  {
      ptr = skip_ws(ptr);
  
      if (!isdigit(*ptr) && *ptr != '-') {
          ecs_meta_error(ctx, ptr, "expected number, got %c", *ptr);
      }
  
      *value_out = strtol(ptr, NULL, 0);
  
      if (ptr[0] == '-') {
          ptr ++;
      } else 
      if (ptr[0] == '0' && ptr[1] == 'x') {
          ptr += 2;
      }
  
      while (isdigit(*ptr)) {
          ptr ++;
      }
  
      return skip_ws(ptr);
  }
  
  static
  const char* parse_identifier(
      const char *ptr, 
      char *buff,
      char *params,
      ecs_meta_parse_ctx_t *ctx) 
  {
      ecs_assert(ptr != NULL, ECS_INTERNAL_ERROR, NULL);
      ecs_assert(buff != NULL, ECS_INTERNAL_ERROR, NULL);
      ecs_assert(ctx != NULL, ECS_INTERNAL_ERROR, NULL);
  
      char *bptr = buff, ch;
  
      if (params) {
          params[0] = '\0';
      }
  
      /* Ignore whitespaces */
      ptr = skip_ws(ptr);
  
      if (!isalpha(*ptr)) {
          ecs_meta_error(ctx, ptr, 
              "invalid identifier (starts with '%c')", *ptr);
      }
  
      while ((ch = *ptr) && !isspace(ch) && ch != ';' && ch != ',' && ch != ')' && ch != '>') {
          /* Type definitions can contain macro's or templates */
          if (ch == '(' || ch == '<') {
              if (!params) {
                  ecs_meta_error(ctx, ptr, "unexpected %c", *ptr);
              }
  
              const char *end = skip_scope(ptr, ctx);
              ecs_os_strncpy(params, ptr, (ecs_size_t)(end - ptr));
              params[end - ptr] = '\0';
  
              ptr = end;
          } else {
              *bptr = ch;
              bptr ++;
              ptr ++;
          }
      }
  
      *bptr = '\0';
  
      if (!ch) {
          ecs_meta_error(ctx, ptr, "unexpected end of token");
      }
  
      return ptr;
  }
  
  static
  const char * ecs_meta_open_scope(
      const char *ptr,
      ecs_meta_parse_ctx_t *ctx)    
  {
      /* Skip initial whitespaces */
      ptr = skip_ws(ptr);
  
      /* Is this the start of the type definition? */
      if (ctx->decl == ptr) {
          if (*ptr != '{') {
              ecs_meta_error(ctx, ptr, "missing '{' in struct definition");     
          }
  
          ptr ++;
          ptr = skip_ws(ptr);
      }
  
      /* Is this the end of the type definition? */
      if (!*ptr) {
          ecs_meta_error(ctx, ptr, "missing '}' at end of struct definition");
      }   
  
      /* Is this the end of the type definition? */
      if (*ptr == '}') {
          ptr = skip_ws(ptr + 1);
          if (*ptr) {
              ecs_meta_error(ctx, ptr, 
                  "stray characters after struct definition");
          }
          return NULL;
      }
  
      return ptr;
  }
  
  const char* ecs_meta_parse_constant(
      const char *ptr,
      ecs_meta_constant_t *token,
      ecs_meta_parse_ctx_t *ctx)
  {    
      ptr = ecs_meta_open_scope(ptr, ctx);
      if (!ptr) {
          return NULL;
      }
  
      token->is_value_set = false;
  
      /* Parse token, constant identifier */
      ptr = parse_identifier(ptr, token->name, NULL, ctx);
      ptr = skip_ws(ptr);
  
      /* Explicit value assignment */
      if (*ptr == '=') {
          int64_t value = 0;
          ptr = parse_digit(ptr + 1, &value, ctx);
          token->value = value;
          token->is_value_set = true;
      }
  
      /* Expect a ',' or '}' */
      if (*ptr != ',' && *ptr != '}') {
          ecs_meta_error(ctx, ptr, "missing , after enum constant");
      }
  
      if (*ptr == ',') {
          return ptr + 1;
      } else {
          return ptr;
      }
  }
  
  static
  const char* ecs_meta_parse_type(
      const char *ptr,
      ecs_meta_type_t *token,
      ecs_meta_parse_ctx_t *ctx)
  {
      token->is_ptr = false;
      token->is_const = false;
  
      ptr = skip_ws(ptr);
  
      /* Parse token, expect type identifier or ECS_PROPERTY */
      ptr = parse_identifier(ptr, token->type, token->params, ctx);
  
      if (!strcmp(token->type, "ECS_PRIVATE")) {
          /* Members from this point are not stored in metadata */
          return NULL;
      }
  
      /* If token is const, set const flag and continue parsing type */
      if (!strcmp(token->type, "const")) {
          token->is_const = true;
  
          /* Parse type after const */
          ptr = parse_identifier(ptr + 1, token->type, token->params, ctx);
      }
  
      /* Check if type is a pointer */
      ptr = skip_ws(ptr);
      if (*ptr == '*') {
          token->is_ptr = true;
          ptr ++;
      }
  
      return ptr;
  }
  
  const char* ecs_meta_parse_member(
      const char *ptr,
      ecs_meta_member_t *token,
      ecs_meta_parse_ctx_t *ctx)
  {
      ptr = ecs_meta_open_scope(ptr, ctx);
      if (!ptr) {
          return NULL;
      }
  
      token->count = 1;
      token->is_partial = false;
  
      /* Parse member type */
      ptr = ecs_meta_parse_type(ptr, &token->type, ctx);
      if (!ptr) {
          /* If NULL is returned, parsing should stop */
          token->is_partial = true;
          return NULL;
      }
  
      /* Next token is the identifier */
      ptr = parse_identifier(ptr, token->name, NULL, ctx);
  
      /* Skip whitespace between member and [ or ; */
      ptr = skip_ws(ptr);
  
      /* Check if this is an array */
      char *array_start = strchr(token->name, '[');
      if (!array_start) {
          /* If the [ was separated by a space, it will not be parsed as part of
           * the name */
          if (*ptr == '[') {
              array_start = (char*)ptr; /* safe, will not be modified */
          }
      }
  
      if (array_start) {
          /* Check if the [ matches with a ] */
          char *array_end = strchr(array_start, ']');
          if (!array_end) {
              ecs_meta_error(ctx, ptr, "missing ']'");
  
          } else if (array_end - array_start == 0) {
              ecs_meta_error(ctx, ptr, "dynamic size arrays are not supported");
          }
  
          token->count = atoi(array_start + 1);
  
          if (array_start == ptr) {
              /* If [ was found after name, continue parsing after ] */
              ptr = array_end + 1;
          } else {
              /* If [ was fonud in name, replace it with 0 terminator */
              array_start[0] = '\0';
          }
      }
  
      /* Expect a ; */
      if (*ptr != ';') {
          ecs_meta_error(ctx, ptr, "missing ; after member declaration");
      }
  
      return ptr + 1;
  }
  
  void ecs_meta_parse_params(
      const char *ptr,
      ecs_meta_params_t *token,
      ecs_meta_parse_ctx_t *ctx)
  {
      token->is_key_value = false;
      token->is_fixed_size = false;
  
      ptr = skip_ws(ptr);
      if (*ptr != '(' && *ptr != '<') {
          ecs_meta_error(ctx, ptr, 
              "expected '(' at start of collection definition");
      }
  
      ptr ++;
  
      /* Parse type identifier */
      ptr = ecs_meta_parse_type(ptr, &token->type, ctx);
      ptr = skip_ws(ptr);
      
      /* If next token is a ',' the first type was a key type */
      if (*ptr == ',') {
          ptr = skip_ws(ptr + 1);
          
          if (isdigit(*ptr)) {
              int64_t value;
              ptr = parse_digit(ptr, &value, ctx);
              token->count = value;
              token->is_fixed_size = true;
          } else {
              token->key_type = token->type;
  
              /* Parse element type */
              ptr = ecs_meta_parse_type(ptr, &token->type, ctx);
              ptr = skip_ws(ptr);
  
              token->is_key_value = true;
          }
      }
  
      if (*ptr != ')' && *ptr != '>') {
          ecs_meta_error(ctx, ptr, 
              "expected ')' at end of collection definition");
      }
  }
  