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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions distr/flecs.c
Original file line number Diff line number Diff line change
Expand Up @@ -50478,6 +50478,8 @@ typedef struct ecs_script_type_visitor_t {
ecs_script_entity_t *reopened;
bool for_static;
bool for_entity;
bool parent_missing; /* v->parent is not the world entity of the current
* entity scope (it does not exist (yet)) */
} ecs_script_type_visitor_t;

int flecs_script_type_scope(
Expand Down Expand Up @@ -101230,6 +101232,43 @@ static bool flecs_script_type_entity_name_ref(
return true;
}

/* Set v->parent to the world entity of an entity scope before typing it, like
* evaluation does, so that names are looked up from the same scope chain as
* during evaluation. This resolves entities that exist in the world, but are
* not symbols of this script (such as entities created by another script).
*
* If the entity does not exist yet, v->parent is kept: evaluation creates it
* without children, so a recursive lookup from it is the same as a lookup from
* its parent. Its descendants do not exist either, so lookups for nested
* scopes stay at the last existing parent. */
static void flecs_script_type_push_parent(
ecs_script_type_visitor_t *t,
ecs_script_entity_t *node)
{
if (t->parent_missing) {
return;
}

ecs_world_t *world = t->v->world;
ecs_entity_t e = flecs_script_type_node_entity(t, node);
if (e && !ecs_is_alive(world, e)) {
e = 0;
}

if (!e && node->name && !node->name_expr &&
ecs_os_strcmp(node->name, "_"))
{
e = ecs_lookup_path_w_sep(
world, t->v->parent, node->name, ".", NULL, false);
}

if (e) {
t->v->parent = e;
} else {
t->parent_missing = true;
}
}

static int flecs_script_type_entity(
ecs_script_type_visitor_t *t,
ecs_script_entity_t *node,
Expand Down Expand Up @@ -101350,8 +101389,13 @@ static int flecs_script_type_entity(

int32_t prev_unresolved = ecs_vec_count(
&t->v->base.script->unresolved_refs);
ecs_entity_t old_parent = t->v->parent;
bool old_parent_missing = t->parent_missing;
flecs_script_type_push_parent(t, node);
int result = flecs_script_type_scope(
t, node->scope, child_table, true, true);
t->v->parent = old_parent;
t->parent_missing = old_parent_missing;
t->v->entity = old_entity;
t->v->is_with_scope = old_with;
t->for_static = old_for_static;
Expand Down
42 changes: 42 additions & 0 deletions src/addons/script/eval/visit_type.c
Original file line number Diff line number Diff line change
Expand Up @@ -1964,6 +1964,43 @@ static bool flecs_script_type_entity_name_ref(
return true;
}

/* Set v->parent to the world entity of an entity scope before typing it, like
* evaluation does, so that names are looked up from the same scope chain as
* during evaluation. This resolves entities that exist in the world, but are
* not symbols of this script (such as entities created by another script).
*
* If the entity does not exist yet, v->parent is kept: evaluation creates it
* without children, so a recursive lookup from it is the same as a lookup from
* its parent. Its descendants do not exist either, so lookups for nested
* scopes stay at the last existing parent. */
static void flecs_script_type_push_parent(
ecs_script_type_visitor_t *t,
ecs_script_entity_t *node)
{
if (t->parent_missing) {
return;
}

ecs_world_t *world = t->v->world;
ecs_entity_t e = flecs_script_type_node_entity(t, node);
if (e && !ecs_is_alive(world, e)) {
e = 0;
}

if (!e && node->name && !node->name_expr &&
ecs_os_strcmp(node->name, "_"))
{
e = ecs_lookup_path_w_sep(
world, t->v->parent, node->name, ".", NULL, false);
}

if (e) {
t->v->parent = e;
} else {
t->parent_missing = true;
}
}

static int flecs_script_type_entity(
ecs_script_type_visitor_t *t,
ecs_script_entity_t *node,
Expand Down Expand Up @@ -2084,8 +2121,13 @@ static int flecs_script_type_entity(

int32_t prev_unresolved = ecs_vec_count(
&t->v->base.script->unresolved_refs);
ecs_entity_t old_parent = t->v->parent;
bool old_parent_missing = t->parent_missing;
flecs_script_type_push_parent(t, node);
int result = flecs_script_type_scope(
t, node->scope, child_table, true, true);
t->v->parent = old_parent;
t->parent_missing = old_parent_missing;
t->v->entity = old_entity;
t->v->is_with_scope = old_with;
t->for_static = old_for_static;
Expand Down
2 changes: 2 additions & 0 deletions src/addons/script/eval/visit_type.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ typedef struct ecs_script_type_visitor_t {
ecs_script_entity_t *reopened;
bool for_static;
bool for_entity;
bool parent_missing; /* v->parent is not the world entity of the current
* entity scope (it does not exist (yet)) */
} ecs_script_type_visitor_t;

int flecs_script_type_scope(
Expand Down
6 changes: 5 additions & 1 deletion test/script/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -834,7 +834,11 @@
"named_child_in_for_loop_warns",
"interpolated_name_in_for_loop_no_warn",
"child_of_interpolated_name_in_for_loop_no_warn",
"named_entity_w_tag_in_for_loop_no_warn"
"named_entity_w_tag_in_for_loop_no_warn",
"scoped_prefab_inherit_from_prev_script",
"scoped_entity_ref_from_prev_script",
"scoped_component_from_prev_script",
"nested_scope_component_from_prev_script"
]
}, {
"id": "Collection",
Expand Down
158 changes: 142 additions & 16 deletions test/script/src/Eval.c
Original file line number Diff line number Diff line change
Expand Up @@ -9983,8 +9983,6 @@ void Eval_const_w_component_expr_in_module(void) {
}

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

ecs_world_t *world = ecs_init();

ecs_entity_t parent = ecs_entity(world, { .name = "parent" });
Expand All @@ -10009,13 +10007,15 @@ void Eval_const_w_component_in_scope_expr_in_scope(void) {
LINE " }"
LINE "}";

ecs_script_eval_result_t result = {0};
test_assert(ecs_script_run_w_desc(world, NULL, expr, &ir_desc, &result) != 0);
test_assert(result.error != NULL);
test_assert(strstr(result.error, "unresolved reference 'Position'") != NULL);
ecs_os_free(result.error);
test_assert(ecs_script_run_w_desc(world, NULL, expr, &ir_desc, NULL) == 0);

test_assert(ecs_lookup(world, "parent.foo") == 0);
ecs_entity_t foo = ecs_lookup(world, "parent.foo");
test_assert(foo != 0);

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);
}
Expand Down Expand Up @@ -10059,8 +10059,6 @@ void Eval_const_w_component_in_scope_expr_in_module(void) {
}

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

ecs_world_t *world = ecs_init();

ecs_entity_t parent = ecs_entity(world, { .name = "parent" });
Expand All @@ -10085,13 +10083,15 @@ void Eval_const_w_component_and_entity_in_scope_expr_in_scope(void) {
LINE " }"
LINE "}";

ecs_script_eval_result_t result = {0};
test_assert(ecs_script_run_w_desc(world, NULL, expr, &ir_desc, &result) != 0);
test_assert(result.error != NULL);
test_assert(strstr(result.error, "unresolved reference") != NULL);
ecs_os_free(result.error);
test_assert(ecs_script_run_w_desc(world, NULL, expr, &ir_desc, NULL) == 0);

test_assert(ecs_lookup(world, "parent.foo") == 0);
ecs_entity_t foo = ecs_lookup(world, "parent.foo");
test_assert(foo != 0);

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);
}
Expand Down Expand Up @@ -26335,3 +26335,129 @@ void Eval_named_entity_w_tag_in_for_loop_no_warn(void) {
ecs_os_set_api_defaults();
ecs_log_set_level(-1);
}

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

ECS_TAG(world, Foo);

const char *pieces =
HEAD "structure {"
LINE " prefabs {"
LINE " Prefab steel { Foo }"
LINE " }"
LINE "}";

const char *kinds =
HEAD "structure {"
LINE " prefabs {"
LINE " Prefab steel_a992 : steel {}"
LINE " }"
LINE "}";

test_assert(ecs_script_run_w_desc(world, "pieces", pieces, &ir_desc, NULL) == 0);
test_assert(ecs_script_run_w_desc(world, "kinds", kinds, &ir_desc, NULL) == 0);

ecs_entity_t steel = ecs_lookup(world, "structure.prefabs.steel");
ecs_entity_t a992 = ecs_lookup(world, "structure.prefabs.steel_a992");
test_assert(steel != 0);
test_assert(a992 != 0);
test_assert(ecs_has_pair(world, a992, EcsIsA, steel));
test_assert(ecs_has_id(world, a992, EcsPrefab));
test_assert(ecs_has(world, a992, Foo));

ecs_fini(world);
}

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

ECS_TAG(world, MadeOf);

const char *pieces =
HEAD "structure {"
LINE " materials {"
LINE " steel {}"
LINE " }"
LINE "}";

const char *kinds =
HEAD "structure {"
LINE " materials {"
LINE " beam { (MadeOf, steel) }"
LINE " }"
LINE "}";

test_assert(ecs_script_run_w_desc(world, "pieces", pieces, &ir_desc, NULL) == 0);
test_assert(ecs_script_run_w_desc(world, "kinds", kinds, &ir_desc, NULL) == 0);

ecs_entity_t steel = ecs_lookup(world, "structure.materials.steel");
ecs_entity_t beam = ecs_lookup(world, "structure.materials.beam");
test_assert(steel != 0);
test_assert(beam != 0);
test_assert(ecs_has_pair(world, beam, MadeOf, steel));

ecs_fini(world);
}

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

const char *pieces =
HEAD "structure {"
LINE " components {"
LINE " struct Profile(width: f32, depth: f32)"
LINE " }"
LINE "}";

const char *kinds =
HEAD "structure {"
LINE " components {"
LINE " mullion { Profile: {width: 65, depth: 200} }"
LINE " }"
LINE "}";

test_assert(ecs_script_run_w_desc(world, "pieces", pieces, &ir_desc, NULL) == 0);
test_assert(ecs_script_run_w_desc(world, "kinds", kinds, &ir_desc, NULL) == 0);

ecs_entity_t profile = ecs_lookup(world, "structure.components.Profile");
ecs_entity_t mullion = ecs_lookup(world, "structure.components.mullion");
test_assert(profile != 0);
test_assert(mullion != 0);
const float *p = ecs_get_id(world, mullion, profile);
test_assert(p != NULL);
test_int(p[0], 65);
test_int(p[1], 200);

ecs_fini(world);
}

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

const char *pieces =
HEAD "structure {"
LINE " struct Profile(width: f32, depth: f32)"
LINE "}";

const char *kinds =
HEAD "structure {"
LINE " prefabs {"
LINE " Prefab mullion { Profile: {width: 65, depth: 200} }"
LINE " }"
LINE "}";

test_assert(ecs_script_run_w_desc(world, "pieces", pieces, &ir_desc, NULL) == 0);
test_assert(ecs_script_run_w_desc(world, "kinds", kinds, &ir_desc, NULL) == 0);

ecs_entity_t profile = ecs_lookup(world, "structure.Profile");
ecs_entity_t mullion = ecs_lookup(world, "structure.prefabs.mullion");
test_assert(profile != 0);
test_assert(mullion != 0);
const float *p = ecs_get_id(world, mullion, profile);
test_assert(p != NULL);
test_int(p[0], 65);
test_int(p[1], 200);

ecs_fini(world);
}
22 changes: 21 additions & 1 deletion test/script/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -828,6 +828,10 @@ void Eval_named_child_in_for_loop_warns(void);
void Eval_interpolated_name_in_for_loop_no_warn(void);
void Eval_child_of_interpolated_name_in_for_loop_no_warn(void);
void Eval_named_entity_w_tag_in_for_loop_no_warn(void);
void Eval_scoped_prefab_inherit_from_prev_script(void);
void Eval_scoped_entity_ref_from_prev_script(void);
void Eval_scoped_component_from_prev_script(void);
void Eval_nested_scope_component_from_prev_script(void);

// Testsuite 'Collection'
void Collection_setup(void);
Expand Down Expand Up @@ -7476,6 +7480,22 @@ bake_test_case Eval_testcases[] = {
{
"named_entity_w_tag_in_for_loop_no_warn",
Eval_named_entity_w_tag_in_for_loop_no_warn
},
{
"scoped_prefab_inherit_from_prev_script",
Eval_scoped_prefab_inherit_from_prev_script
},
{
"scoped_entity_ref_from_prev_script",
Eval_scoped_entity_ref_from_prev_script
},
{
"scoped_component_from_prev_script",
Eval_scoped_component_from_prev_script
},
{
"nested_scope_component_from_prev_script",
Eval_nested_scope_component_from_prev_script
}
};

Expand Down Expand Up @@ -20841,7 +20861,7 @@ static bake_test_suite suites[] = {
"Eval",
Eval_setup,
NULL,
818,
822,
Eval_testcases,
1,
Eval_params
Expand Down
Loading