Skip to content

Conversation

@rexim
Copy link

@rexim rexim commented Jan 20, 2026

Tries to address #15

Example:

main :: () {
    json_string := "{\"foo\": 69, \"bar\": 420}";
    success, json := json_parse_string(json_string, Table(string, *int));
    if !success exit(69);
    for json {
        print("% => %\n", it_index, it.*);
    }
}
 #import "jaison";

Why Tables of Pointers only:

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 that.

Unless I'm missing something. Please let me know if I do.

If I get a spare minute in the foreseeable future, I can spend it on doing the refactoring to bring the support for more Table kinds, but if somebody else wants to tackle it I won't mind it.

Please let me know if you want me to make any changes to the patch.

Thank you for making this very useful library!

rexim added 2 commits January 20, 2026 23:20
Tries to address rluba#15

Example:

```jai
main :: () {
    json_string := "{\"foo\": 69, \"bar\": 420}";
    success, json := json_parse_string(json_string, Table(string, *int));
    if !success exit(69);
    for json {
        print("% => %\n", it_index, it.*);
    }
}
 #import "jaison";
```

Why Tables of Pointers only:

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 that.

Unless I'm missing something. Please let me know if I do.

If I get a spare minute in the foreseeable future, I can spend it on
doing the refactoring to bring the support for more Table kinds, but
if somebody else wants to tuckle it I won't mind it.

Please let me know if you want me to make any changes to the patch.

Thank you for making this very useful library!
Since Table is a .STRUCT anyway expecting a .STRUCT when encountring a
JSON object should work out well in this case.
@bloeys
Copy link

bloeys commented Jan 20, 2026

I posted this on X but it's probably better here:

If your issue is finding the size+type of the table value at runtime, you can do this:

#import "Hash_Table";

test :: ($T: Type) {
    val_ti := type_info(T.Value_Type);
    print("Table value type info = %\n", val_ti.*);
}

test(Table(string, s16));

This works because Value_Type is a constant of type Type, which means we can use it to get Type_Info for the table's value, and from there decide what to do.

@rexim
Copy link
Author

rexim commented Jan 20, 2026

I posted this on X but it's probably better here:

If your issue is finding the size+type of the table value at runtime, you can do this:

#import "Hash_Table";

test :: ($T: Type) {
    val_ti := type_info(T.Value_Type);
    print("Table value type info = %\n", val_ti.*);
}

test(Table(string, s16));

This works because Value_Type is a constant of type Type, which means we can use it to get Type_Info for the table's value, and from there decide what to do.

Thank you for the suggestion! The problem is actually that we need to know it at compile time. Otherwise we can't polymorph the table_add() call. Unless, again, I'm missing something about how Jai works.

@bloeys
Copy link

bloeys commented Jan 21, 2026

Thank you for the suggestion! The problem is actually that we need to know it at compile time. Otherwise we can't polymorph the table_add() call. Unless, again, I'm missing something about how Jai works.

Ah I see, sorry for the confusion. A slight modification of your code makes it work for both pointers and non-pointers:

parse_object_as_table_of_pointers :: (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 {

	assert(str[0] == #char "{", "Invalid object start %", str);
	remainder := advance(str);
	remainder = trim_left(remainder, WHITESPACE_CHARS);
	if !remainder return "", false;

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

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

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

		return false;
	};

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

		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);

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

			#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.(T.Value_Type));
			} else {

				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);
		return remainder, true;

	} else {
		// Should never happen
		return "", false;
	}
}

Of course the only requirement is that $T: Type gets passed from json_parse_string down, but that's the only other change to your PR!

Notes:

  • The IS_TABLE check is a bit retarded and can probably be improved
  • IS_TABLE should be moved up the call stack and removed from this proc. is_table in your PR can be made static and is_type_info_table_of_pointers removed. We often do a ton of JSON parsing, so any perf wins we can get are great.

@rexim
Copy link
Author

rexim commented Jan 21, 2026

@bloeys please submit an actual patch to https://github.com/tsoding/jaison to the branch table-support which implements what you've described in its full context so I can compile it and test it on my machine. :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants