// Copyright (c) 2023 Christoffer Lerno. All rights reserved. // Use of this source code is governed by the MIT license // a copy of which can be found in the LICENSE_STDLIB file. module std::collections::object; import std::collections::map; import std::collections::list; import std::io; const Object TRUE_OBJECT = { .b = true, .type = bool.typeid }; const Object FALSE_OBJECT = { .b = false, .type = bool.typeid }; const Object NULL_OBJECT = { .type = void*.typeid }; struct Object { typeid type; Allocator* allocator; union { uint128 i; double f; bool b; String s; void* other; ObjectInternalList array; ObjectInternalMap map; } } fn void! Object.to_format(&self, Formatter* formatter) @dynamic { switch (self.type) { case void: formatter.printf("{}")!; case void*: formatter.printf("null")!; case String: formatter.printf(`"%s"`, self.s)!; case bool: formatter.printf(self.b ? "true" : "false")!; case ObjectInternalList: formatter.printf("[")!; foreach (i, ol : self.array) { formatter.printf(i == 0 ? " " : ", ")!; ol.to_format(formatter)!; } formatter.printf(" ]")!; case ObjectInternalMap: formatter.printf("{")!; @pool() { foreach (i, key : self.map.key_tlist()) { formatter.printf(i == 0 ? " " : ", ")!; formatter.printf(`"%s": `, key)!; self.map.get(key).to_format(formatter)!; } }; formatter.printf(" }")!; default: switch (self.type.kindof) { case SIGNED_INT: formatter.printf("%d", self.i)!; case UNSIGNED_INT: formatter.printf("%d", (uint128)self.i)!; case FLOAT: formatter.printf("%d", self.f)!; case ENUM: formatter.printf("%d", self.i)!; default: formatter.printf("<>")!; } } } fn Object* new_obj(Allocator* using = mem::heap()) { Object* o = malloc(Object, .using = using); *o = { .allocator = using, .type = void.typeid }; return o; } fn Object* new_null() { return &NULL_OBJECT; } fn Object* new_int(int128 i, Allocator* using = mem::heap()) { Object* o = malloc(Object, .using = using); *o = { .i = i, .allocator = using, .type = int128.typeid }; return o; } macro Object* new_enum(e, Allocator* using = mem::heap()) { Object* o = malloc(Object, .using = using); *o = { .i = (int128)e, .allocator = using, .type = $typeof(e).typeid }; return o; } fn Object* new_float(double f, Allocator* using = mem::current_allocator()) { Object* o = malloc(Object, .using = using); *o = { .f = f, .allocator = using, .type = double.typeid }; return o; } fn Object* new_string(String s, Allocator* using = mem::heap()) { Object* o = malloc(Object, .using = using); *o = { .s = s.copy(using), .allocator = using, .type = String.typeid }; return o; } fn Object* new_bool(bool b) { return b ? &TRUE_OBJECT : &FALSE_OBJECT; } fn void Object.free(&self) { switch (self.type) { case void: break; case String: free(self.s, .using = self.allocator); case ObjectInternalList: foreach (ol : self.array) { ol.free(); } self.array.free(); case ObjectInternalMap: @pool() { foreach (key : self.map.key_tlist()) { (void)self.map.get(key).free(); free(key, .using = self.allocator); } self.map.free(); }; default: break; } if (self.allocator) free(self, .using = self.allocator); } fn bool Object.is_null(&self) @inline => self == &NULL_OBJECT; fn bool Object.is_empty(&self) @inline => self.type == void.typeid; fn bool Object.is_map(&self) @inline => self.type == ObjectInternalMap.typeid; fn bool Object.is_array(&self) @inline => self.type == ObjectInternalList.typeid; fn bool Object.is_bool(&self) @inline => self.type == bool.typeid; fn bool Object.is_string(&self) @inline => self.type == String.typeid; fn bool Object.is_float(&self) @inline => self.type == double.typeid; fn bool Object.is_int(&self) @inline => self.type == int128.typeid; fn bool Object.is_keyable(&self) => self.is_empty() || self.is_map(); fn bool Object.is_indexable(&self) => self.is_empty() || self.is_array(); /** * @require self.is_keyable() **/ fn void Object.init_map_if_needed(&self) @private { if (self.is_empty()) { self.type = ObjectInternalMap.typeid; self.map.init(.using = self.allocator); } } /** * @require self.is_indexable() **/ fn void Object.init_array_if_needed(&self) @private { if (self.is_empty()) { self.type = ObjectInternalList.typeid; self.array.init(.using = self.allocator); } } /** * @require self.is_keyable() **/ fn void Object.set_object(&self, String key, Object* new_object) @private { self.init_map_if_needed(); ObjectInternalMapEntry*! entry = self.map.get_entry(key); defer { (void)free(entry.key, .using = self.allocator); (void)entry.value.free(); } self.map.set(key.copy(self.map.allocator), new_object); } macro Object* object_from_value(value) @private { var $Type = $typeof(value); $switch $case types::is_int($Type): return new_int(value); $case types::is_float($Type): return new_float(value); $case $Type.typeid == String.typeid: return new_string(value); $case $Type.typeid == bool.typeid: return new_bool(value); $case $Type.typeid == Object*.typeid: return value; $case $Type.typeid == void*.typeid: assert(value == null); return &NULL_OBJECT; $case $checks(String s = value): return new_string(value); $default: $error "Unsupported object type."; $endswitch } macro Object* Object.set(&self, String key, value) { Object* val = object_from_value(value); self.set_object(key, val); return val; } /** * @require self.is_indexable() **/ macro Object* Object.set_at(&self, usz index, String key, value) { Object* val = object_from_value(value); self.set_object_at(key, index, val); return val; } /** * @require self.is_indexable() * @ensure return != null **/ macro Object* Object.append(&self, value) { Object* val = object_from_value(value); self.append_object(val); return val; } /** * @require self.is_keyable() **/ fn Object*! Object.get(&self, String key) => self.is_empty() ? SearchResult.MISSING? : self.map.get(key); fn bool Object.has_key(&self, String key) => self.is_map() && self.map.has_key(key); /** * @require self.is_indexable() **/ fn Object* Object.get_at(&self, usz index) { return self.array.get(index); } /** * @require self.is_indexable() **/ fn void Object.append_object(&self, Object* to_append) { self.init_array_if_needed(); self.array.append(to_append); } /** * @require self.is_indexable() **/ fn void Object.set_object_at(&self, usz index, Object* to_set) { self.init_array_if_needed(); while (self.array.len() < index) { self.array.append(&NULL_OBJECT); } if (self.array.len() == index) { self.array.append(to_set); return; } self.array.get(index).free(); self.array.set_at(index, to_set); } macro get_integer_value(Object* value, $Type) { if (value.is_float()) { return ($Type)value.f; } if (value.is_string()) { $if $Type.kindof == TypeKind.SIGNED_INT: return ($Type)value.s.to_int128(); $else return ($Type)value.s.to_uint128(); $endif } if (!value.is_int()) return NumberConversion.MALFORMED_INTEGER?; return ($Type)value.i; } /** * @require self.is_indexable() **/ macro Object.get_integer_at(&self, $Type, usz index) @private { return get_integer_value(self.get_at(index), $Type); } /** * @require self.is_keyable() **/ macro Object.get_integer(&self, $Type, String key) @private { return get_integer_value(self.get(key), $Type); } fn ichar! Object.get_ichar(&self, String key) => self.get_integer(ichar, key); fn short! Object.get_short(&self, String key) => self.get_integer(short, key); fn int! Object.get_int(&self, String key) => self.get_integer(int, key); fn long! Object.get_long(&self, String key) => self.get_integer(long, key); fn int128! Object.get_int128(&self, String key) => self.get_integer(int128, key); fn ichar! Object.get_ichar_at(&self, usz index) => self.get_integer_at(ichar, index); fn short! Object.get_short_at(&self, usz index) => self.get_integer_at(short, index); fn int! Object.get_int_at(&self, usz index) => self.get_integer_at(int, index); fn long! Object.get_long_at(&self, usz index) => self.get_integer_at(long, index); fn int128! Object.get_int128_at(&self, usz index) => self.get_integer_at(int128, index); fn char! Object.get_char(&self, String key) => self.get_integer(ichar, key); fn short! Object.get_ushort(&self, String key) => self.get_integer(ushort, key); fn uint! Object.get_uint(&self, String key) => self.get_integer(uint, key); fn ulong! Object.get_ulong(&self, String key) => self.get_integer(ulong, key); fn uint128! Object.get_uint128(&self, String key) => self.get_integer(uint128, key); fn char! Object.get_char_at(&self, usz index) => self.get_integer_at(char, index); fn ushort! Object.get_ushort_at(&self, usz index) => self.get_integer_at(ushort, index); fn uint! Object.get_uint_at(&self, usz index) => self.get_integer_at(uint, index); fn ulong! Object.get_ulong_at(&self, usz index) => self.get_integer_at(ulong, index); fn uint128! Object.get_uint128_at(&self, usz index) => self.get_integer_at(uint128, index); /** * @require self.is_keyable() **/ fn String! Object.get_string(&self, String key) { Object* value = self.get(key)!; assert(value.is_string()); return value.s; } /** * @require self.is_indexable() **/ fn String Object.get_string_at(&self, usz index) { Object* value = self.get_at(index); assert(value.is_string()); return value.s; } /** * @require self.is_keyable() **/ macro String! Object.get_enum(&self, $EnumType, String key) { Object value = self.get(key)!; assert($EnumType.typeid == value.type); return ($EnumType)value.i; } /** * @require self.is_indexable() **/ macro String Object.get_enum_at(&self, $EnumType, usz index) { Object value = self.get_at(index); assert($EnumType.typeid == value.type); return ($EnumType)value.i; } /** * @require self.is_keyable() **/ fn bool! Object.get_bool(&self, String key) { Object* value = self.get(key)!; assert(value.is_bool()); return value.b; } /** * @require self.is_indexable() **/ fn bool Object.get_bool_at(&self, usz index) { Object* value = self.get_at(index); assert(value.is_bool()); return value.b; } /** * @require self.is_keyable() **/ fn double! Object.get_float(&self, String key) { Object* value = self.get(key)!; switch (value.type.kindof) { case SIGNED_INT: return (double)value.i; case UNSIGNED_INT: return (double)(uint128)value.i; case FLOAT: return value.f; default: unreachable(); } } /** * @require self.is_indexable() **/ fn double Object.get_float_at(&self, usz index) { Object* value = self.get_at(index); switch (value.type.kindof) { case SIGNED_INT: return (double)value.i; case UNSIGNED_INT: return (double)(uint128)value.i; case FLOAT: return value.f; default: unreachable(); } } fn Object* Object.get_or_create_obj(&self, String key) { if (try obj = self.get(key) && !obj.is_null()) return obj; Object* container = new_obj(); self.set(key, container); return container; } def ObjectInternalMap = HashMap() @private; def ObjectInternalList = List() @private; def ObjectInternalMapEntry = Entry() @private;