mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
484 lines
12 KiB
C
484 lines
12 KiB
C
// 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 (Printable)
|
|
{
|
|
typeid type;
|
|
Allocator* allocator;
|
|
union
|
|
{
|
|
uint128 i;
|
|
double f;
|
|
bool b;
|
|
String s;
|
|
void* other;
|
|
ObjectInternalList array;
|
|
ObjectInternalMap map;
|
|
}
|
|
}
|
|
|
|
|
|
fn usz! Object.to_format(&self, Formatter* formatter) @dynamic
|
|
{
|
|
switch (self.type)
|
|
{
|
|
case void:
|
|
return formatter.printf("{}")!;
|
|
case void*:
|
|
return formatter.printf("null")!;
|
|
case String:
|
|
return formatter.printf(`"%s"`, self.s)!;
|
|
case bool:
|
|
return formatter.printf(self.b ? "true" : "false")!;
|
|
case ObjectInternalList:
|
|
usz n = formatter.printf("[")!;
|
|
foreach (i, ol : self.array)
|
|
{
|
|
if (i > 0) n += formatter.printf(",")!;
|
|
n += ol.to_format(formatter)!;
|
|
}
|
|
n += formatter.printf("]")!;
|
|
return n;
|
|
case ObjectInternalMap:
|
|
usz n = formatter.printf("{")!;
|
|
@pool()
|
|
{
|
|
foreach (i, key : self.map.key_tlist())
|
|
{
|
|
if (i > 0) n += formatter.printf(",")!;
|
|
n += formatter.printf(`"%s":`, key)!;
|
|
n += self.map.get(key).to_format(formatter)!;
|
|
}
|
|
};
|
|
n += formatter.printf("}")!;
|
|
return n;
|
|
default:
|
|
switch (self.type.kindof)
|
|
{
|
|
case SIGNED_INT:
|
|
return formatter.printf("%d", self.i)!;
|
|
case UNSIGNED_INT:
|
|
return formatter.printf("%d", (uint128)self.i)!;
|
|
case FLOAT:
|
|
return formatter.printf("%d", self.f)!;
|
|
case ENUM:
|
|
return formatter.printf("%d", self.i)!;
|
|
default:
|
|
return 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::heap())
|
|
{
|
|
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:
|
|
self.map.@each_entry(; ObjectInternalMapEntry* entry) {
|
|
free(entry.key, .using = self.allocator);
|
|
entry.value.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.object_from_value(&self, value) @private
|
|
{
|
|
var $Type = $typeof(value);
|
|
|
|
$switch
|
|
$case types::is_int($Type):
|
|
return new_int(value, self.allocator);
|
|
$case types::is_float($Type):
|
|
return new_float(value, self.allocator);
|
|
$case $Type.typeid == String.typeid:
|
|
return new_string(value, self.allocator);
|
|
$case $Type.typeid == bool.typeid:
|
|
return new_bool(value);
|
|
$case $Type.typeid == Object*.typeid:
|
|
return value;
|
|
$case $Type.typeid == void*.typeid:
|
|
if (value != null) return CastResult.TYPE_MISMATCH?;
|
|
return &NULL_OBJECT;
|
|
$case $checks(String s = value):
|
|
return new_string(value, self.allocator);
|
|
$default:
|
|
$error "Unsupported object type.";
|
|
$endswitch
|
|
|
|
}
|
|
|
|
macro Object* Object.set(&self, String key, value)
|
|
{
|
|
Object* val = self.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 = self.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 = self.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 usz Object.get_len(&self)
|
|
{
|
|
return self.array.len();
|
|
}
|
|
|
|
/**
|
|
* @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);
|
|
}
|
|
|
|
/**
|
|
* @require $Type.kindof.is_int() "Expected an integer type."
|
|
* @checked ($Type)1.0 "Expected an integer type."
|
|
**/
|
|
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()
|
|
* @require $Type.kindof.is_int() : "Expected an integer type"
|
|
**/
|
|
macro Object.get_integer_at(&self, $Type, usz index) @private
|
|
{
|
|
return get_integer_value(self.get_at(index), $Type);
|
|
}
|
|
|
|
/**
|
|
* @require self.is_keyable()
|
|
* @require $Type.kindof.is_int() : "Expected an integer type"
|
|
**/
|
|
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)!;
|
|
if (!value.is_string()) return CastResult.TYPE_MISMATCH?;
|
|
return value.s;
|
|
}
|
|
|
|
/**
|
|
* @require self.is_indexable()
|
|
**/
|
|
fn String! Object.get_string_at(&self, usz index)
|
|
{
|
|
Object* value = self.get_at(index);
|
|
if (!value.is_string()) return CastResult.TYPE_MISMATCH?;
|
|
return value.s;
|
|
}
|
|
|
|
/**
|
|
* @require self.is_keyable()
|
|
**/
|
|
macro String! Object.get_enum(&self, $EnumType, String key)
|
|
{
|
|
Object value = self.get(key)!;
|
|
if ($EnumType.typeid != value.type) return CastResult.TYPE_MISMATCH?;
|
|
return ($EnumType)value.i;
|
|
}
|
|
|
|
/**
|
|
* @require self.is_indexable()
|
|
**/
|
|
macro String! Object.get_enum_at(&self, $EnumType, usz index)
|
|
{
|
|
Object value = self.get_at(index);
|
|
if ($EnumType.typeid != value.type) return CastResult.TYPE_MISMATCH?;
|
|
return ($EnumType)value.i;
|
|
}
|
|
|
|
/**
|
|
* @require self.is_keyable()
|
|
**/
|
|
fn bool! Object.get_bool(&self, String key)
|
|
{
|
|
Object* value = self.get(key)!;
|
|
if (!value.is_bool()) return CastResult.TYPE_MISMATCH?;
|
|
return value.b;
|
|
}
|
|
|
|
|
|
/**
|
|
* @require self.is_indexable()
|
|
**/
|
|
fn bool! Object.get_bool_at(&self, usz index)
|
|
{
|
|
Object* value = self.get_at(index);
|
|
if (!value.is_bool()) return CastResult.TYPE_MISMATCH?;
|
|
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:
|
|
return CastResult.TYPE_MISMATCH?;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @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:
|
|
return CastResult.TYPE_MISMATCH?;
|
|
}
|
|
}
|
|
|
|
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.allocator);
|
|
self.set(key, container);
|
|
return container;
|
|
}
|
|
|
|
def ObjectInternalMap = HashMap(<String, Object*>) @private;
|
|
def ObjectInternalList = List(<Object*>) @private;
|
|
def ObjectInternalMapEntry = Entry(<String, Object*>) @private;
|
|
|