Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@
/modules
examples/example
tests
tests.exe
tests.pdb
106 changes: 106 additions & 0 deletions tests.jai
Original file line number Diff line number Diff line change
Expand Up @@ -251,4 +251,110 @@ main :: () {

log("Report after messages:");
report_memory_leaks();

{
TABLE_JSON :: #string EOF
{"1": 1, "2": 3, "3": 5}
EOF

success, result := json_parse_string(TABLE_JSON, Table(string, int));
defer {
for val, key: result free(key);
deinit(*result);
}

print("\ntable1:\n");
for val, key: result print("key=%, value=%\n", key, val);
}
log("Report after table1:");
report_memory_leaks();

{
TABLE_JSON :: #string EOF
{"1": 1, "2": 3, "3": 5}
EOF

success, result := json_parse_string(TABLE_JSON, Table(string, *int));
defer {
for val, key: result {
free(key);
free(val);
}
deinit(*result);
}

print("\ntable2:\n");
for val, key: result print("key=%, value=%\n", key, val.*);
}
log("Report after table2:");
report_memory_leaks();

{
TABLE_JSON :: #string EOF
{"1": {"num":1}, "2": {"num":3}, "3": {"num":5}}
EOF

success, result := json_parse_string(TABLE_JSON, Table(string, struct{num: s16;}));
defer {
for val, key: result {
free(key);
}
deinit(*result);
}

print("\ntable3:\n");
for val, key: result print("key=%, value=%\n", key, val);
}
log("Report after table3:");
report_memory_leaks();

{
TABLE_JSON :: #string EOF
{"1": {"num":1}, "2": {"num":3}, "3": {"num":5}}
EOF

success, result := json_parse_string(TABLE_JSON, Table(string, *struct{num: s16;}));
defer {
for val, key: result {
free(key);
free(val);
}
deinit(*result);
}

print("\ntable4:\n");
for val, key: result print("key=%, value=%\n", key, val.*);
}
log("Report after table4:");
report_memory_leaks();

{
Test_Struct :: struct {
table: Table(string, struct{num: s16;});
other: int;
}

TABLE_JSON :: #string EOF
{
table: {"1": {"num":1}, "2": {"num":3}, "3": {"num":5}},
other: 135813
}
EOF

// Nested tables are NOT supported
success, result := json_parse_string(TABLE_JSON, Test_Struct);
defer {
for val, key: result.table {
free(key);
}
deinit(*result.table);
}

print("\ntable5:\n");
for val, key: result.table print("key=%, value=%\n", key, val);

print("other = %\n", result.other);
}
log("Report after table5:");
report_memory_leaks();
}
128 changes: 77 additions & 51 deletions typed.jai
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ json_parse_string :: (content: string, $T: Type, ignore_unknown := true, rename
if !content then return false, result;

info := type_info(T);
remainder, success := parse_value(content, cast(*u8)*result, info, ignore_unknown, "", rename=rename, float_handling=float_handling);
remainder, success := parse_value(content, cast(*u8)*result, T, info, ignore_unknown, "", rename=rename, float_handling=float_handling);
if !success return false, result;

remainder = trim_left(remainder, WHITESPACE_CHARS);
Expand Down Expand Up @@ -173,7 +173,7 @@ is_generic_json_value :: (info: *Type_Info) -> bool {
return info == type_info(JSON_Value);
}

parse_value :: (to_parse: string, slot: *u8, info: *Type_Info, ignore_unknown: bool, field_name: string, rename: Rename_Proc, float_handling: Special_Float_Handling) -> remainder: string, success: bool {
parse_value :: (to_parse: string, slot: *u8, $T: Type, info: *Type_Info, ignore_unknown: bool, field_name: string, rename: Rename_Proc, float_handling: Special_Float_Handling) -> remainder: string, success: bool {
remainder := trim_left(to_parse, WHITESPACE_CHARS);
if !remainder return "", false;
success := true;
Expand Down Expand Up @@ -202,31 +202,31 @@ parse_value :: (to_parse: string, slot: *u8, info: *Type_Info, ignore_unknown: b
return null, false, false, value_info, false;
}

// @Incomplete: Add support for Tables of Any Types.
// Currently all the `Type_Info` handling is happening at
// Runtime. But supporting Tables of Values that differ in
// size requires polymorphing such operations as `table_add()`
// which means we need to know the `Type_Info`-s at compile
// time. Until the type.jai is refactored to pass all the
// necessary `Type_Info` with `$` we can only offer Table of
// Pointers.
// - rexim 2026-01-20
is_type_info_table_of_pointers :: (info: *Type_Info) -> (value_info: *Type_Info_Pointer) {
is_type_info_table :: (info: *Type_Info) -> (value_info: *Type_Info) {

if (info.type != .STRUCT) return null;
struct_info := cast(*Type_Info_Struct)info;
if (struct_info.name != "Table") return null;

struct_info := info.(*Type_Info_Struct);
if struct_info.name != "Table" return null;

for struct_info.specified_parameters {

if it.name == "Value_Type" {
if (it.type.type == .TYPE) {
value_info := (cast(**Type_Info)(struct_info.constant_storage.data + it.offset_into_constant_storage)).*;
if value_info.type != .POINTER return null;
return cast(*Type_Info_Pointer)value_info;

if it.type.type != .TYPE {
// Value_Type struct param that is not a Type means we are dealing with something unexpected
break;
}

value_info := (cast(**Type_Info)(struct_info.constant_storage.data + it.offset_into_constant_storage)).*;
return value_info;
}
}

return null;
}
member_info := is_type_info_table_of_pointers(value_info);

member_info := is_type_info_table(value_info);
if member_info {
return slot, true, false, member_info, true;
}
Expand Down Expand Up @@ -362,7 +362,7 @@ parse_value :: (to_parse: string, slot: *u8, info: *Type_Info, ignore_unknown: b
value, remainder, success = parse_array(remainder);
json_set(cast(*JSON_Value)value_slot, value);
} else {
remainder, success = parse_array(remainder, value_slot, cast(*Type_Info_Array) value_info, ignore_unknown, rename=rename, float_handling=float_handling);
remainder, success = parse_array(remainder, value_slot, T, cast(*Type_Info_Array) value_info, ignore_unknown, rename=rename, float_handling=float_handling);
}
}
case #char "{";
Expand All @@ -378,9 +378,9 @@ parse_value :: (to_parse: string, slot: *u8, info: *Type_Info, ignore_unknown: b
value.*, remainder, success = parse_object(remainder);
json_set(cast(*JSON_Value)value_slot, value);
} else if is_table {
remainder, success = parse_object_as_table_of_pointers(remainder, value_slot, cast(*Type_Info_Pointer)value_info, ignore_unknown, rename=rename, float_handling=float_handling);
remainder, success = parse_object_as_table(remainder, value_slot, T, value_info, ignore_unknown, rename=rename, float_handling=float_handling);
} else {
remainder, success = parse_object(remainder, value_slot, cast(*Type_Info_Struct) value_info, ignore_unknown, rename=rename, float_handling=float_handling);
remainder, success = parse_object(remainder, value_slot, T, cast(*Type_Info_Struct) value_info, ignore_unknown, rename=rename, float_handling=float_handling);
}
}
case;
Expand Down Expand Up @@ -494,7 +494,7 @@ parse_enum_string :: (str: string, slot: *u8, info_enum: *Type_Info_Enum) -> rem
return remainder, success;
}

parse_array :: (str: string, slot: *u8, info: *Type_Info_Array, ignore_unknown: bool, rename: Rename_Proc, float_handling: Special_Float_Handling) -> remainder: string, success: bool {
parse_array :: (str: string, slot: *u8, $T: Type, info: *Type_Info_Array, ignore_unknown: bool, rename: Rename_Proc, float_handling: Special_Float_Handling) -> remainder: string, success: bool {
element_size: int;
if slot {
element_size = info.element_type.runtime_size;
Expand Down Expand Up @@ -551,7 +551,7 @@ parse_array :: (str: string, slot: *u8, info: *Type_Info_Array, ignore_unknown:
memset(element_data, 0, element_size);
}

remainder, success = parse_value(remainder, element_data, info.element_type, ignore_unknown, "", rename=rename, float_handling=float_handling);
remainder, success = parse_value(remainder, element_data, T, info.element_type, ignore_unknown, "", rename=rename, float_handling=float_handling);
if !success break;

remainder = trim_left(remainder, WHITESPACE_CHARS);
Expand Down Expand Up @@ -600,7 +600,7 @@ parse_array :: (str: string, slot: *u8, info: *Type_Info_Array, ignore_unknown:
}
first = false;

remainder, success = parse_value(remainder, null, null, ignore_unknown, "", rename=rename, float_handling=float_handling);
remainder, success = parse_value(remainder, null, T, null, ignore_unknown, "", rename=rename, float_handling=float_handling);
if !success return remainder, false;

remainder = trim_left(remainder, WHITESPACE_CHARS);
Expand Down Expand Up @@ -631,7 +631,18 @@ fill_member_table :: (table: *Table(string, Member_Offset), info: *Type_Info_Str
}
}

parse_object_as_table_of_pointers :: (str: string, slot: *u8, value_info: *Type_Info_Pointer, ignore_unknown: bool, rename: Rename_Proc, float_handling: Special_Float_Handling) -> remainder: string, success: bool {
parse_object_as_table :: (str: string, slot: *u8, $T: Type, value_info: *Type_Info, ignore_unknown: bool, rename: Rename_Proc, float_handling: Special_Float_Handling) -> remainder: string, success: bool {

IS_TABLE :: #run begins_with(sprint("%", T), "Table(");
IS_POINTER_VALUE_TYPE :: #run -> bool {

#if IS_TABLE {
return type_info(T.Value_Type).type == .POINTER;
} else {
return false;
}
};

assert(str[0] == #char "{", "Invalid object start %", str);
remainder := advance(str);
remainder = trim_left(remainder, WHITESPACE_CHARS);
Expand All @@ -642,40 +653,55 @@ parse_object_as_table_of_pointers :: (str: string, slot: *u8, value_info: *Type_
return remainder, true;
}

table := cast(*Table(string, *void))slot;
#if IS_TABLE {
table := slot.(*Table(string, T.Value_Type));

while true {
if !remainder || remainder[0] != #char "\"" return remainder, false;
while true {
if !remainder || remainder[0] != #char "\"" return remainder, false;

key: string;
success: bool;
key, remainder, success = parse_string(remainder);
if !success return remainder, false;
defer free(key);
key: string;
success: bool;
key, remainder, success = parse_string(remainder);
if !success return remainder, false;
defer free(key);

remainder = trim_left(remainder, WHITESPACE_CHARS);
if !remainder || remainder[0] != #char ":" return remainder, false;
remainder = advance(remainder);
remainder = trim_left(remainder, WHITESPACE_CHARS);
if !remainder || remainder[0] != #char ":" return remainder, false;
remainder = advance(remainder);

member_info := value_info.pointer_to;
member_slot := NewArray(member_info.runtime_size, u8).data;
remainder, success = parse_value(remainder, member_slot, member_info, ignore_unknown, key, rename, float_handling=float_handling);
if !success return remainder, false;
#if IS_POINTER_VALUE_TYPE {
member_info := value_info.(*Type_Info_Pointer).pointer_to;
member_slot := NewArray(member_info.runtime_size, u8).data;
remainder, success = parse_value(remainder, member_slot, T, member_info, ignore_unknown, key, rename, float_handling=float_handling);
if !success return remainder, false;

table_add(table, copy_string(key), member_slot);
table_add(table, copy_string(key), member_slot.(T.Value_Type));
} else {

remainder = trim_left(remainder, WHITESPACE_CHARS);
if !remainder || remainder[0] != #char "," break;
member_info := value_info;
member_slot: T.Value_Type;
remainder, success = parse_value(remainder, (*member_slot).(*u8), T, member_info, ignore_unknown, key, rename, float_handling=float_handling);
if !success return remainder, false;

table_add(table, copy_string(key), member_slot);
}

remainder = trim_left(remainder, WHITESPACE_CHARS);
if !remainder || remainder[0] != #char "," break;
remainder = advance(remainder);
remainder = trim_left(remainder, WHITESPACE_CHARS);
}

if !remainder || remainder[0] != #char "}" return remainder, false;
remainder = advance(remainder);
remainder = trim_left(remainder, WHITESPACE_CHARS);
return remainder, true;
} else {
// Should never happen
return "", false;
}

if !remainder || remainder[0] != #char "}" return remainder, false;
remainder = advance(remainder);
return remainder, true;
}

parse_object :: (str: string, slot: *u8, info: *Type_Info_Struct, ignore_unknown: bool, rename: Rename_Proc, float_handling: Special_Float_Handling) -> remainder: string, success: bool {
parse_object :: (str: string, slot: *u8, $T: Type, info: *Type_Info_Struct, ignore_unknown: bool, rename: Rename_Proc, float_handling: Special_Float_Handling) -> remainder: string, success: bool {
assert(str[0] == #char "{", "Invalid object start %", str);
remainder := advance(str);
remainder = trim_left(remainder, WHITESPACE_CHARS);
Expand Down Expand Up @@ -719,7 +745,7 @@ parse_object :: (str: string, slot: *u8, info: *Type_Info_Struct, ignore_unknown
if !remainder || remainder[0] != #char ":" return remainder, false;
remainder = advance(remainder);

remainder, success = parse_value(remainder, member_slot, member_info, ignore_unknown, key, rename, float_handling=float_handling);
remainder, success = parse_value(remainder, member_slot, T, member_info, ignore_unknown, key, rename, float_handling=float_handling);
if !success return remainder, false;

remainder = trim_left(remainder, WHITESPACE_CHARS);
Expand Down