#include <addons.h>

#define HEAD
#define LINE "\n"

void Plecs_null(void) {
    ecs_world_t *world = ecs_init();

    test_assert(ecs_plecs_from_str(world, NULL, NULL) == 0);

    ecs_fini(world);
}

void Plecs_empty(void) {
    ecs_world_t *world = ecs_init();

    test_assert(ecs_plecs_from_str(world, NULL, "") == 0);

    ecs_fini(world);
}

void Plecs_space(void) {
    ecs_world_t *world = ecs_init();

    test_assert(ecs_plecs_from_str(world, NULL, " ") == 0);

    ecs_fini(world);
}

void Plecs_space_newline(void) {
    ecs_world_t *world = ecs_init();

    test_assert(ecs_plecs_from_str(world, NULL, " \n \n") == 0);

    ecs_fini(world);
}

void Plecs_two_empty_newlines(void) {
    ecs_world_t *world = ecs_init();

    test_assert(ecs_plecs_from_str(world, NULL, "\n\n") == 0);

    ecs_fini(world);
}

void Plecs_three_empty_newlines(void) {
    ecs_world_t *world = ecs_init();

    test_assert(ecs_plecs_from_str(world, NULL, "\n\n\n") == 0);

    ecs_fini(world);
}

void Plecs_newline_trailing_space(void) {
    ecs_world_t *world = ecs_init();

    test_assert(ecs_plecs_from_str(world, NULL, "\n ") == 0);

    ecs_fini(world);
}

void Plecs_newline_trailing_spaces(void) {
    ecs_world_t *world = ecs_init();

    test_assert(ecs_plecs_from_str(world, NULL, "\n   ") == 0);

    ecs_fini(world);
}

void Plecs_multiple_trailing_newlines(void) {
    ecs_world_t *world = ecs_init();

    test_assert(ecs_plecs_from_str(world, NULL, "Foo\n\n\n") == 0);

    ecs_entity_t foo = ecs_lookup(world, "Foo");
    test_assert(foo != 0);
    test_str(ecs_get_name(world, foo), "Foo");
    test_int(ecs_get_type(world, foo)->count, 1);

    ecs_fini(world);
}

void Plecs_entity(void) {
    ecs_world_t *world = ecs_init();

    test_assert(ecs_plecs_from_str(world, NULL, "Foo") == 0);

    ecs_entity_t foo = ecs_lookup(world, "Foo");
    test_assert(foo != 0);
    test_str(ecs_get_name(world, foo), "Foo");
    test_int(ecs_get_type(world, foo)->count, 1);

    ecs_fini(world);
}

void Plecs_entity_w_entity(void) {
    ecs_world_t *world = ecs_init();

    test_assert(ecs_plecs_from_str(world, NULL, "Foo(Subj)") == 0);

    ecs_entity_t foo = ecs_lookup(world, "Foo");
    test_assert(foo != 0);
    test_str(ecs_get_name(world, foo), "Foo");

    ecs_entity_t subj = ecs_lookup(world, "Subj");
    test_assert(subj != 0);
    test_str(ecs_get_name(world, subj), "Subj");

    test_assert(ecs_has_id(world, subj, foo));

    ecs_fini(world);
}

void Plecs_entity_w_pair(void) {
    ecs_world_t *world = ecs_init();

    test_assert(ecs_plecs_from_str(world, NULL, "Rel(Subj, Obj)") == 0);

    ecs_entity_t rel = ecs_lookup(world, "Rel");
    test_assert(rel != 0);
    test_str(ecs_get_name(world, rel), "Rel");

    ecs_entity_t obj = ecs_lookup(world, "Obj");
    test_assert(obj != 0);
    test_str(ecs_get_name(world, obj), "Obj");

    ecs_entity_t subj = ecs_lookup(world, "Subj");
    test_assert(subj != 0);
    test_str(ecs_get_name(world, subj), "Subj");

    test_assert(ecs_has_pair(world, subj, rel, obj));

    ecs_fini(world);
}

void Plecs_2_entities(void) {
    ecs_world_t *world = ecs_init();

    test_assert(ecs_plecs_from_str(world, NULL, "Foo\nBar\n") == 0);

    ecs_entity_t e = ecs_lookup(world, "Foo");
    test_assert(e != 0);
    test_str(ecs_get_name(world, e), "Foo");

    ecs_fini(world);
}

void Plecs_2_entities_w_entities(void) {
    ecs_world_t *world = ecs_init();

    test_assert(ecs_plecs_from_str(world, NULL, "Foo(Subj_1)\nBar(Subj_2)\n") == 0);

    ecs_entity_t foo = ecs_lookup(world, "Foo");
    test_assert(foo != 0);
    test_str(ecs_get_name(world, foo), "Foo");

    ecs_entity_t bar = ecs_lookup(world, "Bar");
    test_assert(bar != 0);
    test_str(ecs_get_name(world, bar), "Bar");

    ecs_entity_t subj_1 = ecs_lookup(world, "Subj_1");
    test_assert(subj_1 != 0);
    test_str(ecs_get_name(world, subj_1), "Subj_1");

    ecs_entity_t subj_2 = ecs_lookup(world, "Subj_2");
    test_assert(subj_2 != 0);
    test_str(ecs_get_name(world, subj_2), "Subj_2");

    test_assert(ecs_has_id(world, subj_1, foo));
    test_assert(ecs_has_id(world, subj_2, bar));

    ecs_fini(world);
}

void Plecs_3_entities_w_pairs(void) {
    ecs_world_t *world = ecs_init();

    test_assert(ecs_plecs_from_str(world, NULL, 
        "Rel_1(Subj_1, Obj_1)\n"
        "Rel_1(Subj_2, Obj_2)\n"
        "Rel_2(Subj_3, Obj_2)\n") == 0);

    ecs_entity_t rel_1 = ecs_lookup(world, "Rel_1");
    test_assert(rel_1 != 0);
    test_str(ecs_get_name(world, rel_1), "Rel_1");

    ecs_entity_t rel_2 = ecs_lookup(world, "Rel_2");
    test_assert(rel_2 != 0);
    test_str(ecs_get_name(world, rel_2), "Rel_2");

    ecs_entity_t obj_1 = ecs_lookup(world, "Obj_1");
    test_assert(obj_1 != 0);
    test_str(ecs_get_name(world, obj_1), "Obj_1");

    ecs_entity_t obj_2 = ecs_lookup(world, "Obj_2");
    test_assert(obj_2 != 0);
    test_str(ecs_get_name(world, obj_2), "Obj_2");

    ecs_entity_t subj_1 = ecs_lookup(world, "Subj_1");
    test_assert(subj_1 != 0);
    test_str(ecs_get_name(world, subj_1), "Subj_1");

    ecs_entity_t subj_2 = ecs_lookup(world, "Subj_2");
    test_assert(subj_2 != 0);
    test_str(ecs_get_name(world, subj_2), "Subj_2");

    ecs_entity_t subj_3 = ecs_lookup(world, "Subj_3");
    test_assert(subj_3 != 0);
    test_str(ecs_get_name(world, subj_3), "Subj_3");  

    test_assert(ecs_has_pair(world, subj_1, rel_1, obj_1));
    test_assert(ecs_has_pair(world, subj_2, rel_1, obj_2));
    test_assert(ecs_has_pair(world, subj_3, rel_2, obj_2));

    ecs_fini(world);
}

void Plecs_line_comment(void) {
    ecs_world_t *world = ecs_init();

    test_assert(ecs_plecs_from_str(world, NULL, "// Foo(Bar)\n") == 0);

    test_assert(ecs_lookup(world, "Foo") == 0);
    test_assert(ecs_lookup(world, "Bar") == 0);

    ecs_fini(world);
}

void Plecs_line_comment_before_stmt(void) {
    ecs_world_t *world = ecs_init();

    test_assert(ecs_plecs_from_str(world, NULL, "// Hello(World)\nFoo\n") == 0);

    test_assert(ecs_lookup(world, "Hello") == 0);
    test_assert(ecs_lookup(world, "World") == 0);
    test_assert(ecs_lookup(world, "Foo") != 0);

    ecs_fini(world);
}

void Plecs_line_comment_after_stmt(void) {
    ecs_world_t *world = ecs_init();

    test_assert(ecs_plecs_from_str(world, NULL, "Foo\n// Hello(World)\n") == 0);

    test_assert(ecs_lookup(world, "Hello") == 0);
    test_assert(ecs_lookup(world, "World") == 0);
    test_assert(ecs_lookup(world, "Foo") != 0);

    ecs_fini(world);
}

void Plecs_line_comment_between_stmt(void) {
    ecs_world_t *world = ecs_init();

    test_assert(ecs_plecs_from_str(world, NULL, "Foo\n// Hello(World)\nBar\n") == 0);

    test_assert(ecs_lookup(world, "Hello") == 0);
    test_assert(ecs_lookup(world, "World") == 0);
    test_assert(ecs_lookup(world, "Foo") != 0);
    test_assert(ecs_lookup(world, "Bar") != 0);

    ecs_fini(world);
}

void Plecs_multiple_line_comment(void) {
    ecs_world_t *world = ecs_init();

    test_assert(ecs_plecs_from_str(world, NULL, "// Hello(World)\n// Boo(Baz)\nFoo") == 0);

    test_assert(ecs_lookup(world, "Hello") == 0);
    test_assert(ecs_lookup(world, "World") == 0);
    test_assert(ecs_lookup(world, "Boo") == 0);
    test_assert(ecs_lookup(world, "Baz") == 0);
    test_assert(ecs_lookup(world, "Foo") != 0);

    ecs_fini(world);
}

void Plecs_line_comment_after_stmt_same_line(void) {
    ecs_world_t *world = ecs_init();

    test_assert(ecs_plecs_from_str(world, NULL, "Foo // Hello(World)\nBar\n") == 0);

    test_assert(ecs_lookup(world, "Hello") == 0);
    test_assert(ecs_lookup(world, "World") == 0);
    test_assert(ecs_lookup(world, "Foo") != 0);
    test_assert(ecs_lookup(world, "Bar") != 0);

    ecs_fini(world);
}

void Plecs_comma_separated_pred(void) {
    ecs_world_t *world = ecs_init();

    test_assert(ecs_plecs_from_str(world, NULL, "Foo,Bar,Hello,Worlds") == 0);

    test_assert(ecs_lookup(world, "Hello") != 0);
    test_assert(ecs_lookup(world, "Worlds") != 0);
    test_assert(ecs_lookup(world, "Foo") != 0);
    test_assert(ecs_lookup(world, "Bar") != 0);

    ecs_fini(world);
}

void Plecs_comma_separated_pred_w_subj(void) {
    ecs_world_t *world = ecs_init();

    test_assert(ecs_plecs_from_str(world, NULL, "Foo(Bar),Hello(Worlds)") == 0);

    ecs_entity_t foo = ecs_lookup(world, "Foo");
    ecs_entity_t bar = ecs_lookup(world, "Bar");
    ecs_entity_t hello = ecs_lookup(world, "Hello");
    ecs_entity_t _world = ecs_lookup(world, "Worlds");

    test_assert(foo != 0);
    test_assert(bar != 0);
    test_assert(hello != 0);
    test_assert(_world != 0);

    test_assert(ecs_has_id(world, bar, foo));
    test_assert(ecs_has_id(world, _world, hello));

    ecs_fini(world);
}

void Plecs_comma_separated_pred_w_subj_obj(void) {
    ecs_world_t *world = ecs_init();

    test_assert(ecs_plecs_from_str(world, NULL, "Foo(Bar, Obj1),Hello(Worlds, Obj2)") == 0);

    ecs_entity_t foo = ecs_lookup(world, "Foo");
    ecs_entity_t bar = ecs_lookup(world, "Bar");
    ecs_entity_t hello = ecs_lookup(world, "Hello");
    ecs_entity_t _world = ecs_lookup(world, "Worlds");
    ecs_entity_t obj1 = ecs_lookup(world, "Obj1");
    ecs_entity_t obj2 = ecs_lookup(world, "Obj2");

    test_assert(foo != 0);
    test_assert(bar != 0);
    test_assert(hello != 0);
    test_assert(_world != 0);
    test_assert(obj1 != 0);
    test_assert(obj2 != 0);

    test_assert(ecs_has_pair(world, bar, foo, obj1));
    test_assert(ecs_has_pair(world, _world, hello, obj2));

    ecs_fini(world);
}

void Plecs_comma_separated_pred_trailing_comma(void) {
    ecs_world_t *world = ecs_init();

    test_assert(ecs_plecs_from_str(world, NULL, "Foo,Bar,Hello,Worlds,") == 0);

    test_assert(ecs_lookup(world, "Hello") != 0);
    test_assert(ecs_lookup(world, "Worlds") != 0);
    test_assert(ecs_lookup(world, "Foo") != 0);
    test_assert(ecs_lookup(world, "Bar") != 0);

    ecs_fini(world);
}

void Plecs_comma_separated_pred_trailing_comma_newline(void) {
    ecs_world_t *world = ecs_init();

    test_assert(ecs_plecs_from_str(world, NULL, "Foo,Bar,Hello,Worlds,\n") == 0);

    test_assert(ecs_lookup(world, "Hello") != 0);
    test_assert(ecs_lookup(world, "Worlds") != 0);
    test_assert(ecs_lookup(world, "Foo") != 0);
    test_assert(ecs_lookup(world, "Bar") != 0);

    ecs_fini(world);
}

void Plecs_comma_separated_pred_trailing_comma_newline_multiline(void) {
    ecs_world_t *world = ecs_init();

    test_assert(ecs_plecs_from_str(world, NULL, "Foo,Bar,\nHello,Worlds,") == 0);

    test_assert(ecs_lookup(world, "Hello") != 0);
    test_assert(ecs_lookup(world, "Worlds") != 0);
    test_assert(ecs_lookup(world, "Foo") != 0);
    test_assert(ecs_lookup(world, "Bar") != 0);

    ecs_fini(world);
}

void Plecs_hierarchy_1_child(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "Parent {"
    LINE " Child"
    LINE "}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    test_assert(ecs_lookup_fullpath(world, "Child") == 0);

    ecs_entity_t parent = ecs_lookup_fullpath(world, "Parent");
    ecs_entity_t child = ecs_lookup_fullpath(world, "Parent.Child");

    test_assert(parent != 0);
    test_assert(child != 0);

    test_assert(ecs_has_pair(world, child, EcsChildOf, parent));

    ecs_fini(world);
}

void Plecs_hierarchy_2_children(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "Parent {"
    LINE " ChildA"
    LINE " ChildB"
    LINE "}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    test_assert(ecs_lookup_fullpath(world, "ChildA") == 0);
    test_assert(ecs_lookup_fullpath(world, "ChildB") == 0);

    ecs_entity_t parent = ecs_lookup_fullpath(world, "Parent");
    ecs_entity_t child_a = ecs_lookup_fullpath(world, "Parent.ChildA");
    ecs_entity_t child_b = ecs_lookup_fullpath(world, "Parent.ChildB");

    test_assert(parent != 0);
    test_assert(child_a != 0);
    test_assert(child_b != 0);

    test_assert(ecs_has_pair(world, child_a, EcsChildOf, parent));
    test_assert(ecs_has_pair(world, child_b, EcsChildOf, parent));

    ecs_fini(world);
}

void Plecs_hierarchy_1_child_same_line(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "Parent { Child }";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    test_assert(ecs_lookup_fullpath(world, "Child") == 0);

    ecs_entity_t parent = ecs_lookup_fullpath(world, "Parent");
    ecs_entity_t child = ecs_lookup_fullpath(world, "Parent.Child");

    test_assert(parent != 0);
    test_assert(child != 0);

    test_assert(ecs_has_pair(world, child, EcsChildOf, parent));

    ecs_fini(world);
}

void Plecs_hierarchy_2_children_same_line(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "Parent { ChildA, ChildB }";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    test_assert(ecs_lookup_fullpath(world, "ChildA") == 0);
    test_assert(ecs_lookup_fullpath(world, "ChildB") == 0);

    ecs_entity_t parent = ecs_lookup_fullpath(world, "Parent");
    ecs_entity_t child_a = ecs_lookup_fullpath(world, "Parent.ChildA");
    ecs_entity_t child_b = ecs_lookup_fullpath(world, "Parent.ChildB");

    test_assert(parent != 0);
    test_assert(child_a != 0);
    test_assert(child_b != 0);

    test_assert(ecs_has_pair(world, child_a, EcsChildOf, parent));
    test_assert(ecs_has_pair(world, child_b, EcsChildOf, parent));

    ecs_fini(world);
}

void Plecs_entity_after_hierarchy(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "Parent {"
    LINE " Child"
    LINE "}"
    LINE "Foo";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    test_assert(ecs_lookup_fullpath(world, "Child") == 0);

    ecs_entity_t parent = ecs_lookup_fullpath(world, "Parent");
    ecs_entity_t child = ecs_lookup_fullpath(world, "Parent.Child");
    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");

    test_assert(parent != 0);
    test_assert(child != 0);
    test_assert(foo != 0);

    test_assert(ecs_has_pair(world, child, EcsChildOf, parent));
    test_assert(!ecs_has_pair(world, foo, EcsChildOf, parent));
    test_assert(!ecs_has_pair(world, foo, EcsChildOf, EcsWildcard));

    ecs_fini(world);
}

void Plecs_newline_before_scope_open(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "Parent"
    LINE "{"
    LINE " Child"
    LINE "}"
    LINE "Foo";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    test_assert(ecs_lookup_fullpath(world, "Child") == 0);

    ecs_entity_t parent = ecs_lookup_fullpath(world, "Parent");
    ecs_entity_t child = ecs_lookup_fullpath(world, "Parent.Child");

    test_assert(parent != 0);
    test_assert(child != 0);

    test_assert(ecs_has_pair(world, child, EcsChildOf, parent));

    ecs_fini(world);
}

void Plecs_comment_before_scope_open(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "Parent // Some(Comment)"
    LINE "{"
    LINE " Child"
    LINE "}"
    LINE "Foo";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    test_assert(ecs_lookup_fullpath(world, "Child") == 0);

    test_assert(ecs_lookup_fullpath(world, "Some") == 0);
    test_assert(ecs_lookup_fullpath(world, "Comment") == 0);

    ecs_entity_t parent = ecs_lookup_fullpath(world, "Parent");
    ecs_entity_t child = ecs_lookup_fullpath(world, "Parent.Child");

    test_assert(parent != 0);
    test_assert(child != 0);

    test_assert(ecs_has_pair(world, child, EcsChildOf, parent));

    ecs_fini(world);
}

void Plecs_comment_after_newline_before_scope_open(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "Parent"
    LINE "// Some(Comment)"
    LINE "{"
    LINE " Child"
    LINE "}"
    LINE "Foo";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    test_assert(ecs_lookup_fullpath(world, "Child") == 0);

    test_assert(ecs_lookup_fullpath(world, "Some") == 0);
    test_assert(ecs_lookup_fullpath(world, "Comment") == 0);

    ecs_entity_t parent = ecs_lookup_fullpath(world, "Parent");
    ecs_entity_t child = ecs_lookup_fullpath(world, "Parent.Child");

    test_assert(parent != 0);
    test_assert(child != 0);

    test_assert(ecs_has_pair(world, child, EcsChildOf, parent));

    ecs_fini(world);
}

void Plecs_hierarchy_2_levels(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "Parent {"
    LINE " Child {"
    LINE "  GrandChild"
    LINE " }"
    LINE "}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    test_assert(ecs_lookup_fullpath(world, "Child") == 0);
    test_assert(ecs_lookup_fullpath(world, "GrandChild") == 0);

    ecs_entity_t parent = ecs_lookup_fullpath(world, "Parent");
    ecs_entity_t child = ecs_lookup_fullpath(world, "Parent.Child");
    ecs_entity_t grandchild = ecs_lookup_fullpath(world, "Parent.Child.GrandChild");

    test_assert(parent != 0);
    test_assert(child != 0);
    test_assert(grandchild != 0);

    test_assert(ecs_has_pair(world, child, EcsChildOf, parent));
    test_assert(ecs_has_pair(world, grandchild, EcsChildOf, child));

    ecs_fini(world);
}

void Plecs_hierarchy_2_levels_2_subtrees(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "Parent {"
    LINE " ChildA {"
    LINE "  GrandChild"
    LINE " }"
    LINE " ChildB {"
    LINE "  GrandChild"
    LINE " }"
    LINE "}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    test_assert(ecs_lookup_fullpath(world, "ChildA") == 0);
    test_assert(ecs_lookup_fullpath(world, "ChildB") == 0);
    test_assert(ecs_lookup_fullpath(world, "GrandChild") == 0);

    ecs_entity_t parent = ecs_lookup_fullpath(world, "Parent");
    ecs_entity_t child_a = ecs_lookup_fullpath(world, "Parent.ChildA");
    ecs_entity_t child_b = ecs_lookup_fullpath(world, "Parent.ChildB");
    ecs_entity_t grandchild_a = ecs_lookup_fullpath(world, "Parent.ChildA.GrandChild");
    ecs_entity_t grandchild_b = ecs_lookup_fullpath(world, "Parent.ChildB.GrandChild");

    test_assert(parent != 0);
    test_assert(child_a != 0);
    test_assert(child_b != 0);
    test_assert(child_a != child_b);
    test_assert(grandchild_a != 0);
    test_assert(grandchild_b != 0);
    test_assert(grandchild_a != grandchild_b);

    test_assert(ecs_has_pair(world, child_a, EcsChildOf, parent));
    test_assert(ecs_has_pair(world, child_b, EcsChildOf, parent));
    test_assert(ecs_has_pair(world, grandchild_a, EcsChildOf, child_a));
    test_assert(ecs_has_pair(world, grandchild_b, EcsChildOf, child_b));

    ecs_fini(world);
}

void Plecs_missing_end_of_scope(void) {
    ecs_log_set_level(-4);
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "Parent {"
    LINE " Child";

    test_assert(ecs_plecs_from_str(world, NULL, expr) != 0);

    test_assert(ecs_get_scope(world) == 0);
    test_assert(ecs_get_with(world) == 0);

    ecs_fini(world);
}

void Plecs_missing_end_of_predicate_scope(void) {
    ecs_log_set_level(-4);
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "Parent() {"
    LINE " Child";

    test_assert(ecs_plecs_from_str(world, NULL, expr) != 0);

    test_assert(ecs_get_scope(world) == 0);
    test_assert(ecs_get_with(world) == 0);

    ecs_fini(world);
}

void Plecs_create_in_scope(void) {
    ecs_world_t *world = ecs_init();

    ECS_TAG(world, Root);

    ecs_set_scope(world, Root);

    const char *expr =
    HEAD "Parent { Child }";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    test_assert(ecs_get_scope(world) == Root);

    test_assert(ecs_lookup_fullpath(world, "Child") == 0);

    ecs_set_scope(world, 0);

    ecs_entity_t parent = ecs_lookup_fullpath(world, "Parent");
    ecs_entity_t child = ecs_lookup_fullpath(world, "Parent.Child");

    test_assert(parent != 0);
    test_assert(child != 0);

    test_assert(ecs_has_pair(world, child, EcsChildOf, parent));
    test_assert(!ecs_has_pair(world, parent, EcsChildOf, Root));

    ecs_fini(world);
}

void Plecs_hierarchy_w_pred_subj(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "Foo(Bar) {"
    LINE " Child"
    LINE "}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    test_assert(ecs_lookup_fullpath(world, "Child") == 0);

    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar");
    ecs_entity_t child = ecs_lookup_fullpath(world, "Bar.Child");

    test_assert(foo != 0);
    test_assert(bar != 0);
    test_assert(child != 0);

    test_assert(ecs_has_id(world, bar, foo));
    test_assert(ecs_has_pair(world, child, EcsChildOf, bar));

    ecs_fini(world);
}

void Plecs_hierarchy_custom_relation(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "(Foo, Bar) {"
    LINE " Child"
    LINE "}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar");
    ecs_entity_t child = ecs_lookup_fullpath(world, "Child");

    test_assert(foo != 0);
    test_assert(bar != 0);
    test_assert(child != 0);

    test_assert(!ecs_has_id(world, bar, foo));
    test_assert(!ecs_has_id(world, foo, bar));
    test_assert(!ecs_has_pair(world, child, EcsChildOf, foo));
    test_assert(!ecs_has_pair(world, child, EcsChildOf, bar));
    test_assert(ecs_has_pair(world, child, foo, bar));

    ecs_fini(world);
}

void Plecs_hierarchy_custom_relation_2_levels(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "(Foo, Bar) {"
    LINE "  (Rel, ObjA) {"
    LINE "    Child"
    LINE "    ChildA"
    LINE "  }"
    LINE "  (Rel, ObjB) {"
    LINE "    Child"
    LINE "    ChildB"
    LINE "  }"
    LINE "}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar");
    ecs_entity_t rel = ecs_lookup_fullpath(world, "Rel");
    ecs_entity_t obj_a = ecs_lookup_fullpath(world, "ObjA");
    ecs_entity_t obj_b = ecs_lookup_fullpath(world, "ObjB");
    ecs_entity_t child = ecs_lookup_fullpath(world, "Child");
    ecs_entity_t child_a = ecs_lookup_fullpath(world, "ChildA");
    ecs_entity_t child_b = ecs_lookup_fullpath(world, "ChildB");

    test_assert(foo != 0);
    test_assert(bar != 0);
    test_assert(rel != 0);
    test_assert(obj_a != 0);
    test_assert(obj_b != 0);
    test_assert(child != 0);
    test_assert(child_a != 0);
    test_assert(child_b != 0);

    test_assert(!ecs_has_id(world, bar, foo));
    test_assert(!ecs_has_id(world, foo, bar));

    test_assert(!ecs_has_id(world, rel, obj_a));
    test_assert(!ecs_has_id(world, obj_a, rel));

    test_assert(!ecs_has_id(world, rel, obj_b));
    test_assert(!ecs_has_id(world, obj_b, rel));

    test_assert(!ecs_has_pair(world, child, EcsChildOf, foo));
    test_assert(!ecs_has_pair(world, child, EcsChildOf, bar));

    test_assert(ecs_has_pair(world, child, rel, obj_a));
    test_assert(ecs_has_pair(world, child, rel, obj_b));

    test_assert(ecs_has_pair(world, child_a, rel, obj_a));
    test_assert(ecs_has_pair(world, child_b, rel, obj_b));

    ecs_fini(world);
}

void Plecs_entity_after_hierarchy_custom_relation(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "(Foo, Bar) {"
    LINE " Child"
    LINE "}"
    LINE "Hello";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar");
    ecs_entity_t child = ecs_lookup_fullpath(world, "Child");
    ecs_entity_t hello = ecs_lookup_fullpath(world, "Hello");

    test_assert(foo != 0);
    test_assert(bar != 0);
    test_assert(child != 0);
    test_assert(hello != 0);

    test_assert(!ecs_has_id(world, bar, foo));
    test_assert(!ecs_has_id(world, foo, bar));
    test_assert(!ecs_has_pair(world, child, EcsChildOf, foo));
    test_assert(!ecs_has_pair(world, child, EcsChildOf, bar));
    test_assert(ecs_has_pair(world, child, foo, bar));
    test_assert(!ecs_has_pair(world, hello, foo, bar));

    ecs_fini(world);
}

void Plecs_entity_after_hierarchy_custom_relation_2_levels(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "(Foo, Bar) {"
    LINE "  (Rel, ObjA) {"
    LINE "    Child"
    LINE "    ChildA"
    LINE "  }"
    LINE "  TestA"
    LINE "  (Rel, ObjB) {"
    LINE "    Child"
    LINE "    ChildB"
    LINE "  }"
    LINE "}"
    LINE "TestB";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar");
    ecs_entity_t rel = ecs_lookup_fullpath(world, "Rel");
    ecs_entity_t obj_a = ecs_lookup_fullpath(world, "ObjA");
    ecs_entity_t obj_b = ecs_lookup_fullpath(world, "ObjB");
    ecs_entity_t child = ecs_lookup_fullpath(world, "Child");
    ecs_entity_t child_a = ecs_lookup_fullpath(world, "ChildA");
    ecs_entity_t child_b = ecs_lookup_fullpath(world, "ChildB");

    ecs_entity_t test_a = ecs_lookup_fullpath(world, "TestA");
    ecs_entity_t test_b = ecs_lookup_fullpath(world, "TestB");

    test_assert(foo != 0);
    test_assert(bar != 0);
    test_assert(rel != 0);
    test_assert(obj_a != 0);
    test_assert(obj_b != 0);
    test_assert(child != 0);
    test_assert(child_a != 0);
    test_assert(child_b != 0);

    test_assert(test_a != 0);
    test_assert(test_b != 0);

    test_assert(!ecs_has_id(world, bar, foo));
    test_assert(!ecs_has_id(world, foo, bar));

    test_assert(!ecs_has_id(world, rel, obj_a));
    test_assert(!ecs_has_id(world, obj_a, rel));

    test_assert(!ecs_has_id(world, rel, obj_b));
    test_assert(!ecs_has_id(world, obj_b, rel));

    test_assert(!ecs_has_pair(world, child, EcsChildOf, foo));
    test_assert(!ecs_has_pair(world, child, EcsChildOf, bar));

    test_assert(ecs_has_pair(world, child, rel, obj_a));
    test_assert(ecs_has_pair(world, child, rel, obj_b));

    test_assert(ecs_has_pair(world, child_a, rel, obj_a));
    test_assert(ecs_has_pair(world, child_b, rel, obj_b));

    test_assert(ecs_has_pair(world, test_a, foo, bar));
    test_assert(!ecs_has_pair(world, test_a, rel, EcsWildcard));

    test_assert(!ecs_has_pair(world, test_b, foo, bar));
    test_assert(!ecs_has_pair(world, test_b, rel, EcsWildcard));

    ecs_fini(world);
}

void Plecs_hierarchy_custom_relation_apply_to_object(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "(Rel, ObjA) {"
    LINE " (Rel, ObjB) {"
    LINE "   ObjC"
    LINE " }"
    LINE "}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t rel = ecs_lookup_fullpath(world, "Rel");
    ecs_entity_t obj_a = ecs_lookup_fullpath(world, "ObjA");
    ecs_entity_t obj_b = ecs_lookup_fullpath(world, "ObjB");
    ecs_entity_t obj_c = ecs_lookup_fullpath(world, "ObjC");

    test_assert(rel != 0);
    test_assert(obj_a != 0);
    test_assert(obj_b != 0);
    test_assert(obj_c != 0);

    test_assert(ecs_has_pair(world, obj_b, rel, obj_a));
    test_assert(ecs_has_pair(world, obj_c, rel, obj_b));
    test_assert(!ecs_has_pair(world, obj_c, rel, obj_a));

    ecs_fini(world);
}

void Plecs_hierarchy_custom_relation_apply_to_object_2_levels(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "(Rel, ObjA) {"
    LINE " (Rel, ObjB) {"
    LINE "   (Rel, ObjC) {"
    LINE "     ObjD"
    LINE "   }"
    LINE " }"
    LINE "}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t rel = ecs_lookup_fullpath(world, "Rel");
    ecs_entity_t obj_a = ecs_lookup_fullpath(world, "ObjA");
    ecs_entity_t obj_b = ecs_lookup_fullpath(world, "ObjB");
    ecs_entity_t obj_c = ecs_lookup_fullpath(world, "ObjC");
    ecs_entity_t obj_d = ecs_lookup_fullpath(world, "ObjD");

    test_assert(rel != 0);
    test_assert(obj_a != 0);
    test_assert(obj_b != 0);
    test_assert(obj_c != 0);
    test_assert(obj_d != 0);

    test_assert(ecs_has_pair(world, obj_b, rel, obj_a));
    test_assert(ecs_has_pair(world, obj_c, rel, obj_b));
    test_assert(!ecs_has_pair(world, obj_c, rel, obj_a));
    test_assert(ecs_has_pair(world, obj_d, rel, obj_c));
    test_assert(!ecs_has_pair(world, obj_d, rel, obj_b));
    test_assert(!ecs_has_pair(world, obj_d, rel, obj_a));

    ecs_fini(world);
}

void Plecs_pred_scope(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "Foo() {"
    LINE " Hello"
    LINE "}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    ecs_entity_t hello = ecs_lookup_fullpath(world, "Foo.Hello");

    test_assert(foo != 0);
    test_assert(hello != 0);

    test_assert(ecs_has_pair(world, hello, EcsChildOf, foo));
    test_assert(!ecs_has_id(world, hello, foo));

    ecs_fini(world);
}

void Plecs_pred_scope_2_levels(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "Foo() {"
    LINE "  Bar() {"
    LINE "    Hello"
    LINE "  }"
    LINE "}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar");
    ecs_entity_t hello = ecs_lookup_fullpath(world, "Bar.Hello");

    test_assert(foo != 0);
    test_assert(bar != 0);
    test_assert(hello != 0);

    test_assert(!ecs_has_pair(world, hello, EcsChildOf, foo));
    test_assert(ecs_has_pair(world, hello, EcsChildOf, bar));
    test_assert(!ecs_has_id(world, hello, foo));
    test_assert(!ecs_has_id(world, hello, bar));

    test_assert(!ecs_has_pair(world, bar, EcsChildOf, foo));
    test_assert(!ecs_has_id(world, bar, foo));

    ecs_fini(world);
}

void Plecs_pred_scope_inside_with(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "with Tag {"
    HEAD " Foo() {"
    LINE "  Hello"
    LINE " }"
    LINE "}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t tag = ecs_lookup_fullpath(world, "Tag");
    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    ecs_entity_t hello = ecs_lookup_fullpath(world, "Foo.Hello");

    test_assert(foo != 0);
    test_assert(hello != 0);
    test_assert(tag != 0);

    test_assert(ecs_has_pair(world, hello, EcsChildOf, foo));
    test_assert(!ecs_has_id(world, hello, foo));
    test_assert(ecs_has_id(world, hello, tag));

    test_assert(!ecs_has_id(world, foo, tag));

    ecs_fini(world);
}

void Plecs_pred_scope_nested_w_subj_scope(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "Parent {"
    HEAD " Foo() {"
    LINE "  Hello"
    LINE " }"
    LINE "}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t parent = ecs_lookup_fullpath(world, "Parent");
    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    ecs_entity_t hello = ecs_lookup_fullpath(world, "Foo.Hello");

    test_assert(parent != 0);
    test_assert(foo != 0);
    test_assert(hello != 0);

    test_assert(!ecs_has_pair(world, hello, EcsChildOf, parent));
    test_assert(ecs_has_pair(world, hello, EcsChildOf, foo));
    test_assert(!ecs_has_id(world, hello, foo));

    test_assert(!ecs_has_pair(world, foo, EcsChildOf, parent));

    ecs_fini(world);
}


void Plecs_with_tag(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "with Tag {"
    LINE "  Foo"
    LINE "}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    ecs_entity_t tag = ecs_lookup_fullpath(world, "Tag");

    test_assert(foo != 0);
    test_assert(tag != 0);

    test_assert(ecs_has_id(world, foo, tag));

    ecs_fini(world);
}

void Plecs_with_tag_2_entities(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "with Tag {"
    LINE "  Foo"
    LINE "  Bar"
    LINE "}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar");
    ecs_entity_t tag = ecs_lookup_fullpath(world, "Tag");

    test_assert(foo != 0);
    test_assert(bar != 0);
    test_assert(tag != 0);

    test_assert(ecs_has_id(world, foo, tag));
    test_assert(ecs_has_id(world, bar, tag));

    ecs_fini(world);
}

void Plecs_with_tag_same_line(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "with Tag { Foo }";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    ecs_entity_t tag = ecs_lookup_fullpath(world, "Tag");

    test_assert(foo != 0);
    test_assert(tag != 0);

    test_assert(ecs_has_id(world, foo, tag));

    ecs_fini(world);
}

void Plecs_with_tag_2_entities_same_line(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "with Tag { Foo, Bar }";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar");
    ecs_entity_t tag = ecs_lookup_fullpath(world, "Tag");

    test_assert(foo != 0);
    test_assert(bar != 0);
    test_assert(tag != 0);

    test_assert(ecs_has_id(world, foo, tag));
    test_assert(ecs_has_id(world, bar, tag));

    ecs_fini(world);
}

void Plecs_with_tag_2_levels(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "with TagA {"
    HEAD " with TagB {"
    LINE "  Foo"
    LINE " }"
    LINE "}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    ecs_entity_t tag_a = ecs_lookup_fullpath(world, "TagA");
    ecs_entity_t tag_b = ecs_lookup_fullpath(world, "TagB");

    test_assert(foo != 0);
    test_assert(tag_a != 0);
    test_assert(tag_b != 0);

    test_assert(ecs_has_id(world, foo, tag_a));
    test_assert(ecs_has_id(world, foo, tag_b));

    ecs_fini(world);
}

void Plecs_with_tag_2_levels_2_subtrees(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "with TagA {"
    HEAD " with TagB {"
    LINE "  Foo"
    LINE "  BarA"
    LINE " }"
    HEAD " with TagC {"
    LINE "  Foo"
    LINE "  BarB"
    LINE " }"
    LINE "}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    ecs_entity_t tag_a = ecs_lookup_fullpath(world, "TagA");
    ecs_entity_t tag_b = ecs_lookup_fullpath(world, "TagB");
    ecs_entity_t tag_c = ecs_lookup_fullpath(world, "TagC");
    ecs_entity_t bar_a = ecs_lookup_fullpath(world, "BarA");
    ecs_entity_t bar_b = ecs_lookup_fullpath(world, "BarB");

    test_assert(foo != 0);
    test_assert(tag_a != 0);
    test_assert(tag_b != 0);
    test_assert(bar_a != 0);
    test_assert(bar_b != 0);

    test_assert(ecs_has_id(world, foo, tag_a));
    test_assert(ecs_has_id(world, foo, tag_b));
    test_assert(ecs_has_id(world, foo, tag_c));

    test_assert(ecs_has_id(world, bar_a, tag_a));
    test_assert(ecs_has_id(world, bar_b, tag_a));

    test_assert(ecs_has_id(world, bar_a, tag_b));
    test_assert(ecs_has_id(world, bar_b, tag_c));

    test_assert(!ecs_has_id(world, bar_b, tag_b));
    test_assert(!ecs_has_id(world, bar_a, tag_c));

    ecs_fini(world);
}

void Plecs_with_n_tags(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "with TagA, TagB {"
    LINE " Foo"
    LINE " Bar"
    LINE "}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar");
    ecs_entity_t tag_a = ecs_lookup_fullpath(world, "TagA");
    ecs_entity_t tag_b = ecs_lookup_fullpath(world, "TagB");

    test_assert(foo != 0);
    test_assert(bar != 0);
    test_assert(tag_a != 0);
    test_assert(tag_b != 0);

    test_assert(ecs_has_id(world, foo, tag_a));
    test_assert(ecs_has_id(world, foo, tag_b));

    test_assert(ecs_has_id(world, bar, tag_a));
    test_assert(ecs_has_id(world, bar, tag_b));

    ecs_fini(world);
}

void Plecs_with_n_tags_2_levels(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "with TagA, TagB {"
    LINE " with TagC, TagD, TagE {"
    LINE "  Foo"
    LINE " }"
    LINE " HelloA"
    LINE " with TagF, TagG, TagH {"
    LINE "  Bar"
    LINE " }"
    LINE " HelloB"
    LINE "}"
    LINE "HelloC";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t tag_a = ecs_lookup_fullpath(world, "TagA");
    ecs_entity_t tag_b = ecs_lookup_fullpath(world, "TagB");
    ecs_entity_t tag_c = ecs_lookup_fullpath(world, "TagC");
    ecs_entity_t tag_d = ecs_lookup_fullpath(world, "TagD");
    ecs_entity_t tag_e = ecs_lookup_fullpath(world, "TagE");
    ecs_entity_t tag_f = ecs_lookup_fullpath(world, "TagF");
    ecs_entity_t tag_g = ecs_lookup_fullpath(world, "TagG");
    ecs_entity_t tag_h = ecs_lookup_fullpath(world, "TagH");

    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar");

    ecs_entity_t hello_a = ecs_lookup_fullpath(world, "HelloA");
    ecs_entity_t hello_b = ecs_lookup_fullpath(world, "HelloB");
    ecs_entity_t hello_c = ecs_lookup_fullpath(world, "HelloC");

    test_assert(tag_a != 0);
    test_assert(tag_b != 0);
    test_assert(tag_a != 0);
    test_assert(tag_b != 0);
    test_assert(tag_a != 0);
    test_assert(tag_b != 0);
    test_assert(tag_b != 0);
    test_assert(tag_b != 0);
    test_assert(foo != 0);
    test_assert(bar != 0);
    test_assert(hello_a != 0);
    test_assert(hello_b != 0);
    test_assert(hello_c != 0);

    test_assert(ecs_has_id(world, foo, tag_a));
    test_assert(ecs_has_id(world, foo, tag_b));
    test_assert(ecs_has_id(world, foo, tag_c));
    test_assert(ecs_has_id(world, foo, tag_d));
    test_assert(ecs_has_id(world, foo, tag_e));
    test_assert(!ecs_has_id(world, foo, tag_f));
    test_assert(!ecs_has_id(world, foo, tag_g));
    test_assert(!ecs_has_id(world, foo, tag_h));

    test_assert(ecs_has_id(world, bar, tag_a));
    test_assert(ecs_has_id(world, bar, tag_b));
    test_assert(!ecs_has_id(world, bar, tag_c));
    test_assert(!ecs_has_id(world, bar, tag_d));
    test_assert(!ecs_has_id(world, bar, tag_e));
    test_assert(ecs_has_id(world, bar, tag_f));
    test_assert(ecs_has_id(world, bar, tag_g));
    test_assert(ecs_has_id(world, bar, tag_h));

    test_assert(ecs_has_id(world, hello_a, tag_a));
    test_assert(ecs_has_id(world, hello_a, tag_b));
    test_assert(!ecs_has_id(world, hello_a, tag_c));
    test_assert(!ecs_has_id(world, hello_a, tag_d));
    test_assert(!ecs_has_id(world, hello_a, tag_e));
    test_assert(!ecs_has_id(world, hello_a, tag_f));
    test_assert(!ecs_has_id(world, hello_a, tag_g));
    test_assert(!ecs_has_id(world, hello_a, tag_h));

    test_assert(ecs_has_id(world, hello_b, tag_a));
    test_assert(ecs_has_id(world, hello_b, tag_b));
    test_assert(!ecs_has_id(world, hello_b, tag_c));
    test_assert(!ecs_has_id(world, hello_b, tag_d));
    test_assert(!ecs_has_id(world, hello_b, tag_e));
    test_assert(!ecs_has_id(world, hello_b, tag_f));
    test_assert(!ecs_has_id(world, hello_b, tag_g));
    test_assert(!ecs_has_id(world, hello_b, tag_h));

    test_assert(!ecs_has_id(world, hello_c, tag_a));
    test_assert(!ecs_has_id(world, hello_c, tag_b));
    test_assert(!ecs_has_id(world, hello_c, tag_c));
    test_assert(!ecs_has_id(world, hello_c, tag_d));
    test_assert(!ecs_has_id(world, hello_c, tag_e));
    test_assert(!ecs_has_id(world, hello_c, tag_f));
    test_assert(!ecs_has_id(world, hello_c, tag_g));
    test_assert(!ecs_has_id(world, hello_c, tag_h));

    ecs_fini(world);
}

void Plecs_with_after_scope(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "E1 { }"
    HEAD "with E2 {"
    LINE "  E3() { }"
    LINE "  E4 { }"
    LINE "}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t e1 = ecs_lookup_fullpath(world, "E1");
    ecs_entity_t e2 = ecs_lookup_fullpath(world, "E2");
    ecs_entity_t e3 = ecs_lookup_fullpath(world, "E3");
    ecs_entity_t e4 = ecs_lookup_fullpath(world, "E4");

    test_assert(e1 != 0);
    test_assert(e2 != 0);
    test_assert(e3 != 0);
    test_assert(e4 != 0);

    test_assert(!ecs_has_pair(world, e2, EcsChildOf, e1));
    test_assert(!ecs_has_pair(world, e3, EcsChildOf, e1));
    test_assert(!ecs_has_pair(world, e4, EcsChildOf, e1));

    test_assert(!ecs_has_id(world, e3, e2));
    test_assert(ecs_has_id(world, e4, e2));

    ecs_fini(world);
}

void Plecs_with_after_with(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "with E1 { }"
    HEAD "with E2 {"
    LINE "  E3() { }"
    LINE "  E4 { }"
    LINE "}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t e1 = ecs_lookup_fullpath(world, "E1");
    ecs_entity_t e2 = ecs_lookup_fullpath(world, "E2");
    ecs_entity_t e3 = ecs_lookup_fullpath(world, "E3");
    ecs_entity_t e4 = ecs_lookup_fullpath(world, "E4");

    test_assert(e1 != 0);
    test_assert(e2 != 0);
    test_assert(e3 != 0);
    test_assert(e4 != 0);

    test_assert(!ecs_has_id(world, e2, e1));
    test_assert(!ecs_has_id(world, e3, e1));
    test_assert(!ecs_has_id(world, e4, e1));

    test_assert(!ecs_has_id(world, e3, e2));
    test_assert(ecs_has_id(world, e4, e2));

    ecs_fini(world);
}

void Plecs_scope_inside_with_inside_scope(void) {
    ecs_world_t *world = ecs_init();
    
    const char *expr =
    HEAD "Sun {"
    LINE "  with Planet {"
    LINE "    Earth {"
    LINE "      Moon"
    LINE "    }"
    LINE "    Mars"
    LINE "  }"
    LINE "}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t sun = ecs_lookup_fullpath(world, "Sun");
    ecs_entity_t planet = ecs_lookup_fullpath(world, "Planet");
    ecs_entity_t earth = ecs_lookup_fullpath(world, "Sun.Earth");
    ecs_entity_t moon = ecs_lookup_fullpath(world, "Sun.Earth.Moon");
    ecs_entity_t mars = ecs_lookup_fullpath(world, "Sun.Mars");

    test_assert(sun != 0);
    test_assert(planet != 0);
    test_assert(earth != 0);
    test_assert(moon != 0);
    test_assert(mars != 0);

    test_assert(ecs_has_id(world, earth, planet));
    test_assert(ecs_has_id(world, moon, planet));
    test_assert(ecs_has_id(world, mars, planet));

    test_assert(ecs_has_pair(world, earth, EcsChildOf, sun));
    test_assert(ecs_has_pair(world, moon, EcsChildOf, earth));
    test_assert(ecs_has_pair(world, mars, EcsChildOf, sun));

    test_assert(!ecs_has_id(world, earth, sun));
    test_assert(!ecs_has_id(world, moon, earth));
    test_assert(!ecs_has_id(world, mars, sun));

    test_assert(!ecs_has_pair(world, planet, EcsChildOf, sun));

    ecs_fini(world);
}

void Plecs_with_inside_scope(void) {
    ecs_world_t *world = ecs_init();
    
    const char *expr =
    HEAD "Earth {"
    LINE "  with Continent {"
    LINE "    Europe"
    LINE "  }"
    LINE "  Europe"
    LINE "}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t earth = ecs_lookup_fullpath(world, "Earth");
    ecs_entity_t continent = ecs_lookup_fullpath(world, "Continent");
    ecs_entity_t europe = ecs_lookup_fullpath(world, "Earth.Europe");

    test_assert(earth != 0);
    test_assert(continent != 0);
    test_assert(europe != 0);

    test_assert( ecs_has_pair(world, europe, EcsChildOf, earth));
    test_assert( !ecs_has_pair(world, continent, EcsChildOf, earth));
    test_assert( !ecs_has_id(world, europe, earth));
    test_assert( !ecs_has_id(world, continent, earth));

    ecs_fini(world);
}

void Plecs_assignment_w_1(void) {
    ecs_world_t *world = ecs_init();
    
    const char *expr =
    HEAD "Earth :- Planet";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t earth = ecs_lookup_fullpath(world, "Earth");
    ecs_entity_t planet = ecs_lookup_fullpath(world, "Planet");

    test_assert(earth != 0);
    test_assert(planet != 0);

    test_assert( ecs_has_id(world, earth, planet));

    ecs_fini(world);
}

void Plecs_assignment_w_2(void) {
    ecs_world_t *world = ecs_init();
    
    const char *expr =
    HEAD "Earth :- Planet, Habitable";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t earth = ecs_lookup_fullpath(world, "Earth");
    ecs_entity_t planet = ecs_lookup_fullpath(world, "Planet");
    ecs_entity_t habitable = ecs_lookup_fullpath(world, "Habitable");

    test_assert(earth != 0);
    test_assert(planet != 0);
    test_assert(habitable != 0);

    test_assert( ecs_has_id(world, earth, planet));
    test_assert( ecs_has_id(world, earth, habitable));

    ecs_fini(world);
}

void Plecs_assignment_w_pair(void) {
    ecs_world_t *world = ecs_init();
    
    const char *expr =
    HEAD "Earth :- Planet, (ChildOf, Sun)";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t earth = ecs_lookup_fullpath(world, "Sun.Earth");
    ecs_entity_t planet = ecs_lookup_fullpath(world, "Planet");
    ecs_entity_t sun = ecs_lookup_fullpath(world, "Sun");

    test_assert(earth != 0);
    test_assert(planet != 0);
    test_assert(sun != 0);

    test_assert( ecs_has_id(world, earth, planet));
    test_assert( ecs_has_pair(world, earth, EcsChildOf, sun));

    ecs_fini(world);
}

void Plecs_assignment_w_invalid_subject(void) {
    ecs_log_set_level(-4);

    ecs_world_t *world = ecs_init();
    
    const char *expr =
    HEAD "Earth :- Planet(Mars)";

    test_assert(ecs_plecs_from_str(world, NULL, expr) != 0);

    ecs_fini(world);
}

void Plecs_assignment_w_invalid_with(void) {
    ecs_log_set_level(-4);
    
    ecs_world_t *world = ecs_init();
    
    const char *expr =
    HEAD "Earth :- with Planet { Mars }";

    test_assert(ecs_plecs_from_str(world, NULL, expr) != 0);

    ecs_fini(world);
}

void Plecs_inherit_w_colon(void) {    
    ecs_world_t *world = ecs_init();
    
    const char *expr =
    HEAD "Foo : Bar";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar");

    test_assert(foo != 0);
    test_assert(bar != 0);

    test_assert(ecs_has_pair(world, foo, EcsIsA, bar));

    ecs_fini(world);
}

void Plecs_inherit_w_colon_w_scope(void) {
    ecs_world_t *world = ecs_init();
    
    const char *expr =
    HEAD "Foo : Bar { Child }";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar");
    ecs_entity_t child = ecs_lookup_fullpath(world, "Foo.Child");

    test_assert(foo != 0);
    test_assert(bar != 0);

    test_assert(ecs_has_pair(world, foo, EcsIsA, bar));
    test_assert(ecs_has_pair(world, child, EcsChildOf, foo));

    ecs_fini(world);
}

void Plecs_inherit_w_colon_w_assign(void) {
    ecs_world_t *world = ecs_init();
    
    const char *expr =
    HEAD "Foo : Bar :- Comp";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar");
    ecs_entity_t comp = ecs_lookup_fullpath(world, "Comp");

    test_assert(foo != 0);
    test_assert(bar != 0);

    test_assert(ecs_has_pair(world, foo, EcsIsA, bar));
    test_assert(ecs_has_id(world, foo, comp));

    ecs_fini(world);
}

void Plecs_assign_component_value(void) {
    ecs_world_t *world = ecs_init();

    ecs_entity_t ecs_id(Position) = ecs_struct_init(world, &(ecs_struct_desc_t){
        .entity = ecs_entity(world, {.name = "Position"}),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    HEAD "Foo :- Position{10, 20}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);
    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");

    test_assert(foo != 0);

    test_assert(ecs_has(world, foo, Position));

    const Position *ptr = ecs_get(world, foo, Position);
    test_assert(ptr != NULL);
    test_int(ptr->x, 10);
    test_int(ptr->y, 20);

    ecs_fini(world);
}

void Plecs_assign_2_component_values(void) {
    ecs_world_t *world = ecs_init();

    ecs_entity_t ecs_id(Position) = ecs_struct_init(world, &(ecs_struct_desc_t){
        .entity = ecs_entity(world, {.name = "Position"}),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    ecs_entity_t ecs_id(Velocity) = ecs_struct_init(world, &(ecs_struct_desc_t){
        .entity = ecs_entity(world, {.name = "Velocity"}),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    HEAD "Foo :- Position{10, 20}"
    LINE "Foo :- Velocity {1, 2}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);
    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");

    test_assert(foo != 0);

    test_assert(ecs_has(world, foo, Position));
    test_assert(ecs_has(world, foo, Velocity));

    {
    const Position *ptr = ecs_get(world, foo, Position);
    test_assert(ptr != NULL);
    test_int(ptr->x, 10);
    test_int(ptr->y, 20);
    }

    {
    const Velocity *ptr = ecs_get(world, foo, Velocity);
    test_assert(ptr != NULL);
    test_int(ptr->x, 1);
    test_int(ptr->y, 2);
    }

    ecs_fini(world);
}

void Plecs_assign_component_value_in_assign_scope(void) {
    ecs_world_t *world = ecs_init();

    ecs_entity_t ecs_id(Position) = ecs_struct_init(world, &(ecs_struct_desc_t){
        .entity = ecs_entity(world, {.name = "Position"}),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    HEAD "Foo {"
    LINE " - Position {10, 20}"
    LINE "}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);
    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");

    test_assert(foo != 0);

    test_assert(ecs_has(world, foo, Position));

    const Position *ptr = ecs_get(world, foo, Position);
    test_assert(ptr != NULL);

    test_int(ptr->x, 10);
    test_int(ptr->y, 20);

    ecs_fini(world);
}

void Plecs_assign_2_component_values_in_assign_scope(void) {
    ecs_world_t *world = ecs_init();

    ecs_entity_t ecs_id(Position) = ecs_struct_init(world, &(ecs_struct_desc_t){
        .entity = ecs_entity(world, {.name = "Position"}),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    ecs_entity_t ecs_id(Velocity) = ecs_struct_init(world, &(ecs_struct_desc_t){
        .entity = ecs_entity(world, {.name = "Velocity"}),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    HEAD "Foo {"
    LINE " - Position{10, 20}"
    LINE " -Velocity {1, 2}"
    LINE "}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);
    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");

    test_assert(foo != 0);

    test_assert(ecs_has(world, foo, Position));
    test_assert(ecs_has(world, foo, Velocity));

    {
    const Position *ptr = ecs_get(world, foo, Position);
    test_assert(ptr != NULL);
    test_int(ptr->x, 10);
    test_int(ptr->y, 20);
    }

    {
    const Velocity *ptr = ecs_get(world, foo, Velocity);
    test_assert(ptr != NULL);
    test_int(ptr->x, 1);
    test_int(ptr->y, 2);
    }

    ecs_fini(world);
}

void Plecs_type_and_assign_in_plecs(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "flecs.meta.Struct Position {"
    LINE "  x :- flecs.meta.Member{flecs.meta.f32}"
    LINE "  y :- flecs.meta.Member{flecs.meta.f32}"
    LINE "}"
    LINE
    LINE "Foo :- Position{10, 20}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);
    ecs_entity_t ecs_id(Position) = ecs_lookup_fullpath(world, "Position");
    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");

    test_assert(ecs_id(Position) != 0);
    test_assert(foo != 0);

    test_assert(ecs_has(world, foo, Position));

    {
    const Position *ptr = ecs_get(world, foo, Position);
    test_assert(ptr != NULL);
    test_int(ptr->x, 10);
    test_int(ptr->y, 20);
    }

    ecs_fini(world);
}

void Plecs_type_and_assign_in_plecs_w_2_members(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "flecs.meta.Struct Position {"
    LINE "  x :- flecs.meta.Member{flecs.meta.f32}"
    LINE "  y :- flecs.meta.Member{flecs.meta.f32}"
    LINE "}"
    LINE ""
    LINE "Foo :- Position{x: 10, y: 20}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);
    ecs_entity_t ecs_id(Position) = ecs_lookup_fullpath(world, "Position");
    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");

    test_assert(ecs_id(Position) != 0);
    test_assert(foo != 0);

    test_assert(ecs_has(world, foo, Position));

    {
    const Position *ptr = ecs_get(world, foo, Position);
    test_assert(ptr != NULL);
    test_int(ptr->x, 10);
    test_int(ptr->y, 20);
    }

    ecs_fini(world);
}

void Plecs_type_and_assign_in_plecs_w_3_members(void) {
    ecs_world_t *world = ecs_init();

    typedef struct {
        float x;
        float y;
        float z;
    } Position3D;

    const char *expr =
    HEAD "flecs.meta.Struct Position {"
    LINE "  x :- flecs.meta.Member{flecs.meta.f32}"
    LINE "  y :- flecs.meta.Member{flecs.meta.f32}"
    LINE "  z :- flecs.meta.Member{flecs.meta.f32}"
    LINE "}"
    LINE ""
    LINE "Foo :- Position{x: 10, y: 20, z: 30}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);
    ecs_entity_t ecs_id(Position3D) = ecs_lookup_fullpath(world, "Position");
    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");

    test_assert(ecs_id(Position3D) != 0);
    test_assert(foo != 0);

    test_assert(ecs_has(world, foo, Position3D));

    {
    const Position3D *ptr = ecs_get(world, foo, Position3D);
    test_assert(ptr != NULL);
    test_int(ptr->x, 10);
    test_int(ptr->y, 20);
    test_int(ptr->z, 30);
    }

    ecs_fini(world);
}

void Plecs_type_and_assign_in_plecs_w_enum(void) {
    ecs_world_t *world = ecs_init();

    typedef enum {
        Red, Green, Blue
    } Color;

    typedef struct {
        Color value;
    } SomeType;

    const char *expr =
    HEAD "flecs.meta.Enum Color {"
    LINE "  flecs.meta.Constant Red"
    LINE "  flecs.meta.Constant Green"
    LINE "  flecs.meta.Constant Blue"
    LINE "}"
    LINE ""
    LINE "flecs.meta.Struct SomeType {"
    LINE "  value :- flecs.meta.Member{Color}"
    LINE "}"
    LINE ""
    LINE "Foo :- SomeType{value: Blue}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);
    ecs_entity_t ecs_id(SomeType) = ecs_lookup_fullpath(world, "SomeType");
    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");

    test_assert(ecs_id(SomeType) != 0);
    test_assert(foo != 0);

    test_assert(ecs_has(world, foo, SomeType));

    {
    const SomeType *ptr = ecs_get(world, foo, SomeType);
    test_assert(ptr != NULL);
    test_int(ptr->value, Blue);
    }

    ecs_fini(world);
}

void Plecs_type_and_assign_in_plecs_w_enum_using_meta(void) {
    ecs_world_t *world = ecs_init();

    typedef enum {
        Red, Green, Blue
    } Color;

    typedef struct {
        Color value;
    } SomeType;

    const char *expr =
    HEAD "using flecs.meta"
    LINE
    LINE "Enum Color {"
    LINE "  Constant Red"
    LINE "  Constant Green"
    LINE "  Constant Blue"
    LINE "}"
    LINE
    LINE "Struct SomeType {"
    LINE "  value :- Member{Color}"
    LINE "}"
    LINE
    LINE "Foo :- SomeType{value: Blue}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);
    ecs_entity_t ecs_id(SomeType) = ecs_lookup_fullpath(world, "SomeType");
    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");

    /* Make sure no meta entities were created in the root */
    test_assert(ecs_lookup_fullpath(world, "Enum") == 0);
    test_assert(ecs_lookup_fullpath(world, "Struct") == 0);
    test_assert(ecs_lookup_fullpath(world, "Constant") == 0);
    test_assert(ecs_lookup_fullpath(world, "Red") == 0);
    test_assert(ecs_lookup_fullpath(world, "Green") == 0);
    test_assert(ecs_lookup_fullpath(world, "Blue") == 0);

    test_assert(ecs_id(SomeType) != 0);
    test_assert(foo != 0);

    test_assert(ecs_has(world, foo, SomeType));

    {
    const SomeType *ptr = ecs_get(world, foo, SomeType);
    test_assert(ptr != NULL);
    test_int(ptr->value, Blue);
    }

    ecs_fini(world);
}

void Plecs_type_and_assign_in_plecs_w_enum_primitive_using_meta(void) {
    ecs_world_t *world = ecs_init();

    typedef enum {
        Red, Green, Blue
    } Color;

    const char *expr =
    HEAD "using flecs.meta"
    LINE "Enum Color {"
    LINE "  Constant Red"
    LINE "  Constant Green"
    LINE "  Constant Blue"
    LINE "}"
    LINE ""
    LINE "Foo :- Color{Blue}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);
    ecs_entity_t ecs_id(Color) = ecs_lookup_fullpath(world, "Color");
    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");

    /* Make sure no meta entities were created in the root */
    test_assert(ecs_lookup_fullpath(world, "Enum") == 0);
    test_assert(ecs_lookup_fullpath(world, "Struct") == 0);
    test_assert(ecs_lookup_fullpath(world, "Constant") == 0);
    test_assert(ecs_lookup_fullpath(world, "Red") == 0);
    test_assert(ecs_lookup_fullpath(world, "Green") == 0);
    test_assert(ecs_lookup_fullpath(world, "Blue") == 0);

    test_assert(ecs_id(Color) != 0);
    test_assert(foo != 0);

    test_assert(ecs_has(world, foo, Color));

    {
    const Color *ptr = ecs_get(world, foo, Color);
    test_assert(ptr != NULL);
    test_int(*ptr, Blue);
    }

    ecs_fini(world);
}


void Plecs_type_and_assign_in_plecs_w_enum_primitive_and_struct(void) {
    ecs_world_t *world = ecs_init();

    typedef enum {
        Red, Green, Blue
    } Color;

    const char *expr =
    HEAD "using flecs.meta"
    LINE 
    LINE "Struct Position {"
    LINE "  x :- Member{f32}"
    LINE "  y :- Member{f32}"
    LINE "}"
    LINE 
    LINE "Enum Color {"
    LINE "  Constant Red"
    LINE "  Constant Green"
    LINE "  Constant Blue"
    LINE "}"
    LINE 
    LINE "Foo {"
    LINE "  - Position {x: 10, y: 20}"
    LINE "  - Color {Green}"
    LINE "}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);
    ecs_entity_t ecs_id(Position) = ecs_lookup_fullpath(world, "Position");
    ecs_entity_t ecs_id(Color) = ecs_lookup_fullpath(world, "Color");
    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");

    /* Make sure no meta entities were created in the root */
    test_assert(ecs_lookup_fullpath(world, "Enum") == 0);
    test_assert(ecs_lookup_fullpath(world, "Struct") == 0);
    test_assert(ecs_lookup_fullpath(world, "Constant") == 0);
    test_assert(ecs_lookup_fullpath(world, "Red") == 0);
    test_assert(ecs_lookup_fullpath(world, "Green") == 0);
    test_assert(ecs_lookup_fullpath(world, "Blue") == 0);

    test_assert(ecs_id(Position) != 0);
    test_assert(ecs_id(Color) != 0);
    test_assert(foo != 0);

    test_assert(ecs_has(world, foo, Position));
    test_assert(ecs_has(world, foo, Color));

    {
    const Position *ptr = ecs_get(world, foo, Position);
    test_assert(ptr != NULL);
    test_int(ptr->x, 10);
    test_int(ptr->y, 20);
    }

    {
    const Color *ptr = ecs_get(world, foo, Color);
    test_assert(ptr != NULL);
    test_int(*ptr, Green);
    }

    ecs_fini(world);
}

void Plecs_type_and_assign_in_plecs_nested_member(void) {
    ecs_world_t *world = ecs_init();

    typedef struct {
        struct {
            float x;
            float y;
        } start;

        struct {
            float x;
            float y;
        } stop;
    } Line;

    const char *expr =
    HEAD "using flecs.meta"
    LINE
    LINE "Struct Line {"
    LINE "  Member start {"
    LINE "    x :- Member{f32}"
    LINE "    y :- Member{f32}"
    LINE "  }"
    LINE "  Member stop {"
    LINE "    x :- Member{f32}"
    LINE "    y :- Member{f32}"
    LINE "  }"
    LINE "}"
    LINE
    LINE "l :- Line{start: {x: 10, y: 20}, stop: {x: 30, y: 40}}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);
    ecs_entity_t ecs_id(Line) = ecs_lookup_fullpath(world, "Line");
    ecs_entity_t l = ecs_lookup_fullpath(world, "l");

    test_assert(ecs_id(Line) != 0);
    test_assert(l != 0);

    test_assert( ecs_has(world, ecs_id(Line), EcsComponent));
    test_assert( ecs_has(world, ecs_id(Line), EcsStruct));
    test_assert( ecs_has(world, l, Line));

    const Line *ptr = ecs_get(world, l, Line);
    test_assert(ptr != NULL);
    test_int(ptr->start.x, 10);
    test_int(ptr->start.y, 20);
    test_int(ptr->stop.x, 30);
    test_int(ptr->stop.y, 40);
    
    
    ecs_fini(world);
}

void Plecs_dot_assign_nested_member(void) {
    ecs_world_t *world = ecs_init();

    typedef struct {
        struct {
            float x;
            float y;
        } start;

        struct {
            float x;
            float y;
        } stop;
    } Line;

    const char *expr =
    HEAD "using flecs.meta"
    LINE
    LINE "Struct Line {"
    LINE "  Member start {"
    LINE "    x :- Member{f32}"
    LINE "    y :- Member{f32}"
    LINE "  }"
    LINE "  Member stop {"
    LINE "    x :- Member{f32}"
    LINE "    y :- Member{f32}"
    LINE "  }"
    LINE "}"
    LINE
    LINE "l :- Line{start.x: 10, start.y: 20, stop.x: 30, stop.y: 40}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);
    ecs_entity_t ecs_id(Line) = ecs_lookup_fullpath(world, "Line");
    ecs_entity_t l = ecs_lookup_fullpath(world, "l");

    test_assert(ecs_id(Line) != 0);
    test_assert(l != 0);

    test_assert( ecs_has(world, ecs_id(Line), EcsComponent));
    test_assert( ecs_has(world, ecs_id(Line), EcsStruct));
    test_assert( ecs_has(world, l, Line));

    const Line *ptr = ecs_get(world, l, Line);
    test_assert(ptr != NULL);
    test_int(ptr->start.x, 10);
    test_int(ptr->start.y, 20);
    test_int(ptr->stop.x, 30);
    test_int(ptr->stop.y, 40);
    
    
    ecs_fini(world);
}

void Plecs_dot_assign_binary_expr(void) {
    ecs_world_t *world = ecs_init();

    typedef struct {
        struct {
            float x;
            float y;
        } start;

        struct {
            float x;
            float y;
        } stop;
    } Line;

    const char *expr =
    HEAD "using flecs.meta"
    LINE
    LINE "Struct Line {"
    LINE "  Member start {"
    LINE "    x :- Member{f32}"
    LINE "    y :- Member{f32}"
    LINE "  }"
    LINE "  Member stop {"
    LINE "    x :- Member{f32}"
    LINE "    y :- Member{f32}"
    LINE "  }"
    LINE "}"
    LINE
    LINE "l :- Line{start.x: 2 + 8, start.y: 5 + 15, stop.x: 10 + 20, stop.y: 15 + 25}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);
    ecs_entity_t ecs_id(Line) = ecs_lookup_fullpath(world, "Line");
    ecs_entity_t l = ecs_lookup_fullpath(world, "l");

    test_assert(ecs_id(Line) != 0);
    test_assert(l != 0);

    test_assert( ecs_has(world, ecs_id(Line), EcsComponent));
    test_assert( ecs_has(world, ecs_id(Line), EcsStruct));
    test_assert( ecs_has(world, l, Line));

    const Line *ptr = ecs_get(world, l, Line);
    test_assert(ptr != NULL);
    test_int(ptr->start.x, 10);
    test_int(ptr->start.y, 20);
    test_int(ptr->stop.x, 30);
    test_int(ptr->stop.y, 40);
    
    
    ecs_fini(world);
}

void Plecs_open_scope_no_parent(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "Foo {"
    LINE "  Bar"
    LINE "}"
    LINE "{"
    LINE "  Zoo"
    LINE "}"
    LINE "Hello";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);
    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    ecs_entity_t bar = ecs_lookup_fullpath(world, "Foo.Bar");
    ecs_entity_t zoo = ecs_lookup_fullpath(world, "Zoo");
    ecs_entity_t hello = ecs_lookup_fullpath(world, "Hello");

    test_assert(foo != 0);
    test_assert(bar != 0);
    test_assert(zoo != 0);
    test_assert(hello != 0);

    test_assert(ecs_has_pair(world, bar, EcsChildOf, foo));
    test_assert(!ecs_has_pair(world, zoo, EcsChildOf, EcsWildcard));
    test_assert(!ecs_has_pair(world, hello, EcsChildOf, EcsWildcard));

    ecs_fini(world);
}

void Plecs_create_subject_in_root_scope_w_resolvable_id(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "Tag";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);
    ecs_entity_t tag = ecs_lookup_child(world, 0, "Tag");
    test_assert(tag != 0);
    test_assert(tag != EcsTag);

    ecs_fini(world);
}

void Plecs_create_subject_in_scope_w_resolvable_id(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "Foo {"
    LINE "  Tag"
    LINE "}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);
    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    test_assert(foo != 0);

    ecs_entity_t tag = ecs_lookup_fullpath(world, "Foo.Tag");
    test_assert(tag != 0);
    test_assert(tag != EcsTag);

    test_assert(ecs_has_pair(world, tag, EcsChildOf, foo));

    ecs_fini(world);
}

void Plecs_create_subject_in_scope_w_resolvable_id_using(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "Foo {"
    LINE "  Bar"
    LINE "}"
    LINE
    LINE "using Foo"
    LINE
    LINE "Hello(Bar)";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);
    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    ecs_entity_t bar = ecs_lookup_fullpath(world, "Foo.Bar");
    ecs_entity_t root_bar = ecs_lookup_fullpath(world, "Bar");
    ecs_entity_t hello = ecs_lookup_fullpath(world, "Hello");

    test_assert(foo != 0);
    test_assert(bar != 0);
    test_assert(root_bar != 0);
    test_assert(hello != 0);

    test_assert(ecs_has_pair(world, bar, EcsChildOf, foo));

    test_assert(ecs_has_id(world, root_bar, hello));
    test_assert(!ecs_has_id(world, bar, hello));

    ecs_fini(world);
}

void Plecs_using_scope(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "Foo {"
    LINE "  Bar"
    LINE "}"
    LINE ""
    LINE "using Foo"
    LINE "Bar(Hello)"
    LINE "Foo.Bar(World)";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);
    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    ecs_entity_t bar = ecs_lookup_fullpath(world, "Foo.Bar");
    ecs_entity_t not_bar = ecs_lookup_fullpath(world, "Bar");
    ecs_entity_t hello = ecs_lookup_fullpath(world, "Hello");
    ecs_entity_t _world = ecs_lookup_fullpath(world, "World");

    test_assert(foo != 0);
    test_assert(bar != 0);
    test_assert(hello != 0);
    test_assert(_world != 0);
    test_assert(not_bar == 0);

    test_assert(_world != EcsWorld); /* sanity check, verified by other tests */

    test_assert(ecs_has_pair(world, bar, EcsChildOf, foo));
    test_assert(ecs_has_id(world, hello, bar));
    test_assert(ecs_has_id(world, _world, bar));

    ecs_fini(world);
}

void Plecs_using_nested_scope(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "Foo {"
    LINE "  Bar {"
    LINE "    Zoo"
    LINE "  }"
    LINE "}"
    LINE ""
    LINE "using Foo.Bar"
    LINE "Zoo(Hello)"
    LINE "Foo.Bar.Zoo(World)";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);
    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    ecs_entity_t bar = ecs_lookup_fullpath(world, "Foo.Bar");
    ecs_entity_t zoo = ecs_lookup_fullpath(world, "Foo.Bar.Zoo");
    ecs_entity_t not_bar = ecs_lookup_fullpath(world, "Bar");
    ecs_entity_t not_zoo = ecs_lookup_fullpath(world, "Zoo");
    ecs_entity_t hello = ecs_lookup_fullpath(world, "Hello");
    ecs_entity_t _world = ecs_lookup_fullpath(world, "World");

    test_assert(foo != 0);
    test_assert(bar != 0);
    test_assert(zoo != 0);
    test_assert(hello != 0);
    test_assert(_world != 0);
    test_assert(not_bar == 0);
    test_assert(not_zoo == 0);

    test_assert(_world != EcsWorld); /* sanity check, verified by other tests */

    test_assert(ecs_has_id(world, hello, zoo));
    test_assert(ecs_has_id(world, _world, zoo));

    ecs_fini(world);
}

void Plecs_using_nested_in_scope(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "Foo {"
    LINE "  Bar {"
    LINE "    Zoo"
    LINE "  }"
    LINE "}"
    LINE "{"
    LINE "  using Foo.Bar"
    LINE "  Zoo(Hello)"
    LINE "}"
    LINE "Zoo(World)";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);
    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    ecs_entity_t bar = ecs_lookup_fullpath(world, "Foo.Bar");
    ecs_entity_t zoo = ecs_lookup_fullpath(world, "Foo.Bar.Zoo");
    ecs_entity_t not_bar = ecs_lookup_fullpath(world, "Bar");
    ecs_entity_t zoo_root = ecs_lookup_fullpath(world, "Zoo");
    ecs_entity_t hello = ecs_lookup_fullpath(world, "Hello");
    ecs_entity_t _world = ecs_lookup_fullpath(world, "World");

    test_assert(foo != 0);
    test_assert(bar != 0);
    test_assert(zoo != 0);
    test_assert(hello != 0);
    test_assert(_world != 0);
    test_assert(not_bar == 0);
    test_assert(zoo_root != 0);

    test_assert(_world != EcsWorld); /* sanity check, verified by other tests */

    test_assert(ecs_has_id(world, hello, zoo));
    test_assert(ecs_has_id(world, _world, zoo_root));

    ecs_fini(world);
}

void Plecs_using_with_scope(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "using flecs.meta"
    LINE ""
    LINE "Scope {"
    LINE ""
    LINE "Foo {}"
    LINE ""
    LINE "Struct Bar {"
    LINE "  x :- Member{f32}"
    LINE "}"
    LINE ""
    LINE "}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);
    ecs_entity_t scope = ecs_lookup_fullpath(world, "Scope");
    ecs_entity_t foo = ecs_lookup_fullpath(world, "Scope.Foo");
    ecs_entity_t bar = ecs_lookup_fullpath(world, "Scope.Bar");
    ecs_entity_t x = ecs_lookup_fullpath(world, "Scope.Bar.x");

    test_assert(ecs_lookup_fullpath(world, "Foo") == 0);
    test_assert(ecs_lookup_fullpath(world, "Bar") == 0);
    test_assert(ecs_lookup_fullpath(world, "x") == 0);
    test_assert(ecs_lookup_fullpath(world, "Struct") == 0);
    test_assert(ecs_lookup_fullpath(world, "Member") == 0);

    test_assert(scope != 0);
    test_assert(foo != 0);
    test_assert(bar != 0);
    test_assert(x != 0);

    test_assert(ecs_has_pair(world, foo, EcsChildOf, scope));
    test_assert(ecs_has_pair(world, bar, EcsChildOf, scope));
    test_assert(ecs_has_pair(world, x, EcsChildOf, bar));

    ecs_fini(world);
}

void Plecs_using_w_entity_ref_in_value_2_members(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "using flecs.meta"
    LINE
    LINE "Struct Position {"
    LINE "  x :- Member{f32}" // member type is looked up in flecs.meta
    LINE "  y :- Member{f32}"
    LINE "}"
    LINE ""
    LINE "Foo :- Position{x: 10, y: 20}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);
    ecs_entity_t ecs_id(Position) = ecs_lookup_fullpath(world, "Position");
    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");

    test_assert(ecs_id(Position) != 0);
    test_assert(foo != 0);

    test_assert(ecs_has(world, foo, Position));

    {
    const Position *ptr = ecs_get(world, foo, Position);
    test_assert(ptr != NULL);
    test_int(ptr->x, 10);
    test_int(ptr->y, 20);
    }

    ecs_fini(world);
}

void Plecs_using_w_entity_ref_in_value_3_members(void) {
    ecs_world_t *world = ecs_init();

    typedef struct {
        float x;
        float y;
        float z;
    } Position3D;

    const char *expr =
    HEAD "using flecs.meta"
    LINE
    LINE "Struct Position {"
    LINE "  x :- Member{f32}" // member type is looked up in flecs.meta
    LINE "  y :- Member{f32}"
    LINE "  z :- Member{f32}"
    LINE "}"
    LINE
    LINE "Foo :- Position{x: 10, y: 20, z: 30}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);
    ecs_entity_t ecs_id(Position3D) = ecs_lookup_fullpath(world, "Position");
    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");

    test_assert(ecs_id(Position3D) != 0);
    test_assert(foo != 0);

    test_assert(ecs_has(world, foo, Position3D));

    {
    const Position3D *ptr = ecs_get(world, foo, Position3D);
    test_assert(ptr != NULL);
    test_int(ptr->x, 10);
    test_int(ptr->y, 20);
    test_int(ptr->z, 30);
    }

    ecs_fini(world);
}

void Plecs_2_using_scope(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "using Foo"
    LINE "using Bar"
    LINE
    LINE "Foo {"
    LINE "  Hello"
    LINE "}"
    LINE
    LINE "Bar {"
    LINE "  TheWorld"
    LINE "}"
    LINE
    LINE "Hello(E1)"
    LINE "TheWorld(E2)";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);
    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    ecs_entity_t hello = ecs_lookup_fullpath(world, "Foo.Hello");
    ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar");
    ecs_entity_t _world = ecs_lookup_fullpath(world, "Bar.TheWorld");

    ecs_entity_t e1 = ecs_lookup_fullpath(world, "E1");
    ecs_entity_t e2 = ecs_lookup_fullpath(world, "E2");

    test_assert(foo != 0);
    test_assert(bar != 0);
    test_assert(hello != 0);
    test_assert(_world != 0);
    test_assert(e1 != 0);
    test_assert(e2 != 0);

    test_assert(ecs_lookup_fullpath(world, "Hello") == 0);
    test_assert(ecs_lookup_fullpath(world, "TheWorld") == 0);

    test_assert(ecs_has_pair(world, hello, EcsChildOf, foo));
    test_assert(ecs_has_pair(world, _world, EcsChildOf, bar));
    test_assert(ecs_has_id(world, e1, hello));
    test_assert(ecs_has_id(world, e2, _world));

    ecs_fini(world);
}

void Plecs_2_using_in_different_scope(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    LINE "Foo {"
    LINE "  Hello"
    LINE "}"
    LINE
    LINE "Bar {"
    LINE "  TheWorld"
    LINE "}"
    LINE
    LINE "E1 {"
    LINE "  using Foo"
    LINE
    LINE "  Hello(Child)"
    LINE "  TheWorld(Child)"
    LINE "}"
    LINE
    LINE "E2 {"
    LINE "  using Bar"
    LINE
    LINE "  Hello(Child)"
    LINE "  TheWorld(Child)"
    LINE "}"
    LINE
    LINE "Hello(RootChild)"
    LINE "TheWorld(RootChild)"
    LINE;

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);
    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    ecs_entity_t hello = ecs_lookup_fullpath(world, "Foo.Hello");
    ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar");
    ecs_entity_t _world = ecs_lookup_fullpath(world, "Bar.TheWorld");

    ecs_entity_t e1 = ecs_lookup_fullpath(world, "E1");
    ecs_entity_t e1_child = ecs_lookup_fullpath(world, "E1.Child");
    ecs_entity_t e2 = ecs_lookup_fullpath(world, "E2");
    ecs_entity_t e2_child = ecs_lookup_fullpath(world, "E2.Child");

    ecs_entity_t root_hello = ecs_lookup_fullpath(world, "Hello");
    ecs_entity_t root_world = ecs_lookup_fullpath(world, "TheWorld");

    ecs_entity_t root_child = ecs_lookup_fullpath(world, "RootChild");

    test_assert(foo != 0);
    test_assert(bar != 0);
    test_assert(hello != 0);
    test_assert(_world != 0);
    test_assert(e1 != 0);
    test_assert(e2 != 0);
    test_assert(e1_child != 0);
    test_assert(e2_child != 0);
    test_assert(root_hello != 0);
    test_assert(root_world != 0);
    test_assert(root_world != 0);

    test_assert(ecs_has_pair(world, hello, EcsChildOf, foo));
    test_assert(ecs_has_pair(world, _world, EcsChildOf, bar));

    test_assert(ecs_has_pair(world, e1_child, EcsChildOf, e1));
    test_assert(ecs_has_pair(world, e2_child, EcsChildOf, e2));

    test_assert(ecs_has_id(world, e1_child, hello));
    test_assert(ecs_has_id(world, e1_child, root_world));

    test_assert(ecs_has_id(world, e2_child, root_hello));
    test_assert(ecs_has_id(world, e2_child, _world));

    test_assert(ecs_has_id(world, root_child, root_hello));
    test_assert(ecs_has_id(world, root_child, root_world));

    ecs_fini(world);
}

void Plecs_assignment_to_non_component(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "Foo :- Position{x: 10, y: 20}";

    ecs_log_set_level(-4);
    test_assert(ecs_plecs_from_str(world, NULL, expr) != 0);

    ecs_fini(world);
}

void Plecs_struct_w_member_w_assignment_to_nothing(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "flecs.meta.Struct Position {"
    LINE "  flecs.meta.Member(x) = "
    LINE "}";

    ecs_log_set_level(-4);
    test_assert(ecs_plecs_from_str(world, NULL, expr) != 0);

    ecs_fini(world);
}

void Plecs_struct_w_member_w_assignment_to_empty_scope(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "flecs.meta.Struct Position {"
    LINE "  x :- flecs.meta.Member{"
    LINE "}";

    ecs_log_set_level(-4);
    test_assert(ecs_plecs_from_str(world, NULL, expr) != 0);

    ecs_fini(world);
}

void Plecs_scope_after_assign(void) {
    ecs_world_t *world = ecs_init();

    ecs_struct_init(world, &(ecs_struct_desc_t){
        .entity = ecs_entity(world, {.name = "Position"}),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    HEAD "Foo :- Position{10, 20} {"
    LINE "  Bar"
    LINE "}";

    ecs_log_set_level(-4);
    test_assert(ecs_plecs_from_str(world, NULL, expr) != 0);

    ecs_fini(world);
}

void Plecs_assign_after_inherit(void) {
    ecs_world_t *world = ecs_init();

    ecs_entity_t ecs_id(Position) = ecs_struct_init(world, &(ecs_struct_desc_t){
        .entity = ecs_entity(world, {.name = "Position"}),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    HEAD "Foo : Position"
    LINE "Bar :- Position{10, 20}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar");

    test_assert(foo != 0);
    test_assert(bar != 0);

    test_assert( ecs_has_pair(world, foo, EcsIsA, ecs_id(Position)));
    test_assert( !ecs_has(world, foo, Position));

    test_assert( !ecs_has_pair(world, bar, EcsIsA, ecs_id(Position)));
    test_assert( ecs_has(world, bar, Position));

    const Position *ptr = ecs_get(world, bar, Position);
    test_assert(ptr != NULL);
    test_int(ptr->x, 10);
    test_int(ptr->y, 20);

    ecs_fini(world);
}

void Plecs_multiple_assignments_single_line(void) {
    ecs_world_t *world = ecs_init();

    ecs_entity_t ecs_id(Position) = ecs_struct_init(world, &(ecs_struct_desc_t){
        .entity = ecs_entity(world, {.name = "Position"}),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    ecs_entity_t ecs_id(Velocity) = ecs_struct_init(world, &(ecs_struct_desc_t){
        .entity = ecs_entity(world, {.name = "Velocity"}),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    HEAD "Foo :- Position{10, 20}, Velocity{1, 2}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    test_assert(foo != 0);

    {
    const Position *ptr = ecs_get(world, foo, Position);
    test_assert(ptr != NULL);
    test_int(ptr->x, 10);
    test_int(ptr->y, 20);
    }

    {
    const Velocity *ptr = ecs_get(world, foo, Velocity);
    test_assert(ptr != NULL);
    test_int(ptr->x, 1);
    test_int(ptr->y, 2);
    }

    ecs_fini(world);
}

void Plecs_2_stmts_in_scope_w_no_parent(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "{"
    LINE "Bar { }"
    LINE "Foo"
    LINE "}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar");

    test_assert(foo != 0);
    test_assert(bar != 0);

    test_assert( !ecs_has_id(world, foo, bar));
    test_assert( !ecs_has_pair(world, foo, EcsChildOf, bar));

    test_assert( !ecs_has_id(world, bar, foo));
    test_assert( !ecs_has_pair(world, bar, EcsChildOf, foo));

    ecs_fini(world);
}

void Plecs_scope_after_assign_1_tag(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "Foo :- TagA {"
    LINE "  Bar"
    LINE "}";

    ecs_log_set_level(-4);
    test_assert(ecs_plecs_from_str(world, NULL, expr) != 0);

    ecs_fini(world);
}

void Plecs_scope_after_assign_2_tags(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "Foo :- TagA, TagB {"
    LINE "  Bar"
    LINE "}";

    ecs_log_set_level(-4);
    test_assert(ecs_plecs_from_str(world, NULL, expr) != 0);

    ecs_fini(world);
}

void Plecs_empty_scope_after_using(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "using flecs.meta"
    LINE "{"
    LINE "  Foo"
    LINE "}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    test_assert( foo != 0);
    test_assert( !ecs_has_pair(world, foo, EcsChildOf, EcsWildcard));

    ecs_fini(world);
}

void Plecs_invalid_nested_assignment(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "Foo :-"
    LINE "Bar :- Hello";

    ecs_log_set_level(-4);
    test_assert(ecs_plecs_from_str(world, NULL, expr) != 0);

    ecs_fini(world);
}

void Plecs_invalid_partial_pair_assignment(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "Foo :- (Hello, ";

    ecs_log_set_level(-4);
    test_assert(ecs_plecs_from_str(world, NULL, expr) != 0);

    ecs_fini(world);
}

void Plecs_empty_assignment(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "Foo :-";

    ecs_log_set_level(-4);
    test_assert(ecs_plecs_from_str(world, NULL, expr) != 0);

    ecs_fini(world);
}

void Plecs_empty_assignment_before_end_of_scope(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "{Foo :-}";

    ecs_log_set_level(-4);
    test_assert(ecs_plecs_from_str(world, NULL, expr) != 0);

    ecs_fini(world);
}

void Plecs_assign_tag_to_parent(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "Foo {"
    LINE "  - Bar"
    LINE "  Child"
    LINE "}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar");
    ecs_entity_t child = ecs_lookup_fullpath(world, "Foo.Child");

    test_assert(foo != 0);
    test_assert(bar != 0);
    test_assert(child != 0);

    test_assert( ecs_has_id(world, foo, bar));
    test_assert( !ecs_has_id(world, child, bar));
    test_assert( !ecs_has_pair(world, bar, EcsChildOf, foo));
    test_assert( ecs_has_pair(world, child, EcsChildOf, foo));

    ecs_fini(world);
}

void Plecs_assign_component_to_parent(void) {
    ecs_world_t *world = ecs_init();

    ecs_entity_t ecs_id(Position) = ecs_struct_init(world, &(ecs_struct_desc_t){
        .entity = ecs_entity(world, {.name = "Position"}),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    HEAD "Foo {"
    LINE "  - Position {10, 20}"
    LINE "  Child"
    LINE "}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    ecs_entity_t child = ecs_lookup_fullpath(world, "Foo.Child");

    test_assert(foo != 0);
    test_assert(child != 0);

    test_assert( ecs_has(world, foo, Position));
    test_assert( !ecs_has(world, child, Position));
    test_assert( !ecs_has_pair(world, ecs_id(Position), EcsChildOf, foo));
    test_assert( ecs_has_pair(world, child, EcsChildOf, foo));

    const Position *ptr = ecs_get(world, foo, Position);
    test_assert(ptr != NULL);
    test_int(ptr->x, 10);
    test_int(ptr->y, 20);

    ecs_fini(world);
}

void Plecs_assign_to_parent_pair_w_new_entities_in_scope(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "Foo {"
    LINE "  - (Rel, Obj)"
    LINE "}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    ecs_entity_t rel = ecs_lookup_fullpath(world, "Rel");
    ecs_entity_t obj = ecs_lookup_fullpath(world, "Obj");

    test_assert(foo != 0);
    test_assert(rel != 0);
    test_assert(obj != 0);

    test_assert( ecs_has_pair(world, foo, rel, obj));
    test_assert( !ecs_has_pair(world, rel, EcsChildOf, EcsWildcard));
    test_assert( !ecs_has_pair(world, obj, EcsChildOf, EcsWildcard));

    ecs_fini(world);
}

void Plecs_assign_to_parent_pair_w_existing_entities_in_scope(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "Rel, Obj"
    LINE "Foo {"
    LINE "  - (Rel, Obj)"
    LINE "}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    ecs_entity_t rel = ecs_lookup_fullpath(world, "Rel");
    ecs_entity_t obj = ecs_lookup_fullpath(world, "Obj");

    test_assert(foo != 0);
    test_assert(rel != 0);
    test_assert(obj != 0);

    test_assert( ecs_has_pair(world, foo, rel, obj));
    test_assert( !ecs_has_pair(world, rel, EcsChildOf, EcsWildcard));
    test_assert( !ecs_has_pair(world, obj, EcsChildOf, EcsWildcard));

    ecs_fini(world);
}

void Plecs_default_child_component(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "DefaultChildComponent(Foo, Bar)"
    LINE "Foo(Parent) {"
    LINE "  ChildA"
    LINE "  ChildB"
    LINE "}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar");
    
    ecs_entity_t parent = ecs_lookup_fullpath(world, "Parent");
    ecs_entity_t child_a = ecs_lookup_fullpath(world, "Parent.ChildA");
    ecs_entity_t child_b = ecs_lookup_fullpath(world, "Parent.ChildB");

    test_assert(foo != 0);
    test_assert(bar != 0);
    test_assert(parent != 0);
    test_assert(child_a != 0);
    test_assert(child_b != 0);

    test_assert( ecs_has_pair(world, foo, EcsDefaultChildComponent, bar));
    test_assert( ecs_has_id(world, parent, foo));
    test_assert( ecs_has_pair(world, child_a, EcsChildOf, parent));
    test_assert( ecs_has_pair(world, child_b, EcsChildOf, parent));
    
    test_assert(ecs_has_id(world, child_a, bar));
    test_assert(ecs_has_id(world, child_b, bar));

    ecs_fini(world);
}

void Plecs_default_child_component_w_assign(void) {
    ecs_world_t *world = ecs_init();

    ecs_entity_t ecs_id(Position) = ecs_struct_init(world, &(ecs_struct_desc_t){
        .entity = ecs_entity(world, {.name = "Position"}),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    HEAD "DefaultChildComponent(Foo, Position)"
    LINE "Foo(Parent) {"
    LINE "  ChildA :- {10, 20}"
    LINE "  ChildB :- {10, 20}"
    LINE "}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    
    ecs_entity_t parent = ecs_lookup_fullpath(world, "Parent");
    ecs_entity_t child_a = ecs_lookup_fullpath(world, "Parent.ChildA");
    ecs_entity_t child_b = ecs_lookup_fullpath(world, "Parent.ChildB");

    test_assert(foo != 0);
    test_assert(parent != 0);
    test_assert(child_a != 0);
    test_assert(child_b != 0);

    test_assert( ecs_has_pair(world, foo, EcsDefaultChildComponent, ecs_id(Position)));
    test_assert( ecs_has_id(world, parent, foo));
    test_assert( ecs_has_pair(world, child_a, EcsChildOf, parent));
    test_assert( ecs_has_pair(world, child_b, EcsChildOf, parent));
    
    test_assert(ecs_has(world, child_a, Position));
    test_assert(ecs_has(world, child_b, Position));

    ecs_fini(world);
}

void Plecs_struct_type_w_default_child_component(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "using flecs.meta"
    LINE
    LINE "Struct(Position) {"
    LINE "  x :- {f32}"
    LINE "  y :- {f32}"
    LINE "}"
    LINE
    LINE "Foo :- Position{x: 10, y: 20}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    ecs_entity_t ecs_id(Position) = ecs_lookup_fullpath(world, "Position");

    test_assert(foo != 0);
    test_assert(ecs_id(Position) != 0);

    test_assert( ecs_has(world, foo, Position));

    const Position *ptr = ecs_get(world, foo, Position);
    test_assert(ptr != NULL);
    test_int(ptr->x, 10);
    test_int(ptr->y, 20);

    ecs_fini(world);
}

void Plecs_struct_type_w_default_child_component_nested_member(void) {
    ecs_world_t *world = ecs_init();

    typedef struct {
        struct {
            float x;
            float y;
        } start;

        struct {
            float x;
            float y;
        } stop;
    } Line;

    const char *expr =
    HEAD "using flecs.meta"
    LINE
    LINE "Struct(Line) {"
    LINE "  start {"
    LINE "    x :- {f32}"
    LINE "    y :- {f32}"
    LINE "  }"
    LINE "  stop {"
    LINE "    x :- {f32}"
    LINE "    y :- {f32}"
    LINE "  }"
    LINE "}"
    LINE
    LINE "Foo :- Line{start: {10, 20}, stop: {30, 40}}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    ecs_entity_t ecs_id(Line) = ecs_lookup_fullpath(world, "Line");

    test_assert(foo != 0);
    test_assert(ecs_id(Line) != 0);

    test_assert( ecs_has(world, foo, Line));

    const Line *ptr = ecs_get(world, foo, Line);
    test_assert(ptr != NULL);
    test_int(ptr->start.x, 10);
    test_int(ptr->start.y, 20);
    test_int(ptr->stop.x, 30);
    test_int(ptr->stop.y, 40);

    ecs_fini(world);
}

void Plecs_enum_type_w_default_child_component(void) {
    ecs_world_t *world = ecs_init();

    typedef enum {
        Red, Green, Blue
    } Color;

    const char *expr =
    HEAD "using flecs.meta"
    LINE
    LINE "Enum Color {"
    LINE "  Red, Green, Blue"
    LINE "}"
    LINE
    LINE "Foo :- Color{Green}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    ecs_entity_t ecs_id(Color) = ecs_lookup_fullpath(world, "Color");

    test_assert(foo != 0);
    test_assert(ecs_id(Color) != 0);

    test_assert( ecs_has(world, foo, Color));

    const Color *ptr = ecs_get(world, foo, Color);
    test_assert(ptr != NULL);
    test_int(*ptr, Green);

    ecs_fini(world);
}

void Plecs_default_type_from_with(void) {
    ecs_world_t *world = ecs_init();

    ecs_entity_t ecs_id(Position) = ecs_struct_init(world, &(ecs_struct_desc_t){
        .entity = ecs_entity(world, {.name = "Position"}),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    HEAD "with Position {"
    LINE "  e1 :- {10, 20}"
    LINE "  e2 :- {30, 40}"
    LINE "}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t e1 = ecs_lookup_fullpath(world, "e1");
    ecs_entity_t e2 = ecs_lookup_fullpath(world, "e2");

    test_assert(e1 != 0);
    test_assert(e2 != 0);

    const Position *p = ecs_get(world, e1, Position);
    test_assert(p != NULL);
    test_int(p->x, 10);
    test_int(p->y, 20);

    p = ecs_get(world, e2, Position);
    test_assert(p != NULL);
    test_int(p->x, 30);
    test_int(p->y, 40);

    ecs_fini(world);
}

void Plecs_scope_w_1_subj_and_2_pairs(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "Parent {"
    LINE " RelA(Foo, Bar)"
    LINE " RelB(Foo, Bar)"
    LINE "}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t parent = ecs_lookup_fullpath(world, "Parent");
    ecs_entity_t rel_a = ecs_lookup_fullpath(world, "RelA");
    ecs_entity_t rel_b = ecs_lookup_fullpath(world, "RelB");
    ecs_entity_t foo = ecs_lookup_fullpath(world, "Parent.Foo");
    ecs_entity_t bar = ecs_lookup_fullpath(world, "Parent.Bar");

    test_assert(parent != 0);
    test_assert(rel_a != 0);
    test_assert(rel_b != 0);
    test_assert(foo != 0);
    test_assert(bar != 0);

    test_assert( ecs_has_pair(world, foo, rel_a, bar));
    test_assert( ecs_has_pair(world, foo, rel_b, bar));

    ecs_fini(world);
}

void Plecs_inherit_from_multiple(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "Inst : Foo, Bar";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t inst = ecs_lookup_fullpath(world, "Inst");
    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar");

    test_assert(inst != 0);
    test_assert(foo != 0);
    test_assert(bar != 0);

    test_assert(ecs_has_pair(world, inst, EcsIsA, foo));
    test_assert(ecs_has_pair(world, inst, EcsIsA, bar));

    ecs_fini(world);
}

void Plecs_assign_pair_component(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "using flecs.meta"
    LINE
    LINE "Struct(Position) {"
    LINE "  x :- {f32}"
    LINE "  y :- {f32}"
    LINE "}"
    LINE
    LINE "Foo :- (Position, Bar){x: 10, y: 20}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar");
    ecs_entity_t ecs_id(Position) = ecs_lookup_fullpath(world, "Position");

    test_assert(foo != 0);
    test_assert(bar != 0);
    test_assert(ecs_id(Position) != 0);

    test_assert( ecs_has_pair(world, foo, ecs_id(Position), bar));

    const Position *ptr = ecs_get_pair(world, foo, Position, bar);
    test_assert(ptr != NULL);
    test_int(ptr->x, 10);
    test_int(ptr->y, 20);

    ecs_fini(world);
}

void Plecs_assign_pair_component_in_scope(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "using flecs.meta"
    LINE
    LINE "Struct(Position) {"
    LINE "  x :- {f32}"
    LINE "  y :- {f32}"
    LINE "}"
    LINE
    LINE "Parent {"
    LINE "  - (Position, Foo) {x: 10, y: 20}"
    LINE "  - (Position, Bar) {x: 20, y: 30}"
    LINE "}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t parent = ecs_lookup_fullpath(world, "Parent");
    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar");
    ecs_entity_t ecs_id(Position) = ecs_lookup_fullpath(world, "Position");

    test_assert(parent != 0);
    test_assert(foo != 0);
    test_assert(bar != 0);
    test_assert(ecs_id(Position) != 0);

    test_assert( ecs_has_pair(world, parent, ecs_id(Position), foo));
    test_assert( ecs_has_pair(world, parent, ecs_id(Position), bar));

    const Position *
    ptr = ecs_get_pair(world, parent, Position, foo);
    test_assert(ptr != NULL);
    test_int(ptr->x, 10);
    test_int(ptr->y, 20);

    ptr = ecs_get_pair(world, parent, Position, bar);
    test_assert(ptr != NULL);
    test_int(ptr->x, 20);
    test_int(ptr->y, 30);

    ecs_fini(world);
}

void Plecs_set_entity_names(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "foo(self)"
    LINE "foo(parent)"
    LINE "foo(cascade)"
    LINE "foo(down)"
    LINE "foo(up)";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t foo = ecs_lookup_fullpath(world, "foo");
    ecs_entity_t self = ecs_lookup_fullpath(world, "self");
    ecs_entity_t down = ecs_lookup_fullpath(world, "down");
    ecs_entity_t up = ecs_lookup_fullpath(world, "up");
    ecs_entity_t parent = ecs_lookup_fullpath(world, "parent");
    ecs_entity_t cascade = ecs_lookup_fullpath(world, "cascade");

    test_assert(foo != 0);
    test_assert(self != 0);
    test_assert(down != 0);
    test_assert(up != 0);
    test_assert(parent != 0);
    test_assert(cascade != 0);

    test_assert( ecs_has_id(world, self, foo));
    test_assert( ecs_has_id(world, down, foo));
    test_assert( ecs_has_id(world, up, foo));
    test_assert( ecs_has_id(world, parent, foo));
    test_assert( ecs_has_id(world, cascade, foo));

    ecs_fini(world);
}

void Plecs_oneof(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "flecs.meta.Enum(Color) {"
    LINE "  flecs.meta.Constant(Red)"
    LINE "  flecs.meta.Constant(Green)"
    LINE "  flecs.meta.Constant(Blue)"
    LINE "}"
    LINE "e :- (Color, Green)";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t color = ecs_lookup_fullpath(world, "Color");
    ecs_entity_t green = ecs_lookup_fullpath(world, "Color.Green");
    ecs_entity_t e = ecs_lookup_fullpath(world, "e");

    test_assert(color != 0);
    test_assert(green != 0);
    test_assert(e != 0);
    test_assert(ecs_lookup_fullpath(world, "Green") == 0);

    test_assert( ecs_has_id(world, color, EcsOneOf));
    test_assert( ecs_has_id(world, green, EcsConstant));
    test_assert( ecs_has_id(world, e, ecs_pair(color, green)));

    ecs_fini(world);
}

void Plecs_invalid_oneof(void) {
    ecs_log_set_level(-4);
    
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "flecs.meta.Enum(Color) {"
    LINE "  flecs.meta.Constant(Red)"
    LINE "  flecs.meta.Constant(Green)"
    LINE "  flecs.meta.Constant(Blue)"
    LINE "}"
    LINE "e :- (Color, Foo)";

    test_assert(ecs_plecs_from_str(world, NULL, expr) != 0);

    ecs_entity_t color = ecs_lookup_fullpath(world, "Color");
    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    ecs_entity_t e = ecs_lookup_fullpath(world, "e");

    test_assert(color != 0);
    test_assert(foo == 0);
    test_assert(e != 0);

    test_assert( !ecs_has_pair(world, e, color, EcsWildcard));

    ecs_fini(world);
}

void Plecs_brief_annotation(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "@brief A description"
    LINE "Foo"
    LINE "Bar"
    LINE ""
    LINE "@brief Another description"
    LINE "Baz";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar");
    ecs_entity_t baz = ecs_lookup_fullpath(world, "Baz");

    test_assert(foo != 0);
    test_assert(bar != 0);
    test_assert(baz != 0);

    test_str(ecs_doc_get_brief(world, foo), "A description");
    test_str(ecs_doc_get_brief(world, bar), NULL);
    test_str(ecs_doc_get_brief(world, baz), "Another description");

    ecs_fini(world);
}

void Plecs_name_annotation(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "@name A name"
    LINE "Foo"
    LINE "Bar"
    LINE ""
    LINE "@name Another name"
    LINE "Baz";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar");
    ecs_entity_t baz = ecs_lookup_fullpath(world, "Baz");

    test_assert(foo != 0);
    test_assert(bar != 0);
    test_assert(baz != 0);

    test_str(ecs_doc_get_name(world, foo), "A name");
    test_str(ecs_doc_get_name(world, bar), "Bar");
    test_str(ecs_doc_get_name(world, baz), "Another name");

    ecs_fini(world);
}

void Plecs_link_annotation(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "@link A link"
    LINE "Foo"
    LINE "Bar"
    LINE ""
    LINE "@link Another link"
    LINE "Baz";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar");
    ecs_entity_t baz = ecs_lookup_fullpath(world, "Baz");

    test_assert(foo != 0);
    test_assert(bar != 0);
    test_assert(baz != 0);

    test_str(ecs_doc_get_link(world, foo), "A link");
    test_str(ecs_doc_get_link(world, bar), NULL);
    test_str(ecs_doc_get_link(world, baz), "Another link");

    ecs_fini(world);
}

void Plecs_color_annotation(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "@color #554433"
    LINE "Foo"
    LINE "Bar"
    LINE ""
    LINE "@color rgb(10, 20, 30, 1.0)"
    LINE "Baz";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar");
    ecs_entity_t baz = ecs_lookup_fullpath(world, "Baz");

    test_assert(foo != 0);
    test_assert(bar != 0);
    test_assert(baz != 0);

    test_str(ecs_doc_get_color(world, foo), "#554433");
    test_str(ecs_doc_get_color(world, bar), NULL);
    test_str(ecs_doc_get_color(world, baz), "rgb(10, 20, 30, 1.0)");

    ecs_fini(world);
}

void Plecs_multiple_annotations(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "@brief A description"
    LINE "@name A name"
    LINE "@link A link"
    LINE "@color #554433"
    LINE "Foo"
    LINE "Bar"
    LINE ""
    LINE "@brief Another description"
    LINE "@name Another name"
    LINE "Baz";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar");
    ecs_entity_t baz = ecs_lookup_fullpath(world, "Baz");

    test_assert(foo != 0);
    test_assert(bar != 0);
    test_assert(baz != 0);

    test_str(ecs_doc_get_brief(world, foo), "A description");
    test_str(ecs_doc_get_name(world, foo), "A name");
    test_str(ecs_doc_get_link(world, foo), "A link");
    test_str(ecs_doc_get_color(world, foo), "#554433");
    test_str(ecs_doc_get_brief(world, bar), NULL);
    test_str(ecs_doc_get_brief(world, baz), "Another description");
    test_str(ecs_doc_get_name(world, baz), "Another name");

    ecs_fini(world);
}

void Plecs_annotation_w_trailing_space(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "@brief   A description  "
    LINE "Foo";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    test_assert(foo != 0);

    test_str(ecs_doc_get_brief(world, foo), "A description");

    ecs_fini(world);
}

typedef struct String {
    char *value;
} String;

void Plecs_multiline_string(void) {
    ecs_world_t *world = ecs_init();

    ecs_entity_t ecs_id(String) = ecs_struct_init(world, &(ecs_struct_desc_t){
        .entity = ecs_entity(world, {.name = "String"}),
        .members = {
            {"value", ecs_id(ecs_string_t)}
        }
    });

    const char *expr =
    HEAD "Foo :- String{value: `start"
    LINE "Hello World"
    LINE "Foo Bar"
    LINE "Special characters }{\"\"'',"
    LINE "`}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    test_assert(foo != 0);
    test_assert(ecs_has(world, foo, String));

    const String *ptr = ecs_get(world, foo, String);
    test_assert(ptr != NULL);
    test_assert(ptr->value != NULL);
    test_str(ptr->value,
        "start\n"
        "Hello World\n"
        "Foo Bar\n"
        "Special characters }{\"\"'',\n"
    );

    ecs_os_free(ptr->value);

    ecs_fini(world);
}

void Plecs_unterminated_multiline_string(void) {
    ecs_world_t *world = ecs_init();

    ecs_struct_init(world, &(ecs_struct_desc_t){
        .entity = ecs_entity(world, {.name = "String"}),
        .members = {
            {"value", ecs_id(ecs_string_t)}
        }
    });

    const char *expr =
    HEAD "Foo :- String{value: `start"
    LINE "Hello World"
    LINE "Foo Bar"
    LINE "Special characters }{\"\"'',"
    LINE "}";

    ecs_log_set_level(-4);
    test_assert(ecs_plecs_from_str(world, NULL, expr) != 0);

    ecs_fini(world);
}

void Plecs_annotate_declaration(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "@brief A brief description"
    LINE "Foo Bar";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    test_assert(foo != 0);

    ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar");
    test_assert(bar != 0);

    test_assert(ecs_has_id(world, bar, foo));

    test_assert(ecs_doc_get_brief(world, foo) == NULL);
    test_assert(ecs_doc_get_brief(world, bar) != NULL);
    test_str(ecs_doc_get_brief(world, bar), "A brief description");

    ecs_fini(world);
}

void Plecs_declaration_w_underscore_name(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "Foo _Bar";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    test_assert(foo != 0);

    ecs_entity_t bar = ecs_lookup_fullpath(world, "_Bar");
    test_assert(bar != 0);

    test_assert(!ecs_has_id(world, foo, bar));
    test_assert(ecs_has_id(world, bar, foo));

    ecs_fini(world);
}

void Plecs_anonymous_entity(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "_ :- Foo";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    test_assert(foo != 0);

    ecs_iter_t it = ecs_term_iter(world, &(ecs_term_t){ foo });
    test_assert(ecs_term_next(&it));
    test_int(it.count, 1);
    ecs_entity_t e = it.entities[0];
    test_assert(e != 0);
    test_str(ecs_get_name(world, e), NULL);
    test_assert(!ecs_term_next(&it));

    ecs_fini(world);
}

void Plecs_anonymous_entity_in_scope(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "Parent {"
    LINE "  _ :- Foo"
    LINE "}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    test_assert(foo != 0);
    ecs_entity_t parent = ecs_lookup_fullpath(world, "Parent");
    test_assert(parent != 0);

    ecs_iter_t it = ecs_term_iter(world, &(ecs_term_t){ foo });
    test_assert(ecs_term_next(&it));
    test_int(it.count, 1);
    ecs_entity_t e = it.entities[0];
    test_assert(e != 0);
    test_str(ecs_get_name(world, e), NULL);
    test_assert(ecs_has_pair(world, e, EcsChildOf, parent));
    test_assert(!ecs_term_next(&it));

    ecs_fini(world);
}

void Plecs_anonymous_declaration(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "Foo _";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    test_assert(foo != 0);

    ecs_iter_t it = ecs_term_iter(world, &(ecs_term_t){ foo });
    test_assert(ecs_term_next(&it));
    test_int(it.count, 1);
    ecs_entity_t e = it.entities[0];
    test_assert(e != 0);
    test_str(ecs_get_name(world, e), NULL);
    test_assert(!ecs_term_next(&it));

    ecs_fini(world);
}

void Plecs_const_var_int(void) {
    ecs_world_t *world = ecs_init();

    ecs_entity_t ecs_id(Position) = ecs_struct(world, {
        .entity = ecs_entity(world, {.name = "Position"}),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    HEAD "const var_x = 10"
    LINE "const var_y = 20"
    LINE ""
    LINE "e :- Position{$var_x, $var_y}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t e = ecs_lookup_fullpath(world, "e");
    test_assert(e != 0);
    test_assert(ecs_has(world, e, Position));

    const Position *p = ecs_get(world, e, Position);
    test_assert(p != NULL);
    test_int(p->x, 10);
    test_int(p->y, 20);

    ecs_fini(world);
}

void Plecs_const_var_float(void) {
    ecs_world_t *world = ecs_init();

    ecs_entity_t ecs_id(Position) = ecs_struct(world, {
        .entity = ecs_entity(world, {.name = "Position"}),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    HEAD "const var_x = 10.5"
    LINE "const var_y = 20.5"
    LINE ""
    LINE "e :- Position{$var_x, $var_y}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t e = ecs_lookup_fullpath(world, "e");
    test_assert(e != 0);
    test_assert(ecs_has(world, e, Position));

    const Position *p = ecs_get(world, e, Position);
    test_assert(p != NULL);
    test_flt(p->x, 10.5);
    test_flt(p->y, 20.5);

    ecs_fini(world);
}

void Plecs_const_var_bool(void) {
    ecs_world_t *world = ecs_init();

    typedef struct Bools {
        bool x;
        bool y;
    } Bools;

    ecs_entity_t ecs_id(Bools) = ecs_struct(world, {
        .entity = ecs_entity(world, {.name = "Bools"}),
        .members = {
            {"x", ecs_id(ecs_bool_t)},
            {"y", ecs_id(ecs_bool_t)}
        }
    });

    const char *expr =
    HEAD "const var_x = true"
    LINE "const var_y = false"
    LINE ""
    LINE "e :- Bools{$var_x, $var_y}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t e = ecs_lookup_fullpath(world, "e");
    test_assert(e != 0);
    test_assert(ecs_has(world, e, Bools));

    const Bools *p = ecs_get(world, e, Bools);
    test_assert(p != NULL);
    test_bool(p->x, true);
    test_bool(p->y, false);

    ecs_fini(world);
}

void Plecs_const_var_string(void) {
    ecs_world_t *world = ecs_init();

    ecs_entity_t ecs_id(Position) = ecs_struct(world, {
        .entity = ecs_entity(world, {.name = "Position"}),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    HEAD "const var_x = \"10.5\""
    LINE "const var_y = \"20.5\""
    LINE ""
    LINE "e :- Position{$var_x, $var_y}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t e = ecs_lookup_fullpath(world, "e");
    test_assert(e != 0);
    test_assert(ecs_has(world, e, Position));

    const Position *p = ecs_get(world, e, Position);
    test_assert(p != NULL);
    test_flt(p->x, 10.5);
    test_flt(p->y, 20.5);

    ecs_fini(world);
}

typedef struct Line {
    Position start;
    Position stop;
} Line;

void Plecs_const_var_struct(void) {
    ecs_world_t *world = ecs_init();

    ecs_entity_t ecs_id(Position) = ecs_struct(world, {
        .entity = ecs_entity(world, {.name = "Position"}),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    ecs_entity_t ecs_id(Line) = ecs_struct(world, {
        .entity = ecs_entity(world, {.name = "Line"}),
        .members = {
            {"start", ecs_id(Position)},
            {"stop", ecs_id(Position)}
        }
    });

    const char *expr =
    HEAD "const var_start = Position{10.5, 20.5}"
    LINE "const var_stop = Position{30.5, 40.5}"
    LINE ""
    LINE "e :- Line{start: $var_start, stop: $var_stop}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t e = ecs_lookup_fullpath(world, "e");
    test_assert(e != 0);
    test_assert(ecs_has(world, e, Line));

    const Line *l = ecs_get(world, e, Line);
    test_assert(l != NULL);
    test_flt(l->start.x, 10.5);
    test_flt(l->start.y, 20.5);
    test_flt(l->stop.x, 30.5);
    test_flt(l->stop.y, 40.5);

    ecs_fini(world);
}

void Plecs_const_var_redeclare(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "const var_x = 10"
    LINE "const var_x = 20";

    ecs_log_set_level(-4);
    test_assert(ecs_plecs_from_str(world, NULL, expr) != 0);

    ecs_fini(world);
}

void Plecs_const_var_scoped(void) {
    ecs_world_t *world = ecs_init();

    ecs_entity_t ecs_id(Position) = ecs_struct(world, {
        .entity = ecs_entity(world, {.name = "Position"}),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    HEAD "const var_x = 10"
    LINE "a :- Position{$var_x, $var_x}"
    LINE "a {"
    LINE "  const var_x = 20"
    LINE "  const var_y = 30"
    LINE "  b :- Position{$var_x, $var_y}"
    LINE "}"
    LINE "a {"
    LINE "  const var_y = 20"
    LINE "  c :- Position{$var_x, $var_y}"
    LINE "}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t a = ecs_lookup_fullpath(world, "a");
    test_assert(a != 0);
    ecs_entity_t b = ecs_lookup_fullpath(world, "a.b");
    test_assert(b != 0);
    ecs_entity_t c = ecs_lookup_fullpath(world, "a.c");
    test_assert(c != 0);

    test_assert(ecs_has(world, a, Position));
    test_assert(ecs_has(world, b, Position));
    test_assert(ecs_has(world, c, Position));
    const Position *p;

    p = ecs_get(world, a, Position);
    test_assert(p != NULL);
    test_int(p->x, 10);
    test_int(p->y, 10);

    p = ecs_get(world, b, Position);
    test_assert(p != NULL);
    test_int(p->x, 20);
    test_int(p->y, 30);

    p = ecs_get(world, c, Position);
    test_assert(p != NULL);
    test_int(p->x, 10);
    test_int(p->y, 20);

    ecs_fini(world);
}

void Plecs_assign_component_from_var(void) {
    ecs_world_t *world = ecs_init();

    ecs_entity_t ecs_id(Position) = ecs_struct(world, {
        .entity = ecs_entity(world, {.name = "Position"}),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    HEAD "const var_pos = Position{10, 20}"
    LINE "a :- $var_pos"
    LINE "";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t var = ecs_lookup_fullpath(world, "var_pos");
    test_assert(var == 0);

    ecs_entity_t a = ecs_lookup_fullpath(world, "a");
    test_assert(a != 0);

    const Position *p = ecs_get(world, a, Position);
    test_assert(p != NULL);
    test_int(p->x, 10);
    test_int(p->y, 20);

    ecs_fini(world);
}

void Plecs_assign_component_from_var_in_scope(void) {
    ecs_world_t *world = ecs_init();

    ecs_entity_t ecs_id(Position) = ecs_struct(world, {
        .entity = ecs_entity(world, {.name = "Position"}),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    HEAD "const var_pos = Position{10, 20}"
    LINE "a {"
    LINE " - $var_pos"
    LINE "}"
    LINE "";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t var = ecs_lookup_fullpath(world, "var_pos");
    test_assert(var == 0);

    ecs_entity_t a = ecs_lookup_fullpath(world, "a");
    test_assert(a != 0);

    const Position *p = ecs_get(world, a, Position);
    test_assert(p != NULL);
    test_int(p->x, 10);
    test_int(p->y, 20);

    ecs_fini(world);
}

void Plecs_scope_w_component_after_const_var(void) {
    ecs_world_t *world = ecs_init();

    ecs_entity_t ecs_id(Position) = ecs_struct(world, {
        .entity = ecs_entity(world, {.name = "Position"}),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    HEAD "Foo {"
    LINE "  const var = 5"
    LINE "  - Position{x: 10, y: $var}"
    LINE "}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    test_assert(foo != 0);
    test_assert(ecs_has(world, foo, Position));

    const Position *p = ecs_get(world, foo, Position);
    test_assert(p != NULL);
    test_int(p->x, 10);
    test_int(p->y, 5);

    ecs_fini(world);
}

void Plecs_component_after_const_add_expr(void) {
    ecs_world_t *world = ecs_init();

    ecs_entity_t ecs_id(Position) = ecs_struct(world, {
        .entity = ecs_entity(world, {.name = "Position"}),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    HEAD "Foo {"
    LINE "  const var = 5 + 15"
    LINE "  - Position{x: 10, y: $var}"
    LINE "}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    test_assert(foo != 0);
    test_assert(ecs_has(world, foo, Position));

    const Position *p = ecs_get(world, foo, Position);
    test_assert(p != NULL);
    test_int(p->x, 10);
    test_int(p->y, 20);

    ecs_fini(world);
}

void Plecs_component_after_const_sub_expr(void) {
    ecs_world_t *world = ecs_init();

    ecs_entity_t ecs_id(Position) = ecs_struct(world, {
        .entity = ecs_entity(world, {.name = "Position"}),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    HEAD "Foo {"
    LINE "  const var = 25 - 5"
    LINE "  - Position{x: 10, y: $var}"
    LINE "}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    test_assert(foo != 0);
    test_assert(ecs_has(world, foo, Position));

    const Position *p = ecs_get(world, foo, Position);
    test_assert(p != NULL);
    test_int(p->x, 10);
    test_int(p->y, 20);

    ecs_fini(world);
}

void Plecs_component_after_const_mul_expr(void) {
    ecs_world_t *world = ecs_init();

    ecs_entity_t ecs_id(Position) = ecs_struct(world, {
        .entity = ecs_entity(world, {.name = "Position"}),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    HEAD "Foo {"
    LINE "  const var = 2 * 10"
    LINE "  - Position{x: 10, y: $var}"
    LINE "}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    test_assert(foo != 0);
    test_assert(ecs_has(world, foo, Position));

    const Position *p = ecs_get(world, foo, Position);
    test_assert(p != NULL);
    test_int(p->x, 10);
    test_int(p->y, 20);

    ecs_fini(world);
}

void Plecs_component_after_const_div_expr(void) {
    ecs_world_t *world = ecs_init();

    ecs_entity_t ecs_id(Position) = ecs_struct(world, {
        .entity = ecs_entity(world, {.name = "Position"}),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    HEAD "Foo {"
    LINE "  const var = 40 / 2"
    LINE "  - Position{x: 10, y: $var}"
    LINE "}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    test_assert(foo != 0);
    test_assert(ecs_has(world, foo, Position));

    const Position *p = ecs_get(world, foo, Position);
    test_assert(p != NULL);
    test_int(p->x, 10);
    test_int(p->y, 20);

    ecs_fini(world);
}

void Plecs_component_after_const_paren_expr(void) {
    ecs_world_t *world = ecs_init();

    ECS_COMPONENT(world, Position);

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    LINE "e {"
    LINE "  const val = (10 + 20)"
    LINE "  - Position{$val, $val * 2}"
    LINE "}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t e = ecs_lookup_fullpath(world, "e");
    test_assert(e != 0);
    test_assert(ecs_has(world, e, Position));

    const Position *p = ecs_get(world, e, Position);
    test_assert(p != NULL);
    test_int(p->x, 30);
    test_int(p->y, 60);

    ecs_fini(world);
}

void Plecs_parse_with(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    HEAD "Foo"
    LINE "Bar {"
    LINE "  Hello"
    LINE "}";

    ECS_TAG(world, Tag);

    ecs_set_with(world, Tag);
    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);
    ecs_set_with(world, 0);

    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar");
    ecs_entity_t hello = ecs_lookup_fullpath(world, "Bar.Hello");

    test_assert(foo != 0);
    test_assert(bar != 0);
    test_assert(hello != 0);
    
    test_assert(ecs_has(world, foo, Tag));
    test_assert(ecs_has(world, bar, Tag));
    test_assert(ecs_has(world, hello, Tag));

    ecs_fini(world);
}

void Plecs_parse_with_w_with(void) {
    ecs_world_t *world = ecs_init();

    ECS_TAG(world, TagA);
    ECS_TAG(world, TagB);

    const char *expr =
    HEAD "Foo"
    LINE "with TagB {"
    LINE "  Bar {"
    LINE "    Hello"
    LINE "  }"
    LINE "}";

    ecs_set_with(world, TagA);
    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);
    ecs_set_with(world, 0);

    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar");
    ecs_entity_t hello = ecs_lookup_fullpath(world, "Bar.Hello");

    test_assert(foo != 0);
    test_assert(bar != 0);
    test_assert(hello != 0);
    
    test_assert(ecs_has(world, foo, TagA));
    test_assert(ecs_has(world, bar, TagA));
    test_assert(ecs_has(world, hello, TagA));

    test_assert(!ecs_has(world, TagB, TagA));
    test_assert(!ecs_has(world, foo, TagB));
    test_assert(ecs_has(world, bar, TagB));
    test_assert(ecs_has(world, hello, TagB));

    ecs_fini(world);
}

void Plecs_parse_with_w_tag(void) {
    ecs_world_t *world = ecs_init();

    ECS_TAG(world, TagA);
    ECS_TAG(world, TagB);

    const char *expr =
    HEAD "Foo"
    LINE "with TagB {"
    LINE "  Bar {"
    LINE "    - Hello"
    LINE "  }"
    LINE "}";

    ecs_set_with(world, TagA);
    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);
    ecs_set_with(world, 0);

    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar");
    ecs_entity_t hello = ecs_lookup_fullpath(world, "Hello");

    test_assert(foo != 0);
    test_assert(bar != 0);
    test_assert(hello != 0);
    
    test_assert(ecs_has(world, foo, TagA));
    test_assert(ecs_has(world, bar, TagA));
    test_assert(ecs_has(world, hello, TagA));
    test_assert(ecs_has_id(world, bar, hello));

    test_assert(!ecs_has(world, TagB, TagA));
    test_assert(!ecs_has(world, foo, TagB));
    test_assert(ecs_has(world, bar, TagB));
    test_assert(!ecs_has(world, hello, TagB));

    ecs_fini(world);
}

void Plecs_parse_with_value(void) {
    ecs_world_t *world = ecs_init();

    ECS_COMPONENT(world, Position);

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    LINE "with Position{10, 20} {"
    LINE "  Foo"
    LINE "  Bar"
    LINE "}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar");

    test_assert(foo != 0);
    test_assert(bar != 0);

    {
        const Position *ptr = ecs_get(world, foo, Position);
        test_assert(ptr != NULL);
        test_int(ptr->x, 10);
        test_int(ptr->y, 20);
    }
    
    {
        const Position *ptr = ecs_get(world, bar, Position);
        test_assert(ptr != NULL);
        test_int(ptr->x, 10);
        test_int(ptr->y, 20);
    }

    ecs_fini(world);
}

void Plecs_parse_with_2_values(void) {
    ecs_world_t *world = ecs_init();

    ECS_COMPONENT(world, Position);
    ECS_COMPONENT(world, Velocity);

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    ecs_struct(world, {
        .entity = ecs_id(Velocity),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    LINE "with Position{10, 20}, Velocity{1, 2} {"
    LINE "  Foo"
    LINE "  Bar"
    LINE "}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar");

    test_assert(foo != 0);
    test_assert(bar != 0);

    {
        const Position *ptr = ecs_get(world, foo, Position);
        test_assert(ptr != NULL);
        test_int(ptr->x, 10);
        test_int(ptr->y, 20);
    }
    {
        const Position *ptr = ecs_get(world, bar, Position);
        test_assert(ptr != NULL);
        test_int(ptr->x, 10);
        test_int(ptr->y, 20);
    }

    {
        const Velocity *ptr = ecs_get(world, foo, Velocity);
        test_assert(ptr != NULL);
        test_int(ptr->x, 1);
        test_int(ptr->y, 2);
    }
    {
        const Velocity *ptr = ecs_get(world, bar, Velocity);
        test_assert(ptr != NULL);
        test_int(ptr->x, 1);
        test_int(ptr->y, 2);
    }

    ecs_fini(world);
}

void Plecs_parse_with_2_nested_values(void) {
    ecs_world_t *world = ecs_init();

    ECS_COMPONENT(world, Position);
    ECS_COMPONENT(world, Velocity);

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    ecs_struct(world, {
        .entity = ecs_id(Velocity),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    LINE "with Position{10, 20} {"
    LINE "  with Velocity{1, 2} {"
    LINE "    Foo"
    LINE "    Bar"
    LINE "  }"
    LINE "}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar");

    test_assert(foo != 0);
    test_assert(bar != 0);

    {
        const Position *ptr = ecs_get(world, foo, Position);
        test_assert(ptr != NULL);
        test_int(ptr->x, 10);
        test_int(ptr->y, 20);
    }
    {
        const Position *ptr = ecs_get(world, bar, Position);
        test_assert(ptr != NULL);
        test_int(ptr->x, 10);
        test_int(ptr->y, 20);
    }

    {
        const Velocity *ptr = ecs_get(world, foo, Velocity);
        test_assert(ptr != NULL);
        test_int(ptr->x, 1);
        test_int(ptr->y, 2);
    }
    {
        const Velocity *ptr = ecs_get(world, bar, Velocity);
        test_assert(ptr != NULL);
        test_int(ptr->x, 1);
        test_int(ptr->y, 2);
    }

    ecs_fini(world);
}

void Plecs_parse_with_var(void) {
    ecs_world_t *world = ecs_init();

    ECS_COMPONENT(world, Position);

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    LINE "const pos : Position = {10, 20}"
    LINE "with $pos {"
    LINE "  Foo"
    LINE "  Bar"
    LINE "}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar");

    test_assert(foo != 0);
    test_assert(bar != 0);

    {
        const Position *ptr = ecs_get(world, foo, Position);
        test_assert(ptr != NULL);
        test_int(ptr->x, 10);
        test_int(ptr->y, 20);
    }
    {
        const Position *ptr = ecs_get(world, bar, Position);
        test_assert(ptr != NULL);
        test_int(ptr->x, 10);
        test_int(ptr->y, 20);
    }

    ecs_fini(world);
}

void Plecs_parse_with_2_vars(void) {
    ecs_world_t *world = ecs_init();

    ECS_COMPONENT(world, Position);
    ECS_COMPONENT(world, Velocity);

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    ecs_struct(world, {
        .entity = ecs_id(Velocity),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    LINE "const pos : Position = {10, 20}"
    LINE "const vel : Velocity = {1, 2}"
    LINE "with $pos, $vel {"
    LINE "  Foo"
    LINE "  Bar"
    LINE "}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar");

    test_assert(foo != 0);
    test_assert(bar != 0);

    {
        const Position *ptr = ecs_get(world, foo, Position);
        test_assert(ptr != NULL);
        test_int(ptr->x, 10);
        test_int(ptr->y, 20);
    }
    {
        const Position *ptr = ecs_get(world, bar, Position);
        test_assert(ptr != NULL);
        test_int(ptr->x, 10);
        test_int(ptr->y, 20);
    }

    {
        const Velocity *ptr = ecs_get(world, foo, Velocity);
        test_assert(ptr != NULL);
        test_int(ptr->x, 1);
        test_int(ptr->y, 2);
    }
    {
        const Velocity *ptr = ecs_get(world, bar, Velocity);
        test_assert(ptr != NULL);
        test_int(ptr->x, 1);
        test_int(ptr->y, 2);
    }

    ecs_fini(world);
}

void Plecs_parse_with_2_nested_vars(void) {
    ecs_world_t *world = ecs_init();

    ECS_COMPONENT(world, Position);
    ECS_COMPONENT(world, Velocity);

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    ecs_struct(world, {
        .entity = ecs_id(Velocity),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    LINE "const pos : Position = {10, 20}"
    LINE "const vel : Velocity = {1, 2}"
    LINE "with $pos, $vel {"
    LINE "  with $vel {"
    LINE "    Foo"
    LINE "    Bar"
    LINE "  }"
    LINE "}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar");

    test_assert(foo != 0);
    test_assert(bar != 0);

    {
        const Position *ptr = ecs_get(world, foo, Position);
        test_assert(ptr != NULL);
        test_int(ptr->x, 10);
        test_int(ptr->y, 20);
    }
    {
        const Position *ptr = ecs_get(world, bar, Position);
        test_assert(ptr != NULL);
        test_int(ptr->x, 10);
        test_int(ptr->y, 20);
    }

    {
        const Velocity *ptr = ecs_get(world, foo, Velocity);
        test_assert(ptr != NULL);
        test_int(ptr->x, 1);
        test_int(ptr->y, 2);
    }
    {
        const Velocity *ptr = ecs_get(world, bar, Velocity);
        test_assert(ptr != NULL);
        test_int(ptr->x, 1);
        test_int(ptr->y, 2);
    }

    ecs_fini(world);
}

void Plecs_parse_with_var_in_scope(void) {
    ecs_world_t *world = ecs_init();

    ECS_COMPONENT(world, Position);

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    LINE "Parent {"
    LINE "  const pos : Position = {10, 20}"
    LINE "  with $pos { "
    LINE "    Foo"
    LINE "    Bar"
    LINE "  }"
    LINE "}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t parent = ecs_lookup_fullpath(world, "Parent");
    ecs_entity_t foo = ecs_lookup_fullpath(world, "Parent.Foo");
    ecs_entity_t bar = ecs_lookup_fullpath(world, "Parent.Bar");

    test_assert(parent != 0);
    test_assert(foo != 0);
    test_assert(bar != 0);

    {
        const Position *ptr = ecs_get(world, foo, Position);
        test_assert(ptr != NULL);
        test_int(ptr->x, 10);
        test_int(ptr->y, 20);
    }
    {
        const Position *ptr = ecs_get(world, bar, Position);
        test_assert(ptr != NULL);
        test_int(ptr->x, 10);
        test_int(ptr->y, 20);
    }

    ecs_fini(world);
}


void Plecs_assign_const_w_expr(void) {
    ecs_world_t *world = ecs_init();

    ecs_entity_t ecs_id(Position) = ecs_struct(world, {
        .entity = ecs_entity(world, {.name = "Position"}),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    LINE "const var = 5 + 1"
    LINE "e :- Position{x: $var, y: $var * 2}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t e = ecs_lookup_fullpath(world, "e");
    test_assert(e != 0);
    test_assert(ecs_has(world, e, Position));

    const Position *p = ecs_get(world, e, Position);
    test_assert(p != NULL);
    test_int(p->x, 6);
    test_int(p->y, 12);

    ecs_fini(world);
}

void Plecs_const_w_type(void) {
    ecs_world_t *world = ecs_init();

    ecs_entity_t ecs_id(Position) = ecs_struct(world, {
        .entity = ecs_entity(world, {.name = "Position"}),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    LINE "const var : flecs.meta.i32 = 5 / 2"
    LINE "e :- Position{x: $var, y: $var * 3}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t e = ecs_lookup_fullpath(world, "e");
    test_assert(e != 0);
    test_assert(ecs_has(world, e, Position));

    const Position *p = ecs_get(world, e, Position);
    test_assert(p != NULL);
    test_int(p->x, 2);
    test_int(p->y, 6);

    ecs_fini(world);
}

void Plecs_assembly_no_scope(void) {
    ecs_world_t *world = ecs_init();

    ECS_COMPONENT(world, Position);

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    LINE "assembly Tree";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t tree = ecs_lookup_fullpath(world, "Tree");
    test_assert(tree != 0);

    ecs_fini(world);
}

void Plecs_assembly_empty(void) {
    ecs_world_t *world = ecs_init();

    ECS_COMPONENT(world, Position);

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    LINE "assembly Tree {"
    LINE "}";

    ecs_log_set_level(-4);
    test_assert(ecs_plecs_from_str(world, NULL, expr) != 0);

    ecs_fini(world);
}

void Plecs_assembly_no_props(void) {
    ecs_world_t *world = ecs_init();

    ECS_COMPONENT(world, Position);

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    LINE "assembly Tree {"
    LINE "  - Foo"
    LINE "}";

    ecs_log_set_level(-4);
    test_assert(ecs_plecs_from_str(world, NULL, expr) != 0);

    ecs_fini(world);
}

void Plecs_assembly_prop_no_type(void) {
    ecs_world_t *world = ecs_init();

    ECS_COMPONENT(world, Position);

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    LINE "assembly Tree {"
    LINE "  prop height"
    LINE "}";

    ecs_log_set_level(-4);
    test_assert(ecs_plecs_from_str(world, NULL, expr) != 0);

    ecs_fini(world);
}

void Plecs_assembly_prop_no_default(void) {
    ecs_world_t *world = ecs_init();

    ECS_COMPONENT(world, Position);

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    LINE "assembly Tree {"
    LINE "  prop height: flecs.meta.f32"
    LINE "}";

    ecs_log_set_level(-4);
    test_assert(ecs_plecs_from_str(world, NULL, expr) != 0);

    ecs_fini(world);
}

void Plecs_assembly_prop(void) {
    ecs_world_t *world = ecs_init();

    ECS_COMPONENT(world, Position);

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    LINE "assembly Tree {"
    LINE "  prop height: flecs.meta.f32 = 0"
    LINE "}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t tree = ecs_lookup_fullpath(world, "Tree");
    test_assert(tree != 0);

    const EcsStruct *st = ecs_get(world, tree, EcsStruct);
    test_assert(st != NULL);
    test_int(st->members.count, 1);
    test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "height");
    test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_f32_t));

    ecs_fini(world);
}

void Plecs_assembly_prop_space_colon(void) {
    ecs_world_t *world = ecs_init();

    ECS_COMPONENT(world, Position);

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    LINE "assembly Tree {"
    LINE "  prop height : flecs.meta.f32 = 0"
    LINE "}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t tree = ecs_lookup_fullpath(world, "Tree");
    test_assert(tree != 0);

    const EcsStruct *st = ecs_get(world, tree, EcsStruct);
    test_assert(st != NULL);
    test_int(st->members.count, 1);
    test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "height");
    test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_f32_t));

    ecs_fini(world);
}

void Plecs_assembly_2_props(void) {
    ecs_world_t *world = ecs_init();

    ECS_COMPONENT(world, Position);

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    LINE "assembly Tree {"
    LINE "  prop width : flecs.meta.i32 = 0"
    LINE "  prop height : flecs.meta.f32 = 0"
    LINE "}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t tree = ecs_lookup_fullpath(world, "Tree");
    test_assert(tree != 0);

    const EcsStruct *st = ecs_get(world, tree, EcsStruct);
    test_assert(st != NULL);
    test_int(st->members.count, 2);
    test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "width");
    test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_i32_t));
    test_str(ecs_vec_get_t(&st->members, ecs_member_t, 1)->name, "height");
    test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 1)->type, ecs_id(ecs_f32_t));

    ecs_fini(world);
}

void Plecs_assembly_instance_w_default_values(void) {
    ecs_world_t *world = ecs_init();

    ECS_COMPONENT(world, Position);

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    LINE "assembly Tree {"
    LINE "  prop width : flecs.meta.f32 = 10"
    LINE "  prop height : flecs.meta.f32 = 20"
    LINE "}"
    LINE ""
    LINE "e :- Tree"
    LINE "";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t tree = ecs_lookup_fullpath(world, "Tree");
    test_assert(tree != 0);

    ecs_entity_t e = ecs_lookup_fullpath(world, "e");
    test_assert(e != 0);

    const EcsStruct *st = ecs_get(world, tree, EcsStruct);
    test_assert(st != NULL);
    test_int(st->members.count, 2);
    test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "width");
    test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_f32_t));
    test_str(ecs_vec_get_t(&st->members, ecs_member_t, 1)->name, "height");
    test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 1)->type, ecs_id(ecs_f32_t));

    test_assert(ecs_has_id(world, e, tree));
    const void *ptr = ecs_get_id(world, e, tree);
    test_assert(ptr != NULL);
    char *str = ecs_ptr_to_expr(world, tree, ptr);
    test_assert(str != NULL);
    test_str(str, "{width: 10, height: 20}");
    ecs_os_free(str);

    ecs_fini(world);
}

void Plecs_assembly_instance_w_assign_default_values(void) {
    ecs_world_t *world = ecs_init();

    ECS_COMPONENT(world, Position);

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    LINE "assembly Tree {"
    LINE "  prop width : flecs.meta.f32 = 10"
    LINE "  prop height : flecs.meta.f32 = 20"
    LINE "}"
    LINE ""
    LINE "e :- Tree{}"
    LINE "";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t tree = ecs_lookup_fullpath(world, "Tree");
    test_assert(tree != 0);

    ecs_entity_t e = ecs_lookup_fullpath(world, "e");
    test_assert(e != 0);

    const EcsStruct *st = ecs_get(world, tree, EcsStruct);
    test_assert(st != NULL);
    test_int(st->members.count, 2);
    test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "width");
    test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_f32_t));
    test_str(ecs_vec_get_t(&st->members, ecs_member_t, 1)->name, "height");
    test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 1)->type, ecs_id(ecs_f32_t));

    test_assert(ecs_has_id(world, e, tree));
    const void *ptr = ecs_get_id(world, e, tree);
    test_assert(ptr != NULL);
    char *str = ecs_ptr_to_expr(world, tree, ptr);
    test_assert(str != NULL);
    test_str(str, "{width: 10, height: 20}");
    ecs_os_free(str);

    ecs_fini(world);
}

void Plecs_assembly_instance_w_overridden_values(void) {
    ecs_world_t *world = ecs_init();

    ECS_COMPONENT(world, Position);

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    LINE "assembly Tree {"
    LINE "  prop width : flecs.meta.f32 = 10"
    LINE "  prop height : flecs.meta.f32 = 20"
    LINE "}"
    LINE ""
    LINE "e :- Tree{width: 30, height: 40}"
    LINE "";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t tree = ecs_lookup_fullpath(world, "Tree");
    test_assert(tree != 0);

    ecs_entity_t e = ecs_lookup_fullpath(world, "e");
    test_assert(e != 0);

    const EcsStruct *st = ecs_get(world, tree, EcsStruct);
    test_assert(st != NULL);
    test_int(st->members.count, 2);
    test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "width");
    test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_f32_t));
    test_str(ecs_vec_get_t(&st->members, ecs_member_t, 1)->name, "height");
    test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 1)->type, ecs_id(ecs_f32_t));

    test_assert(ecs_has_id(world, e, tree));
    const void *ptr = ecs_get_id(world, e, tree);
    test_assert(ptr != NULL);
    char *str = ecs_ptr_to_expr(world, tree, ptr);
    test_assert(str != NULL);
    test_str(str, "{width: 30, height: 40}");
    ecs_os_free(str);

    ecs_fini(world);
}

void Plecs_assembly_w_child(void) {
    ecs_world_t *world = ecs_init();

    ECS_COMPONENT(world, Position);

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    LINE "assembly Tree {"
    LINE "  prop width : flecs.meta.f32 = 0"
    LINE "  prop height : flecs.meta.f32 = 0"
    LINE "  child :- Position{$width * 10 + 1, $height * 20 + 2}"
    LINE "}"
    LINE ""
    LINE "e :- Tree{width: 1, height: 2}"
    LINE "";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t tree = ecs_lookup_fullpath(world, "Tree");
    test_assert(tree != 0);

    ecs_entity_t e = ecs_lookup_fullpath(world, "e");
    test_assert(e != 0);

    ecs_entity_t child = ecs_lookup_fullpath(world, "e.child");
    test_assert(child != 0);

    const EcsStruct *st = ecs_get(world, tree, EcsStruct);
    test_assert(st != NULL);
    test_int(st->members.count, 2);
    test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "width");
    test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_f32_t));
    test_str(ecs_vec_get_t(&st->members, ecs_member_t, 1)->name, "height");
    test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 1)->type, ecs_id(ecs_f32_t));

    test_assert(ecs_has_id(world, e, tree));
    const void *ptr = ecs_get_id(world, e, tree);
    test_assert(ptr != NULL);
    char *str = ecs_ptr_to_expr(world, tree, ptr);
    test_assert(str != NULL);
    test_str(str, "{width: 1, height: 2}");
    ecs_os_free(str);

    const Position *p = ecs_get(world, child, Position);
    test_assert(p != NULL);
    test_int(p->x, 11);
    test_int(p->y, 42);

    ecs_fini(world);
}

void Plecs_assembly_w_child_parse_script(void) {
    ecs_world_t *world = ecs_init();

    ECS_COMPONENT(world, Position);

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    LINE "assembly Tree {"
    LINE "  prop width : flecs.meta.f32 = 0"
    LINE "  prop height : flecs.meta.f32 = 0"
    LINE "  child :- Position{$width * 10 + 1, $height * 20 + 2}"
    LINE "}"
    LINE ""
    LINE "e :- Tree{width: 1, height: 2}"
    LINE "";

    test_assert(ecs_script(world, {
        .entity = ecs_entity(world, { .name = "main" }),
        .str = expr
    }) != 0);

    ecs_entity_t tree = ecs_lookup_fullpath(world, "Tree");
    test_assert(tree != 0);

    ecs_entity_t e = ecs_lookup_fullpath(world, "e");
    test_assert(e != 0);

    ecs_entity_t child = ecs_lookup_fullpath(world, "e.child");
    test_assert(child != 0);

    const EcsStruct *st = ecs_get(world, tree, EcsStruct);
    test_assert(st != NULL);
    test_int(st->members.count, 2);
    test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "width");
    test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_f32_t));
    test_str(ecs_vec_get_t(&st->members, ecs_member_t, 1)->name, "height");
    test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 1)->type, ecs_id(ecs_f32_t));

    test_assert(ecs_has_id(world, e, tree));
    const void *ptr = ecs_get_id(world, e, tree);
    test_assert(ptr != NULL);
    char *str = ecs_ptr_to_expr(world, tree, ptr);
    test_assert(str != NULL);
    test_str(str, "{width: 1, height: 2}");
    ecs_os_free(str);

    const Position *p = ecs_get(world, child, Position);
    test_assert(p != NULL);
    test_int(p->x, 11);
    test_int(p->y, 42);

    ecs_fini(world);
}

void Plecs_assembly_w_child_parse_script_twice(void) {
    ecs_world_t *world = ecs_init();

    ECS_COMPONENT(world, Position);

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    LINE "assembly Tree {"
    LINE "  prop width : flecs.meta.f32 = 0"
    LINE "  prop height : flecs.meta.f32 = 0"
    LINE "  child :- Position{$width * 10 + 1, $height * 20 + 2}"
    LINE "}"
    LINE ""
    LINE "e :- Tree{width: 1, height: 2}"
    LINE "";

    test_assert(ecs_script(world, {
        .entity = ecs_entity(world, { .name = "main" }),
        .str = expr
    }) != 0);

    test_assert(ecs_script(world, {
        .entity = ecs_entity(world, { .name = "main" }),
        .str = expr
    }) != 0);

    ecs_entity_t tree = ecs_lookup_fullpath(world, "Tree");
    test_assert(tree != 0);

    ecs_entity_t e = ecs_lookup_fullpath(world, "e");
    test_assert(e != 0);

    ecs_entity_t child = ecs_lookup_fullpath(world, "e.child");
    test_assert(child != 0);

    const EcsStruct *st = ecs_get(world, tree, EcsStruct);
    test_assert(st != NULL);
    test_int(st->members.count, 2);
    test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "width");
    test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_f32_t));
    test_str(ecs_vec_get_t(&st->members, ecs_member_t, 1)->name, "height");
    test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 1)->type, ecs_id(ecs_f32_t));

    test_assert(ecs_has_id(world, e, tree));
    const void *ptr = ecs_get_id(world, e, tree);
    test_assert(ptr != NULL);
    char *str = ecs_ptr_to_expr(world, tree, ptr);
    test_assert(str != NULL);
    test_str(str, "{width: 1, height: 2}");
    ecs_os_free(str);

    const Position *p = ecs_get(world, child, Position);
    test_assert(p != NULL);
    test_int(p->x, 11);
    test_int(p->y, 42);

    ecs_fini(world);
}

void Plecs_assembly_w_child_update_after_parse(void) {
    ecs_world_t *world = ecs_init();

    ECS_COMPONENT(world, Position);

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    LINE "assembly Tree {"
    LINE "  prop width : flecs.meta.f32 = 0"
    LINE "  prop height : flecs.meta.f32 = 0"
    LINE "  child :- Position{$width * 10 + 1, $height * 20 + 2}"
    LINE "}"
    LINE ""
    LINE "e :- Tree{width: 1, height: 2}"
    LINE "";

    test_assert(ecs_script(world, {
        .entity = ecs_entity(world, { .name = "main" }),
        .str = expr
    }) != 0);

    {
        ecs_entity_t tree = ecs_lookup_fullpath(world, "Tree");
        test_assert(tree != 0);

        ecs_entity_t e = ecs_lookup_fullpath(world, "e");
        test_assert(e != 0);

        ecs_entity_t child = ecs_lookup_fullpath(world, "e.child");
        test_assert(child != 0);
    }

    test_assert(!ecs_is_deferred(world));

    const char *expr_update =
    LINE "e :- Tree{width: 3, height: 4}"
    LINE "";

    test_assert(ecs_plecs_from_str(world, NULL, expr_update) == 0);

    ecs_entity_t tree = ecs_lookup_fullpath(world, "Tree");
    test_assert(tree != 0);

    ecs_entity_t e = ecs_lookup_fullpath(world, "e");
    test_assert(e != 0);

    ecs_entity_t child = ecs_lookup_fullpath(world, "e.child");
    test_assert(child != 0);

    const EcsStruct *st = ecs_get(world, tree, EcsStruct);
    test_assert(st != NULL);
    test_int(st->members.count, 2);
    test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "width");
    test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_f32_t));
    test_str(ecs_vec_get_t(&st->members, ecs_member_t, 1)->name, "height");
    test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 1)->type, ecs_id(ecs_f32_t));

    test_assert(ecs_has_id(world, e, tree));
    const void *ptr = ecs_get_id(world, e, tree);
    test_assert(ptr != NULL);
    char *str = ecs_ptr_to_expr(world, tree, ptr);
    test_assert(str != NULL);
    test_str(str, "{width: 3, height: 4}");
    ecs_os_free(str);

    const Position *p = ecs_get(world, child, Position);
    test_assert(p != NULL);
    test_int(p->x, 31);
    test_int(p->y, 82);

    ecs_fini(world);
}

void Plecs_assembly_w_nested_child(void) {
    ecs_world_t *world = ecs_init();

    ECS_COMPONENT(world, Position);

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    LINE "assembly Tree {"
    LINE "  prop width : flecs.meta.f32 = 0"
    LINE "  prop height : flecs.meta.f32 = 0"
    LINE "  child {"
    LINE "    - Position{$width, $height}"
    LINE "    grand_child :- Position{$height, $width}"
    LINE "  }"
    LINE "}"
    LINE ""
    LINE "e :- Tree{width: 1, height: 2}"
    LINE "";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t tree = ecs_lookup_fullpath(world, "Tree");
    test_assert(tree != 0);

    ecs_entity_t e = ecs_lookup_fullpath(world, "e");
    test_assert(e != 0);

    ecs_entity_t child = ecs_lookup_fullpath(world, "e.child");
    test_assert(child != 0);

    ecs_entity_t grand_child = ecs_lookup_fullpath(world, "e.child.grand_child");
    test_assert(grand_child != 0);

    const EcsStruct *st = ecs_get(world, tree, EcsStruct);
    test_assert(st != NULL);
    test_int(st->members.count, 2);
    test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "width");
    test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_f32_t));
    test_str(ecs_vec_get_t(&st->members, ecs_member_t, 1)->name, "height");
    test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 1)->type, ecs_id(ecs_f32_t));

    test_assert(ecs_has_id(world, e, tree));
    const void *ptr = ecs_get_id(world, e, tree);
    test_assert(ptr != NULL);
    char *str = ecs_ptr_to_expr(world, tree, ptr);
    test_assert(str != NULL);
    test_str(str, "{width: 1, height: 2}");
    ecs_os_free(str);

    {
        const Position *p = ecs_get(world, child, Position);
        test_assert(p != NULL);
        test_int(p->x, 1);
        test_int(p->y, 2);
    }

    {
        const Position *p = ecs_get(world, grand_child, Position);
        test_assert(p != NULL);
        test_int(p->x, 2);
        test_int(p->y, 1);
    }

    ecs_fini(world);
}

void Plecs_assembly_w_prefab(void) {
    ecs_world_t *world = ecs_init();

    ECS_COMPONENT(world, Position);
    ECS_COMPONENT(world, Velocity);

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    ecs_struct(world, {
        .entity = ecs_id(Velocity),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    LINE "assembly Tree {"
    LINE "  prop width : flecs.meta.f32 = 0"
    LINE "  prop height : flecs.meta.f32 = 0"
    LINE "  Prefab base {"
    LINE "    - Velocity{$width * 2, $height * 3}"
    LINE "  }"
    LINE "  child : base {"
    LINE "    - Position{$width, $height}"
    LINE "  }"
    LINE "}"
    LINE ""
    LINE "e :- Tree{width: 1, height: 2}"
    LINE "";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t tree = ecs_lookup_fullpath(world, "Tree");
    test_assert(tree != 0);

    ecs_entity_t e = ecs_lookup_fullpath(world, "e");
    test_assert(e != 0);

    ecs_entity_t child = ecs_lookup_fullpath(world, "e.child");
    test_assert(child != 0);

    ecs_entity_t base = ecs_lookup_fullpath(world, "e.base");
    test_assert(base != 0);

    const EcsStruct *st = ecs_get(world, tree, EcsStruct);
    test_assert(st != NULL);
    test_int(st->members.count, 2);
    test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "width");
    test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_f32_t));
    test_str(ecs_vec_get_t(&st->members, ecs_member_t, 1)->name, "height");
    test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 1)->type, ecs_id(ecs_f32_t));

    test_assert(ecs_has_id(world, e, tree));
    const void *ptr = ecs_get_id(world, e, tree);
    test_assert(ptr != NULL);
    char *str = ecs_ptr_to_expr(world, tree, ptr);
    test_assert(str != NULL);
    test_str(str, "{width: 1, height: 2}");
    ecs_os_free(str);

    test_assert(ecs_has_pair(world, child, EcsIsA, base));

    {
        const Position *p = ecs_get(world, child, Position);
        test_assert(p != NULL);
        test_int(p->x, 1);
        test_int(p->y, 2);
    }

    {
        const Velocity *v = ecs_get(world, child, Velocity);
        test_assert(v != NULL);
        test_int(v->x, 2);
        test_int(v->y, 6);
    }

    {
        const Velocity *v = ecs_get(world, base, Velocity);
        test_assert(v != NULL);
        test_int(v->x, 2);
        test_int(v->y, 6);
    }

    ecs_fini(world);
}

void Plecs_assembly_w_prefab_tree(void) {
    ecs_world_t *world = ecs_init();

    ECS_COMPONENT(world, Position);
    ECS_COMPONENT(world, Velocity);

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    ecs_struct(world, {
        .entity = ecs_id(Velocity),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    LINE "assembly Tree {"
    LINE "  prop width : flecs.meta.f32 = 0"
    LINE "  prop height : flecs.meta.f32 = 0"
    LINE "  Prefab base {"
    LINE "    - Velocity{$width * 2, $height * 3}"
    LINE "    Prefab child {"
    LINE "      - Velocity{$height * 3, $width * 2}"
    LINE "    }"
    LINE "  }"
    LINE "  child : base {"
    LINE "    - Position{$width, $height}"
    LINE "  }"
    LINE "}"
    LINE ""
    LINE "e :- Tree{width: 1, height: 2}"
    LINE "";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t tree = ecs_lookup_fullpath(world, "Tree");
    test_assert(tree != 0);

    ecs_entity_t e = ecs_lookup_fullpath(world, "e");
    test_assert(e != 0);

    ecs_entity_t child = ecs_lookup_fullpath(world, "e.child");
    test_assert(child != 0);

    ecs_entity_t base = ecs_lookup_fullpath(world, "e.base");
    test_assert(base != 0);

    ecs_entity_t base_child = ecs_lookup_fullpath(world, "e.base.child");
    test_assert(base_child != 0);

    const EcsStruct *st = ecs_get(world, tree, EcsStruct);
    test_assert(st != NULL);
    test_int(st->members.count, 2);
    test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "width");
    test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_f32_t));
    test_str(ecs_vec_get_t(&st->members, ecs_member_t, 1)->name, "height");
    test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 1)->type, ecs_id(ecs_f32_t));

    test_assert(ecs_has_id(world, e, tree));
    const void *ptr = ecs_get_id(world, e, tree);
    test_assert(ptr != NULL);
    char *str = ecs_ptr_to_expr(world, tree, ptr);
    test_assert(str != NULL);
    test_str(str, "{width: 1, height: 2}");
    ecs_os_free(str);

    test_assert(ecs_has_pair(world, child, EcsIsA, base));

    {
        const Position *p = ecs_get(world, child, Position);
        test_assert(p != NULL);
        test_int(p->x, 1);
        test_int(p->y, 2);
    }

    {
        const Velocity *v = ecs_get(world, child, Velocity);
        test_assert(v != NULL);
        test_int(v->x, 2);
        test_int(v->y, 6);
    }

    {
        const Velocity *v = ecs_get(world, base, Velocity);
        test_assert(v != NULL);
        test_int(v->x, 2);
        test_int(v->y, 6);
    }

    {
        const Velocity *v = ecs_get(world, base_child, Velocity);
        test_assert(v != NULL);
        test_int(v->x, 6);
        test_int(v->y, 2);
    }

    ecs_fini(world);
}

void Plecs_assembly_w_nested_assembly(void) {
    ecs_world_t *world = ecs_init();

    ECS_COMPONENT(world, Position);
    ECS_COMPONENT(world, Velocity);

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    LINE "assembly Tree {"
    LINE "  prop width : flecs.meta.f32 = 0"
    LINE "  prop height : flecs.meta.f32 = 0"
    LINE "  child {"
    LINE "    - Position{$width, $height}"
    LINE "  }"
    LINE "}"
    LINE ""
    LINE "assembly Forest {"
    LINE "  prop width : flecs.meta.f32 = 0"
    LINE "  prop height : flecs.meta.f32 = 0"
    LINE "  tree_1 :- Tree{-$width, -$height}"
    LINE "  tree_2 :- Tree{$width + 1, $height + 1}"
    LINE "}"
    LINE "";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t tree = ecs_lookup_fullpath(world, "Tree");
    test_assert(tree != 0);
    ecs_entity_t forest = ecs_lookup_fullpath(world, "Forest");
    test_assert(forest != 0);
    test_assert(ecs_lookup_fullpath(world, "Forest.tree_1") != 0);
    test_assert(ecs_lookup_fullpath(world, "Forest.tree_2") != 0);
    test_assert(ecs_lookup_fullpath(world, "Forest.tree_1.child") == 0);
    test_assert(ecs_lookup_fullpath(world, "Forest.tree_2.child") == 0);
    test_assert(ecs_lookup_fullpath(world, "tree_1") == 0);
    test_assert(ecs_lookup_fullpath(world, "tree_2") == 0);

    {
        const EcsStruct *st = ecs_get(world, tree, EcsStruct);
        test_assert(st != NULL);
        test_int(st->members.count, 2);
        test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "width");
        test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_f32_t));
        test_str(ecs_vec_get_t(&st->members, ecs_member_t, 1)->name, "height");
        test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 1)->type, ecs_id(ecs_f32_t));
    }

    {
        const EcsStruct *st = ecs_get(world, forest, EcsStruct);
        test_assert(st != NULL);
        test_int(st->members.count, 2);
        test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "width");
        test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_f32_t));
        test_str(ecs_vec_get_t(&st->members, ecs_member_t, 1)->name, "height");
        test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 1)->type, ecs_id(ecs_f32_t));
    }

    test_assert(!ecs_is_deferred(world));

    const char *expr_instance =
    LINE "f :- Forest{10, 20}"
    LINE "";
    test_assert(ecs_plecs_from_str(world, NULL, expr_instance) == 0);

    ecs_entity_t f = ecs_lookup_fullpath(world, "f");
    test_assert(f != 0);
    ecs_entity_t f_tree_1 = ecs_lookup_fullpath(world, "f.tree_1");
    test_assert(f_tree_1 != 0);
    ecs_entity_t f_tree_2 = ecs_lookup_fullpath(world, "f.tree_2");
    test_assert(f_tree_2 != 0);
    ecs_entity_t f_tree_1_child = ecs_lookup_fullpath(world, "f.tree_1.child");
    test_assert(f_tree_1_child != 0);
    ecs_entity_t f_tree_2_child = ecs_lookup_fullpath(world, "f.tree_2.child");
    test_assert(f_tree_2_child != 0);

    {
        const void *ptr = ecs_get_id(world, f, forest);
        test_assert(ptr != NULL);
        char *str = ecs_ptr_to_expr(world, forest, ptr);
        test_str(str, "{width: 10, height: 20}");
        ecs_os_free(str);
    }
    {
        const void *ptr = ecs_get_id(world, f_tree_1, tree);
        test_assert(ptr != NULL);
        char *str = ecs_ptr_to_expr(world, tree, ptr);
        test_str(str, "{width: -10, height: -20}");
        ecs_os_free(str);
    }
    {
        const void *ptr = ecs_get_id(world, f_tree_2, tree);
        test_assert(ptr != NULL);
        char *str = ecs_ptr_to_expr(world, tree, ptr);
        test_str(str, "{width: 11, height: 21}");
        ecs_os_free(str);
    }

    {
        const Position *p = ecs_get(world, f_tree_1_child, Position);
        test_assert(p != NULL);
        test_int(p->x, -10);
        test_int(p->y, -20);
    }
    {
        const Position *p = ecs_get(world, f_tree_2_child, Position);
        test_assert(p != NULL);
        test_int(p->x, 11);
        test_int(p->y, 21);
    }

    ecs_fini(world);
}

void Plecs_instantiate_prefab_w_assembly(void) {
    ecs_world_t *world = ecs_init();

    ECS_COMPONENT(world, Position);

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    LINE "assembly Tree {"
    LINE "  prop width : flecs.meta.f32 = 0"
    LINE "  prop height : flecs.meta.f32 = 0"
    LINE "  child :- Position{$width, $height}"
    LINE "}"
    LINE ""
    LINE "Prefab p :- Tree{width: 10, height: 20}"
    LINE "";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t tree = ecs_lookup_fullpath(world, "Tree");
    test_assert(tree != 0);
    ecs_entity_t p = ecs_lookup_fullpath(world, "p");
    test_assert(p != 0);
    test_assert(ecs_lookup_fullpath(world, "p.child") == 0);
    test_assert(ecs_lookup_fullpath(world, "child") == 0);
    
    const char *expr_instance =
    LINE "e : p"
    LINE "";
    test_assert(ecs_plecs_from_str(world, NULL, expr_instance) == 0);

    ecs_entity_t e = ecs_lookup_fullpath(world, "e");
    test_assert(e != 0);
    ecs_entity_t child = ecs_lookup_fullpath(world, "e.child");
    test_assert(child != 0);

    test_assert(ecs_lookup_fullpath(world, "e.child") != 0);

    const EcsStruct *st = ecs_get(world, tree, EcsStruct);
    test_assert(st != NULL);
    test_int(st->members.count, 2);
    test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "width");
    test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_f32_t));
    test_str(ecs_vec_get_t(&st->members, ecs_member_t, 1)->name, "height");
    test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 1)->type, ecs_id(ecs_f32_t));

    {
        const void *ptr = ecs_get_id(world, p, tree);
        test_assert(ptr != NULL);
        char *str = ecs_ptr_to_expr(world, tree, ptr);
        test_str(str, "{width: 10, height: 20}");
        ecs_os_free(str);
    }

    test_assert(ecs_has_pair(world, e, EcsIsA, p));

    {
        const Position *p = ecs_get(world, child, Position);
        test_assert(p != NULL);
        test_int(p->x, 10);
        test_int(p->y, 20);
    }

    ecs_fini(world);
}

void Plecs_assembly_w_prefab_w_assembly(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    LINE "assembly Tree {"
    LINE "  prop count: flecs.meta.f32 = 0"
    LINE "}"
    LINE ""
    LINE "assembly Forest {"
    LINE "  prop count: flecs.meta.f32 = 0"
    LINE ""
    LINE "  Prefab TreePrefab {"
    LINE "    - Tree{count: $count}"
    LINE "  }"
    LINE ""
    LINE "  child : TreePrefab"
    LINE "}"
    LINE "f :- Forest{10}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t tree = ecs_lookup_fullpath(world, "Tree");
    test_assert(tree != 0);
    ecs_entity_t forest = ecs_lookup_fullpath(world, "Forest");
    test_assert(forest != 0);
    ecs_entity_t tree_prefab = ecs_lookup_fullpath(world, "Forest.TreePrefab");
    test_assert(tree_prefab != 0);

    ecs_entity_t f = ecs_lookup_fullpath(world, "f");
    test_assert(f != 0);
    ecs_entity_t child = ecs_lookup_fullpath(world, "f.child");
    test_assert(child != 0);

    {
        const EcsStruct *st = ecs_get(world, tree, EcsStruct);
        test_assert(st != NULL);
        test_int(st->members.count, 1);
        test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "count");
        test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_f32_t));
    }
    
    {
        const EcsStruct *st = ecs_get(world, forest, EcsStruct);
        test_assert(st != NULL);
        test_int(st->members.count, 1);
        test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "count");
        test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_f32_t));
    }
    
    {
        const void *ptr = ecs_get_id(world, f, forest);
        test_assert(ptr != NULL);
        char *str = ecs_ptr_to_expr(world, forest, ptr);
        test_str(str, "{count: 10}");
        ecs_os_free(str);
    }

    {
        const void *ptr = ecs_get_id(world, tree_prefab, tree);
        test_assert(ptr != NULL);
        char *str = ecs_ptr_to_expr(world, tree, ptr);
        test_str(str, "{count: 0}");
        ecs_os_free(str);
    }

    ecs_fini(world);
}

void Plecs_3_assemblies(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    LINE "assembly Tree {"
    LINE "  prop t: flecs.meta.f32 = 10"
    LINE "}"
    LINE ""
    LINE "assembly Forest {"
    LINE "  prop f: flecs.meta.f32 = 20"
    LINE "}"
    LINE ""
    LINE "assembly Park {"
    LINE "  prop p: flecs.meta.f32 = 30"
    LINE "}"
    LINE ""
    LINE "a :- Tree{}"
    LINE "b :- Forest{}"
    LINE "c :- Park{}"
    LINE "";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t tree = ecs_lookup_fullpath(world, "Tree");
    test_assert(tree != 0);
    ecs_entity_t forest = ecs_lookup_fullpath(world, "Forest");
    test_assert(forest != 0);
    ecs_entity_t park = ecs_lookup_fullpath(world, "Park");
    test_assert(park != 0);
    
    ecs_entity_t a = ecs_lookup_fullpath(world, "a");
    test_assert(a != 0);
    ecs_entity_t b = ecs_lookup_fullpath(world, "b");
    test_assert(b != 0);
    ecs_entity_t c = ecs_lookup_fullpath(world, "c");
    test_assert(c != 0);

    {
        const EcsStruct *st = ecs_get(world, tree, EcsStruct);
        test_assert(st != NULL);
        test_int(st->members.count, 1);
        test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "t");
        test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_f32_t));
    }  
    {
        const EcsStruct *st = ecs_get(world, forest, EcsStruct);
        test_assert(st != NULL);
        test_int(st->members.count, 1);
        test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "f");
        test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_f32_t));
    }
    {
        const EcsStruct *st = ecs_get(world, park, EcsStruct);
        test_assert(st != NULL);
        test_int(st->members.count, 1);
        test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "p");
        test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_f32_t));
    }
    
    {
        const void *ptr = ecs_get_id(world, a, tree);
        test_assert(ptr != NULL);
        char *str = ecs_ptr_to_expr(world, tree, ptr);
        test_str(str, "{t: 10}");
        ecs_os_free(str);
    }
    {
        const void *ptr = ecs_get_id(world, b, forest);
        test_assert(ptr != NULL);
        char *str = ecs_ptr_to_expr(world, forest, ptr);
        test_str(str, "{f: 20}");
        ecs_os_free(str);
    }
    {
        const void *ptr = ecs_get_id(world, c, park);
        test_assert(ptr != NULL);
        char *str = ecs_ptr_to_expr(world, park, ptr);
        test_str(str, "{p: 30}");
        ecs_os_free(str);
    }

    ecs_fini(world);
}

void Plecs_assembly_nested_w_default_var(void) {
    ecs_world_t *world = ecs_init();

    ECS_COMPONENT(world, Position);

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    LINE "assembly Tree {"
    LINE "  prop count: flecs.meta.f32 = 0"
    LINE "  trunk :- Position{$count, $count * 2}"
    LINE "}"
    LINE ""
    LINE "assembly Forest {"
    LINE "  prop count: flecs.meta.f32 = 0"
    LINE "  child :- Tree{count:$}"
    LINE "}"
    LINE "f :- Forest{10}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t tree = ecs_lookup_fullpath(world, "Tree");
    test_assert(tree != 0);
    ecs_entity_t forest = ecs_lookup_fullpath(world, "Forest");
    test_assert(forest != 0);

    ecs_entity_t f = ecs_lookup_fullpath(world, "f");
    test_assert(f != 0);
    ecs_entity_t child = ecs_lookup_fullpath(world, "f.child");
    test_assert(child != 0);
    ecs_entity_t trunk = ecs_lookup_fullpath(world, "f.child.trunk");
    test_assert(trunk != 0);

    {
        const EcsStruct *st = ecs_get(world, tree, EcsStruct);
        test_assert(st != NULL);
        test_int(st->members.count, 1);
        test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "count");
        test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_f32_t));
    }
    
    {
        const EcsStruct *st = ecs_get(world, forest, EcsStruct);
        test_assert(st != NULL);
        test_int(st->members.count, 1);
        test_str(ecs_vec_get_t(&st->members, ecs_member_t, 0)->name, "count");
        test_uint(ecs_vec_get_t(&st->members, ecs_member_t, 0)->type, ecs_id(ecs_f32_t));
    }

    {
        const void *ptr = ecs_get_id(world, f, forest);
        test_assert(ptr != NULL);
        char *str = ecs_ptr_to_expr(world, forest, ptr);
        test_str(str, "{count: 10}");
        ecs_os_free(str);
    }
    {
        const void *ptr = ecs_get_id(world, child, tree);
        test_assert(ptr != NULL);
        char *str = ecs_ptr_to_expr(world, tree, ptr);
        test_str(str, "{count: 10}");
        ecs_os_free(str);
    }

    {
        const Position *p = ecs_get(world, trunk, Position);
        test_assert(p != NULL);
        test_int(p->x, 10);
        test_int(p->y, 20);
    }

    ecs_fini(world);
}

void Plecs_assembly_w_anonymous(void) {
    ecs_world_t *world = ecs_init();

    ECS_COMPONENT(world, Position);

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    LINE "assembly Tree {"
    LINE "  prop count: flecs.meta.i32 = 0"
    LINE "  _ :- Position{$count, $count * 2}"
    LINE "  _ :- Position{$count, $count * 2}"
    LINE "}"
    LINE ""
    LINE "t :- Tree{10}";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t tree = ecs_lookup_fullpath(world, "Tree");
    test_assert(tree != 0);
    ecs_entity_t t = ecs_lookup_fullpath(world, "t");
    test_assert(t != 0);

    {
        ecs_filter_t *f = ecs_filter(world, {
            .terms = {
                { .id = ecs_childof(tree) },
                { .id = ecs_id(Position) },
                { .id = EcsPrefab },
            }
        });

        ecs_iter_t it = ecs_filter_iter(world, f);
        test_int(2, ecs_iter_count(&it));
        ecs_filter_fini(f);
    }

    {
        ecs_filter_t *f = ecs_filter(world, {
            .terms = {
                { .id = ecs_childof(t) },
                { .id = ecs_id(Position) },
            }
        });

        ecs_iter_t it = ecs_filter_iter(world, f);
        test_int(2, ecs_iter_count(&it));
        ecs_filter_fini(f);
    }

    ecs_fini(world);
}

void Plecs_assembly_w_anonymous_parse_again(void) {
    ecs_world_t *world = ecs_init();

    ECS_COMPONENT(world, Position);

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    LINE "assembly Tree {"
    LINE "  prop count: flecs.meta.i32 = 0"
    LINE "  _ :- Position{$count, $count * 2}"
    LINE "  _ :- Position{$count, $count * 2}"
    LINE "}"
    LINE ""
    LINE "t :- Tree{10}";
    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    const char *expr_again =
    LINE "t :- Tree{10}";
    test_assert(ecs_plecs_from_str(world, NULL, expr_again) == 0);

    ecs_entity_t tree = ecs_lookup_fullpath(world, "Tree");
    test_assert(tree != 0);
    ecs_entity_t t = ecs_lookup_fullpath(world, "t");
    test_assert(t != 0);

    {
        ecs_filter_t *f = ecs_filter(world, {
            .terms = {
                { .id = ecs_childof(tree) },
                { .id = ecs_id(Position) },
                { .id = EcsPrefab },
            }
        });

        ecs_iter_t it = ecs_filter_iter(world, f);
        test_int(2, ecs_iter_count(&it));
        ecs_filter_fini(f);
    }

    {
        ecs_filter_t *f = ecs_filter(world, {
            .terms = {
                { .id = ecs_childof(t) },
                { .id = ecs_id(Position) },
            }
        });

        ecs_iter_t it = ecs_filter_iter(world, f);
        test_int(2, ecs_iter_count(&it));
        ecs_filter_fini(f);
    }

    ecs_fini(world);
}

void Plecs_typed_const_w_composite_type_invalid_assignment(void) {
    ecs_world_t *world = ecs_init();

    ecs_struct(world, {
        .entity = ecs_entity(world, {.name = "Position"}),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    HEAD "const var_pos : Position = Position{10, 20}"
    LINE "a :- $var_pos"
    LINE "";

    ecs_log_set_level(-4);
    test_assert(ecs_plecs_from_str(world, NULL, expr) != 0);

    ecs_fini(world);
}

void Plecs_typed_const_w_composite_type(void) {
    ecs_world_t *world = ecs_init();

    ecs_entity_t ecs_id(Position) = ecs_struct(world, {
        .entity = ecs_entity(world, {.name = "Position"}),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    HEAD "const var_pos : Position = {10, 20}"
    LINE "a :- $var_pos"
    LINE "";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t var = ecs_lookup_fullpath(world, "var_pos");
    test_assert(var == 0);

    ecs_entity_t a = ecs_lookup_fullpath(world, "a");
    test_assert(a != 0);

    const Position *p = ecs_get(world, a, Position);
    test_assert(p != NULL);
    test_int(p->x, 10);
    test_int(p->y, 20);

    ecs_fini(world);
}

void Plecs_assign_var_to_typed_const_w_composite_type(void) {
    ecs_world_t *world = ecs_init();

    ecs_entity_t ecs_id(Position) = ecs_struct(world, {
        .entity = ecs_entity(world, {.name = "Position"}),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    HEAD "const var_pos_a : Position = {10, 20}"
    HEAD "const var_pos_b = $var_pos_a"
    LINE "a :- $var_pos_b"
    LINE "";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t var = ecs_lookup_fullpath(world, "var_pos");
    test_assert(var == 0);

    ecs_entity_t a = ecs_lookup_fullpath(world, "a");
    test_assert(a != 0);

    const Position *p = ecs_get(world, a, Position);
    test_assert(p != NULL);
    test_int(p->x, 10);
    test_int(p->y, 20);

    ecs_fini(world);
}

void Plecs_assembly_w_composite_prop_invalid_assignment(void) {
    ecs_world_t *world = ecs_init();

    ECS_COMPONENT(world, Position);

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    LINE "assembly Tree {"
    LINE "  prop pos: Position = Position{10, 20}"
    LINE "  child :- $pos"
    LINE "}"
    LINE ""
    LINE "t :- Tree{pos: {20, 30}}";

    ecs_log_set_level(-4);
    test_assert(ecs_plecs_from_str(world, NULL, expr) != 0);

    ecs_fini(world);
}

void Plecs_assembly_w_composite_prop(void) {
    ecs_world_t *world = ecs_init();

    ECS_COMPONENT(world, Position);

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    LINE "assembly Tree {"
    LINE "  prop pos: Position = {10, 20}"
    LINE "  child :- $pos"
    LINE "}"
    LINE "t :- Tree{pos: {20, 30}}"
    LINE "";
    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t tree = ecs_lookup_fullpath(world, "Tree");
    test_assert(tree != 0);
    ecs_entity_t t = ecs_lookup_fullpath(world, "t");
    test_assert(t != 0);
    ecs_entity_t t_child = ecs_lookup_fullpath(world, "t.child");
    test_assert(t_child != 0);

    {
        const void *ptr = ecs_get_id(world, t, tree);
        test_assert(ptr != NULL);
        char *str = ecs_ptr_to_expr(world, tree, ptr);
        test_str(str, "{pos: {x: 20, y: 30}}");
        ecs_os_free(str);
    }

    {
        const Position *p = ecs_get(world, t_child, Position);
        test_assert(p != NULL);
        test_int(p->x, 20);
        test_int(p->y, 30);
    }

    ecs_fini(world);
}

void Plecs_assembly_with_with(void) {
    ecs_world_t *world = ecs_init();

    ECS_COMPONENT(world, Position);

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    LINE "assembly Tree {"
    LINE "  prop count: flecs.meta.i32 = 0"
    LINE "  with Foo {"
    LINE "    child :- Position{$count, $count * 2}"
    LINE "  }"
    LINE "}"
    LINE "t :- Tree{count: 10}"
    LINE "";
    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t tree = ecs_lookup_fullpath(world, "Tree");
    test_assert(tree != 0);
    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    test_assert(foo != 0);
    ecs_entity_t t = ecs_lookup_fullpath(world, "t");
    test_assert(t != 0);
    ecs_entity_t t_child = ecs_lookup_fullpath(world, "t.child");
    test_assert(t_child != 0);

    {
        const void *ptr = ecs_get_id(world, t, tree);
        test_assert(ptr != NULL);
        char *str = ecs_ptr_to_expr(world, tree, ptr);
        test_str(str, "{count: 10}");
        ecs_os_free(str);
    }

    {
        const Position *p = ecs_get(world, t_child, Position);
        test_assert(p != NULL);
        test_int(p->x, 10);
        test_int(p->y, 20);
    }

    ecs_fini(world);
}

void Plecs_using_wildcard(void) {
    ecs_world_t *world = ecs_init();

    ecs_entity_t p1 = ecs_new_entity(world, "foo.p1");
    ecs_entity_t p2 = ecs_new_entity(world, "foo.p2");

    ecs_set_scope(world, p1);
    ECS_COMPONENT(world, Position);

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    ecs_set_scope(world, p2);
    ECS_COMPONENT(world, Velocity);

    ecs_struct(world, {
        .entity = ecs_id(Velocity),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    ecs_set_scope(world, 0);

    const char *expr =
    LINE "using foo.*\n"
    LINE "e :- Position{10, 20}\n"
    LINE "e :- Velocity{1, 2}\n";
    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t pos = ecs_lookup_fullpath(world, "foo.p1.Position");
    test_assert(pos == ecs_id(Position));
    ecs_entity_t vel = ecs_lookup_fullpath(world, "foo.p2.Velocity");
    test_assert(vel == ecs_id(Velocity));
    ecs_entity_t e = ecs_lookup_fullpath(world, "e");
    test_assert(e != 0);

    test_assert(ecs_has(world, e, Position));
    test_assert(ecs_has(world, e, Velocity));

    {
        const Position *ptr = ecs_get(world, e, Position);
        test_assert(ptr != NULL);
        test_int(ptr->x, 10);
        test_int(ptr->y, 20);
    }
    {
        const Velocity *ptr = ecs_get(world, e, Velocity);
        test_assert(ptr != NULL);
        test_int(ptr->x, 1);
        test_int(ptr->y, 2);
    }

    ecs_fini(world);
}

void Plecs_single_line_comment_in_value(void) {
    ecs_world_t *world = ecs_init();

    ECS_COMPONENT(world, Position);

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    LINE "e :- Position{\n"
    LINE "  x: 10\n"
    LINE "  //foo\n"
    LINE "  y: 20\n"
    LINE "}";
    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t e = ecs_lookup_fullpath(world, "e");
    test_assert(e != 0);

    test_assert(ecs_has(world, e, Position));

    const Position *ptr = ecs_get(world, e, Position);
    test_assert(ptr != NULL);
    test_int(ptr->x, 10);
    test_int(ptr->y, 20);

    ecs_fini(world);
}

void Plecs_multi_line_comment_in_value(void) {
    ecs_world_t *world = ecs_init();

    ECS_COMPONENT(world, Position);

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    LINE "e :- Position{\n"
    LINE "  x: 10\n"
    LINE "  /*\n"
    LINE "   * foo\n"
    LINE "   */\n"
    LINE "  y: 20\n"
    LINE "}";
    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t e = ecs_lookup_fullpath(world, "e");
    test_assert(e != 0);

    test_assert(ecs_has(world, e, Position));

    const Position *ptr = ecs_get(world, e, Position);
    test_assert(ptr != NULL);
    test_int(ptr->x, 10);
    test_int(ptr->y, 20);

    ecs_fini(world);
}

void Plecs_unterminated_multi_line_comment_in_value(void) {
    ecs_world_t *world = ecs_init();

    ECS_COMPONENT(world, Position);

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    LINE "e :- Position{\n"
    LINE "  x: 10\n"
    LINE "  /*\n"
    LINE "   * foo\n"
    LINE "  y: 20\n"
    LINE "}";
    ecs_log_set_level(-4);
    test_assert(ecs_plecs_from_str(world, NULL, expr) != 0);

    ecs_fini(world);
}

void Plecs_module_stmt(void) {
    ecs_world_t *world = ecs_init();

    ECS_COMPONENT(world, Position);

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    LINE "module hello\n"
    LINE "e :- Position{10, 20}\n"
    LINE "e :- Foo\n";
    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t hello = ecs_lookup_fullpath(world, "hello");
    test_assert(hello != 0);
    test_assert(ecs_has_id(world, hello, EcsModule));

    ecs_entity_t e = ecs_lookup_fullpath(world, "hello.e");
    test_assert(e != 0);
    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    test_assert(foo != 0);

    test_assert(ecs_has(world, e, Position));
    test_assert(ecs_has_id(world, e, foo));

    {
        const Position *ptr = ecs_get(world, e, Position);
        test_assert(ptr != NULL);
        test_int(ptr->x, 10);
        test_int(ptr->y, 20);
    }

    ecs_fini(world);
}

void Plecs_nested_module_stmt(void) {
    ecs_world_t *world = ecs_init();

    ECS_COMPONENT(world, Position);

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    LINE "module hello.world\n"
    LINE "e :- Position{10, 20}\n"
    LINE "e :- Foo\n";
    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t hello = ecs_lookup_fullpath(world, "hello");
    test_assert(hello != 0);
    test_assert(ecs_has_id(world, hello, EcsModule));
    ecs_entity_t hello_world = ecs_lookup_fullpath(world, "hello.world");
    test_assert(hello_world != 0);
    test_assert(ecs_has_id(world, hello_world, EcsModule));

    ecs_entity_t e = ecs_lookup_fullpath(world, "hello.world.e");
    test_assert(e != 0);
    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    test_assert(foo != 0);

    test_assert(ecs_has(world, e, Position));
    test_assert(ecs_has_id(world, e, foo));

    {
        const Position *ptr = ecs_get(world, e, Position);
        test_assert(ptr != NULL);
        test_int(ptr->x, 10);
        test_int(ptr->y, 20);
    }

    ecs_fini(world);
}

void Plecs_module_stmt_w_scope(void) {
    ecs_world_t *world = ecs_init();

    ECS_COMPONENT(world, Position);

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    LINE "module hello.world\n"
    LINE "parent {\n"
    LINE "  e :- Position{10, 20}\n"
    LINE "  e :- Foo\n"
    LINE "}\n"
    LINE "f :- Position{30, 40}\n"
    LINE "f :- Foo\n"
    LINE "";
    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t hello = ecs_lookup_fullpath(world, "hello");
    test_assert(hello != 0);
    test_assert(ecs_has_id(world, hello, EcsModule));
    ecs_entity_t hello_world = ecs_lookup_fullpath(world, "hello.world");
    test_assert(hello_world != 0);
    test_assert(ecs_has_id(world, hello_world, EcsModule));

    ecs_entity_t parent = ecs_lookup_fullpath(world, "hello.world.parent");
    test_assert(parent != 0);
    ecs_entity_t e = ecs_lookup_fullpath(world, "hello.world.parent.e");
    test_assert(e != 0);
    ecs_entity_t f = ecs_lookup_fullpath(world, "hello.world.f");
    test_assert(f != 0);
    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    test_assert(foo != 0);

    test_assert(ecs_has(world, e, Position));
    test_assert(ecs_has_id(world, e, foo));
    test_assert(ecs_has(world, f, Position));
    test_assert(ecs_has_id(world, f, foo));

    {
        const Position *ptr = ecs_get(world, e, Position);
        test_assert(ptr != NULL);
        test_int(ptr->x, 10);
        test_int(ptr->y, 20);
    }
    {
        const Position *ptr = ecs_get(world, f, Position);
        test_assert(ptr != NULL);
        test_int(ptr->x, 30);
        test_int(ptr->y, 40);
    }

    ecs_fini(world);
}

void Plecs_module_stmt_w_nested_scope(void) {
    ecs_world_t *world = ecs_init();

    ECS_COMPONENT(world, Position);

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    LINE "module hello.world\n"
    LINE "parent {\n"
    LINE "  child {\n"
    LINE "    e :- Position{10, 20}\n"
    LINE "    e :- Foo\n"
    LINE "  }\n"
    LINE "}\n"
    LINE "f :- Position{30, 40}\n"
    LINE "f :- Foo\n"
    LINE "";
    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t hello = ecs_lookup_fullpath(world, "hello");
    test_assert(hello != 0);
    test_assert(ecs_has_id(world, hello, EcsModule));
    ecs_entity_t hello_world = ecs_lookup_fullpath(world, "hello.world");
    test_assert(hello_world != 0);
    test_assert(ecs_has_id(world, hello_world, EcsModule));

    ecs_entity_t parent = ecs_lookup_fullpath(world, "hello.world.parent");
    test_assert(parent != 0);
    ecs_entity_t child = ecs_lookup_fullpath(world, "hello.world.parent.child");
    test_assert(child != 0);
    ecs_entity_t e = ecs_lookup_fullpath(world, "hello.world.parent.child.e");
    test_assert(e != 0);
    ecs_entity_t f = ecs_lookup_fullpath(world, "hello.world.f");
    test_assert(f != 0);
    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    test_assert(foo != 0);

    test_assert(ecs_has(world, e, Position));
    test_assert(ecs_has_id(world, e, foo));
    test_assert(ecs_has(world, f, Position));
    test_assert(ecs_has_id(world, f, foo));

    {
        const Position *ptr = ecs_get(world, e, Position);
        test_assert(ptr != NULL);
        test_int(ptr->x, 10);
        test_int(ptr->y, 20);
    }
    {
        const Position *ptr = ecs_get(world, f, Position);
        test_assert(ptr != NULL);
        test_int(ptr->x, 30);
        test_int(ptr->y, 40);
    }

    ecs_fini(world);
}

void Plecs_module_w_assembly(void) {
    ecs_world_t *world = ecs_init();

    ECS_COMPONENT(world, Position);

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    LINE "module hello.world\n"
    LINE "assembly Tree {\n"
    LINE "  prop count: flecs.meta.i32 = 0\n"
    LINE "  child :- Position{$count, $count * 2}\n"
    LINE "}\n"
    LINE "";
    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    const char *expr_inst =
    LINE "t :- hello.world.Tree{10}\n";
    test_assert(ecs_plecs_from_str(world, NULL, expr_inst) == 0);

    ecs_entity_t tree = ecs_lookup_fullpath(world, "hello.world.Tree");
    test_assert(tree != 0);
    ecs_entity_t t = ecs_lookup_fullpath(world, "t");
    test_assert(t != 0);
    ecs_entity_t child = ecs_lookup_fullpath(world, "t.child");
    test_assert(child != 0);

    {
        const void *ptr = ecs_get_id(world, t, tree);
        test_assert(ptr != NULL);
        char *str = ecs_ptr_to_expr(world, tree, ptr);
        test_str(str, "{count: 10}");
        ecs_os_free(str);
    }

    test_assert(ecs_has(world, child, Position));

    {
        const Position *ptr = ecs_get(world, child, Position);
        test_assert(ptr != NULL);
        test_int(ptr->x, 10);
        test_int(ptr->y, 20);
    }

    ecs_fini(world);
}

void Plecs_module_w_nested_assembly(void) {
    ecs_world_t *world = ecs_init();

    ECS_COMPONENT(world, Position);

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    LINE "module hello.world\n"
    LINE "assembly Tree {\n"
    LINE "  prop count: flecs.meta.i32 = 0\n"
    LINE "  child :- Position{$count, $count * 2}\n"
    LINE "}\n"
    LINE "assembly Forest {\n"
    LINE "  prop count: flecs.meta.i32 = 0\n"
    LINE "  t :- Tree{count:$}\n"
    LINE "}\n"
    LINE "";
    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    const char *expr_inst =
    LINE "f :- hello.world.Forest{10}\n";
    test_assert(ecs_plecs_from_str(world, NULL, expr_inst) == 0);

    ecs_entity_t forest = ecs_lookup_fullpath(world, "hello.world.Forest");
    test_assert(forest != 0);
    ecs_entity_t tree = ecs_lookup_fullpath(world, "hello.world.Tree");
    test_assert(tree != 0);
    ecs_entity_t f = ecs_lookup_fullpath(world, "f");
    test_assert(f != 0);
    ecs_entity_t t = ecs_lookup_fullpath(world, "f.t");
    test_assert(t != 0);
    ecs_entity_t child = ecs_lookup_fullpath(world, "f.t.child");
    test_assert(child != 0);

    {
        const void *ptr = ecs_get_id(world, f, forest);
        test_assert(ptr != NULL);
        char *str = ecs_ptr_to_expr(world, forest, ptr);
        test_str(str, "{count: 10}");
        ecs_os_free(str);
    }
    {
        const void *ptr = ecs_get_id(world, t, tree);
        test_assert(ptr != NULL);
        char *str = ecs_ptr_to_expr(world, tree, ptr);
        test_str(str, "{count: 10}");
        ecs_os_free(str);
    }

    test_assert(ecs_has(world, child, Position));

    {
        const Position *ptr = ecs_get(world, child, Position);
        test_assert(ptr != NULL);
        test_int(ptr->x, 10);
        test_int(ptr->y, 20);
    }

    ecs_fini(world);
}

void Plecs_assign_singleton_tag(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    LINE "$ :- Foo\n";
    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    test_assert(foo != 0);

    test_assert(ecs_has_id(world, foo, foo));

    ecs_fini(world);
}

void Plecs_assign_singleton_component(void) {
    ecs_world_t *world = ecs_init();

    ECS_COMPONENT(world, Position);

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    LINE "$ :- Position{10, 20}\n";
    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    test_assert(ecs_has(world, ecs_id(Position), Position));

    const Position *p = ecs_get(world, ecs_id(Position), Position);
    test_assert(p != NULL);
    test_int(p->x, 10);
    test_int(p->y, 20);

    ecs_fini(world);
}

void Plecs_assign_singleton_tag_w_scope(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    LINE "$ {\n"
    LINE "  - Foo\n"
    LINE "}\n";
    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    test_assert(foo != 0);

    test_assert(ecs_has_id(world, foo, foo));

    ecs_fini(world);
}

void Plecs_assign_singleton_2_tags_w_scope(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    LINE "$ {\n"
    LINE "  - Foo\n"
    LINE "  - Bar\n"
    LINE "}\n";
    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    test_assert(foo != 0);
    ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar");
    test_assert(bar != 0);

    test_assert(ecs_has_id(world, foo, foo));
    test_assert(ecs_has_id(world, bar, bar));

    ecs_fini(world);
}

void Plecs_assign_singleton_component_w_scope(void) {
    ecs_world_t *world = ecs_init();

    ECS_COMPONENT(world, Position);

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    LINE "$ {\n"
    LINE "  - Position{10, 20}\n"
    LINE "}\n";
    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    test_assert(ecs_has(world, ecs_id(Position), Position));

    const Position *p = ecs_get(world, ecs_id(Position), Position);
    test_assert(p != NULL);
    test_int(p->x, 10);
    test_int(p->y, 20);

    ecs_fini(world);
}

void Plecs_assign_singleton_2_components_w_scope(void) {
    ecs_world_t *world = ecs_init();

    ECS_COMPONENT(world, Position);
    ECS_COMPONENT(world, Velocity);

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    ecs_struct(world, {
        .entity = ecs_id(Velocity),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    LINE "$ {\n"
    LINE "  - Position{10, 20}\n"
    LINE "  - Velocity{1, 2}\n"
    LINE "}\n";
    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    const Position *p = ecs_get(world, ecs_id(Position), Position);
    test_assert(p != NULL);
    test_int(p->x, 10);
    test_int(p->y, 20);

    const Velocity *v = ecs_get(world, ecs_id(Velocity), Velocity);
    test_assert(v != NULL);
    test_int(v->x, 1);
    test_int(v->y, 2);

    ecs_fini(world);
}

void Plecs_with_pair_in_scope(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    LINE "Tgt\n"
    LINE "Rel\n"
    LINE "\n"
    LINE "Parent {\n"
    LINE "  with (Rel, Tgt) {\n"
    LINE "    Child\n"
    LINE "  }\n"
    LINE "}\n";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t parent = ecs_lookup_fullpath(world, "Parent");
    ecs_entity_t child = ecs_lookup_fullpath(world, "Parent.Child");
    ecs_entity_t rel = ecs_lookup_fullpath(world, "Rel");
    ecs_entity_t tgt = ecs_lookup_fullpath(world, "Tgt");

    test_assert(!ecs_lookup_fullpath(world, "Parent.Rel"));
    test_assert(!ecs_lookup_fullpath(world, "Parent.Tgt"));

    test_assert(parent != 0);
    test_assert(child != 0);
    test_assert(rel != 0);
    test_assert(tgt != 0);

    test_assert(ecs_has_pair(world, child, rel, tgt));

    ecs_fini(world);
}

void Plecs_assembly_redeclare_prop_as_const(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    LINE "assembly Foo {\n"
    LINE "  prop x : flecs.meta.f32 = 10\n"
    LINE "  prop y : flecs.meta.f32 = 10\n"
    LINE "  const y : flecs.meta.f32 = 20\n"
    LINE "}"
    LINE "ent :- Foo{}\n"
    LINE "\n";

    ecs_log_set_level(-4);
    test_assert(ecs_plecs_from_str(world, NULL, expr) != 0);

    ecs_fini(world);
}

void Plecs_assembly_redeclare_prop_as_prop(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    LINE "assembly Foo {\n"
    LINE "  prop x : flecs.meta.f32 = 10\n"
    LINE "  prop y : flecs.meta.f32 = 10\n"
    LINE "  prop y : flecs.meta.f32 = 20\n"
    LINE "}"
    LINE "ent :- Foo{}\n"
    LINE "\n";

    ecs_log_set_level(-4);
    test_assert(ecs_plecs_from_str(world, NULL, expr) != 0);

    ecs_fini(world);
}

void Plecs_assembly_redeclare_const_as_const(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    LINE "assembly Foo {\n"
    LINE "  prop x : flecs.meta.f32 = 10\n"
    LINE "  const y : flecs.meta.f32 = 10\n"
    LINE "  const y : flecs.meta.f32 = 20\n"
    LINE "}"
    LINE "ent :- Foo{}\n"
    LINE "\n";

    ecs_log_set_level(-4);
    test_assert(ecs_plecs_from_str(world, NULL, expr) != 0);

    ecs_fini(world);
}

void Plecs_add_auto_override(void) {
    ecs_world_t *world = ecs_init();

    ECS_TAG(world, Tag);

    const char *expr =
    LINE "p :- OVERRIDE|Tag"
    LINE "\n";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t p = ecs_lookup_fullpath(world, "p");
    test_assert(p != 0);
    test_assert(ecs_has_id(world, p, ECS_OVERRIDE | Tag));

    ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, p);
    test_assert(e != 0);
    test_assert(ecs_has(world, e, Tag));

    ecs_fini(world);
}

void Plecs_add_auto_override_pair(void) {
    ecs_world_t *world = ecs_init();

    ECS_TAG(world, Rel);
    ECS_TAG(world, Tgt);

    const char *expr =
    LINE "p :- OVERRIDE|(Rel, Tgt)"
    LINE "\n";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t p = ecs_lookup_fullpath(world, "p");
    test_assert(p != 0);
    test_assert(ecs_has_id(world, p, ECS_OVERRIDE | ecs_pair(Rel, Tgt)));

    ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, p);
    test_assert(e != 0);
    test_assert(ecs_has_pair(world, e, Rel, Tgt));

    ecs_fini(world);
}

void Plecs_scope_w_auto_override(void) {
    ecs_world_t *world = ecs_init();

    ECS_TAG(world, Tag);

    const char *expr =
    LINE "p {"
    LINE " - OVERRIDE|Tag"
    LINE "}"
    LINE "\n";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t p = ecs_lookup_fullpath(world, "p");
    test_assert(p != 0);
    test_assert(ecs_has_id(world, p, ECS_OVERRIDE | Tag));

    ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, p);
    test_assert(e != 0);
    test_assert(ecs_has(world, e, Tag));

    ecs_fini(world);
}

void Plecs_scope_w_auto_override_pair(void) {
    ecs_world_t *world = ecs_init();

    ECS_TAG(world, Rel);
    ECS_TAG(world, Tgt);

    const char *expr =
    LINE "p {"
    LINE " - OVERRIDE|(Rel, Tgt)"
    LINE "}"
    LINE "\n";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t p = ecs_lookup_fullpath(world, "p");
    test_assert(p != 0);
    test_assert(ecs_has_id(world, p, ECS_OVERRIDE | ecs_pair(Rel, Tgt)));

    ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, p);
    test_assert(e != 0);
    test_assert(ecs_has_pair(world, e, Rel, Tgt));

    ecs_fini(world);
}

void Plecs_pair_w_rel_var(void) {
    ecs_world_t *world = ecs_init();

    ECS_TAG(world, Rel);
    ECS_TAG(world, Tgt);

    const char *expr =
    LINE "const rel = Rel\n"
    LINE "ent {\n"
    LINE " - ($rel, Tgt)\n"
    LINE "}\n"
    LINE "\n";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t ent = ecs_lookup_fullpath(world, "ent");

    test_assert(ecs_has_pair(world, ent, Rel, Tgt));

    ecs_fini(world);
}

void Plecs_pair_w_tgt_var(void) {
    ecs_world_t *world = ecs_init();

    ECS_TAG(world, Rel);
    ECS_TAG(world, Tgt);

    const char *expr =
    LINE "const tgt = Tgt\n"
    LINE "ent {\n"
    LINE " - (Rel, $tgt)\n"
    LINE "}\n"
    LINE "\n";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t ent = ecs_lookup_fullpath(world, "ent");

    test_assert(ecs_has_pair(world, ent, Rel, Tgt));

    ecs_fini(world);
}

void Plecs_assembly_w_pair_w_this_var(void) {
    ecs_world_t *world = ecs_init();

    ECS_TAG(world, Rel);

    const char *expr =
    LINE "assembly Foo {\n"
    LINE "  prop x : flecs.meta.f32 = 10\n" // dummy prop
    LINE " - (Rel, $this)\n"
    LINE "}\n"
    LINE "ent :- Foo{}\n"
    LINE "\n";

    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t foo = ecs_lookup_fullpath(world, "Foo");
    ecs_entity_t ent = ecs_lookup_fullpath(world, "ent");

    test_assert(ecs_has_id(world, ent, foo));
    test_assert(ecs_has_pair(world, ent, Rel, ent));

    ecs_fini(world);
}

void Plecs_with_value_not_a_component(void) {
    ecs_world_t *world = ecs_init();

    const char *expr =
    LINE "with Foo{10} {\n"
    LINE "  Bar\n"
    LINE "}\n"
    LINE "\n";

    ecs_log_set_level(-4);
    test_assert(ecs_plecs_from_str(world, NULL, expr) != 0);

    ecs_fini(world);
}

void Plecs_component_in_with_scope(void) {
    ecs_world_t *world = ecs_init();

    ECS_COMPONENT(world, Position);

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    LINE "with Position{10, 20} {\n"
    LINE "  - Bar\n"
    LINE "}\n"
    LINE "\n";

    ecs_log_set_level(-4);
    test_assert(ecs_plecs_from_str(world, NULL, expr) != 0);

    ecs_fini(world);
}

void Plecs_component_in_with_scope_nested(void) {
    ecs_world_t *world = ecs_init();

    ECS_COMPONENT(world, Position);
    ECS_COMPONENT(world, Velocity);

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    ecs_struct(world, {
        .entity = ecs_id(Velocity),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    LINE "with Position{10, 20} {\n"
    LINE "  with Velocity{10, 20} {\n"
    LINE "    - Bar\n"
    LINE "  }\n"
    LINE "}\n"
    LINE "\n";

    ecs_log_set_level(-4);
    test_assert(ecs_plecs_from_str(world, NULL, expr) != 0);

    ecs_fini(world);
}

void Plecs_component_in_with_scope_in_scope(void) {
    ecs_world_t *world = ecs_init();

    ECS_COMPONENT(world, Position);

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    LINE "foo {\n"
    LINE "  - Position{10, 20}\n"
    LINE "  with Position{30, 40} {\n"
    LINE "    - Bar\n"
    LINE "  }\n"
    LINE "}\n";

    ecs_log_set_level(-4);
    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t foo = ecs_lookup_fullpath(world, "foo");
    test_assert(foo != 0);
    ecs_entity_t bar = ecs_lookup_fullpath(world, "Bar");
    test_assert(bar != 0);

    test_assert(ecs_has(world, foo, Position));
    test_assert(ecs_has_id(world, foo, bar));

    const Position *p = ecs_get(world, foo, Position);
    test_assert(p != NULL);
    test_int(p->x, 10);
    test_int(p->y, 20);

    ecs_fini(world);
}

void Plecs_assign_after_with_in_scope(void) {
    ecs_world_t *world = ecs_init();

    ECS_COMPONENT(world, Position);
    ECS_COMPONENT(world, Velocity);

    ecs_struct(world, {
        .entity = ecs_id(Position),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });
    ecs_struct(world, {
        .entity = ecs_id(Velocity),
        .members = {
            {"x", ecs_id(ecs_f32_t)},
            {"y", ecs_id(ecs_f32_t)}
        }
    });

    const char *expr =
    LINE "foo {\n"
    LINE "  - Position{10, 20}\n"
    LINE "  with Position{30, 40} {\n"
    LINE "  }\n"
    LINE "  - Velocity{1, 2}\n"
    LINE "}\n";

    ecs_log_set_level(-4);
    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t foo = ecs_lookup_fullpath(world, "foo");
    test_assert(foo != 0);

    test_assert(ecs_has(world, foo, Position));
    test_assert(ecs_has(world, foo, Velocity));

    const Position *p = ecs_get(world, foo, Position);
    test_assert(p != NULL);
    test_int(p->x, 10);
    test_int(p->y, 20);

    const Velocity *v = ecs_get(world, foo, Velocity);
    test_assert(v != NULL);
    test_int(v->x, 1);
    test_int(v->y, 2);

    ecs_fini(world);
}

void Plecs_array_component(void) {
    ecs_world_t *world = ecs_init();

    ECS_COMPONENT(world, Position);

    ecs_array(world, {
        .entity = ecs_id(Position),
        .type = ecs_id(ecs_f32_t),
        .count = 2
    });

    const char *expr =
    LINE "foo :- Position[10, 20]\n";
    test_assert(ecs_plecs_from_str(world, NULL, expr) == 0);

    ecs_entity_t foo = ecs_lookup_fullpath(world, "foo");
    test_assert(foo != 0);

    test_assert(ecs_has(world, foo, Position));

    const Position *p = ecs_get(world, foo, Position);
    test_assert(p != NULL);
    test_int(p->x, 10);
    test_int(p->y, 20);

    ecs_fini(world);
}
