Skip to content

Commit

Permalink
Expose C/C++ functions to parse component id expressions (#1365)
Browse files Browse the repository at this point in the history
Summary:
Pull Request resolved: #1365

This diff adds an `ecs_id_from_str` function that parses a (component) id expression into an id, and adds a `flecs::id::id(flecs::world_t*, const char*)` constructor.

Reviewed By: Jason-M-Fugate

Differential Revision: D63060151
  • Loading branch information
SanderMertens committed Sep 20, 2024
1 parent 47f081f commit c51cf55
Show file tree
Hide file tree
Showing 13 changed files with 440 additions and 20 deletions.
41 changes: 35 additions & 6 deletions distr/flecs.c
Original file line number Diff line number Diff line change
Expand Up @@ -6604,6 +6604,15 @@ int flecs_traverse_from_expr(
if (!id) {
break;
}

if (!ecs_id_is_valid(world, id)) {
char *idstr = ecs_id_str(world, id);
ecs_parser_error(name, expr, (ptr - expr),
"id %s is invalid for add expression", idstr);
ecs_os_free(idstr);
goto error;
}

ecs_vec_append_t(&world->allocator, ids, ecs_id_t)[0] = id;
}

Expand Down Expand Up @@ -10974,6 +10983,9 @@ ecs_entity_t ecs_new_from_path_w_sep(
*/


#ifdef FLECS_SCRIPT
#endif

bool ecs_id_match(
ecs_id_t id,
ecs_id_t pattern)
Expand Down Expand Up @@ -11100,6 +11112,29 @@ ecs_flags32_t ecs_id_get_flags(
}
}

ecs_id_t ecs_id_from_str(
const ecs_world_t *world,
const char *expr)
{
#ifdef FLECS_SCRIPT
ecs_id_t result;

/* Temporarily disable parser logging */
int prev_level = ecs_log_set_level(-3);
if (!flecs_id_parse(world, NULL, expr, &result)) {
/* Invalid expression */
ecs_log_set_level(prev_level);
return 0;
}
ecs_log_set_level(prev_level);
return result;
#else
(void)world;
(void)expr;
ecs_abort(ECS_UNSUPPORTED, "ecs_id_from_str requires FLECS_SCRIPT addon");
#endif
}

/**
* @file iter.c
* @brief Iterator API.
Expand Down Expand Up @@ -57518,12 +57553,6 @@ const char* flecs_id_parse(
return NULL;
}

if (!ecs_id_is_valid(world, term.id)) {
ecs_parser_error(name, expr, (result - expr),
"invalid term for add expression");
return NULL;
}

if (term.oper != EcsAnd) {
ecs_parser_error(name, expr, (result - expr),
"invalid operator for add expression");
Expand Down
20 changes: 18 additions & 2 deletions distr/flecs.h
Original file line number Diff line number Diff line change
Expand Up @@ -7517,7 +7517,7 @@ FLECS_API
const char* ecs_id_flag_str(
ecs_id_t id_flags);

/** Convert id to string.
/** Convert (component) id to string.
* This operation interprets the structure of an id and converts it to a string.
*
* @param world The world.
Expand All @@ -7529,7 +7529,7 @@ char* ecs_id_str(
const ecs_world_t *world,
ecs_id_t id);

/** Write id string to buffer.
/** Write (component) id string to buffer.
* Same as ecs_id_str() but writes result to ecs_strbuf_t.
*
* @param world The world.
Expand All @@ -7542,6 +7542,18 @@ void ecs_id_str_buf(
ecs_id_t id,
ecs_strbuf_t *buf);

/** Convert string to a (component) id.
* This operation is the reverse of ecs_id_str(). The FLECS_SCRIPT addon
* is required for this operation to work.
*
* @param world The world.
* @param expr The string to convert to an id.
*/
FLECS_API
ecs_id_t ecs_id_from_str(
const ecs_world_t *world,
const char *expr);

/** @} */

/**
Expand Down Expand Up @@ -17910,6 +17922,10 @@ struct id {
: world_(world)
, id_(ecs_pair(first, second)) { }

explicit id(flecs::world_t *world, const char *expr)
: world_(world)
, id_(ecs_id_from_str(world, expr)) { }

explicit id(flecs::id_t first, flecs::id_t second)
: world_(nullptr)
, id_(ecs_pair(first, second)) { }
Expand Down
16 changes: 14 additions & 2 deletions include/flecs.h
Original file line number Diff line number Diff line change
Expand Up @@ -4310,7 +4310,7 @@ FLECS_API
const char* ecs_id_flag_str(
ecs_id_t id_flags);

/** Convert id to string.
/** Convert (component) id to string.
* This operation interprets the structure of an id and converts it to a string.
*
* @param world The world.
Expand All @@ -4322,7 +4322,7 @@ char* ecs_id_str(
const ecs_world_t *world,
ecs_id_t id);

/** Write id string to buffer.
/** Write (component) id string to buffer.
* Same as ecs_id_str() but writes result to ecs_strbuf_t.
*
* @param world The world.
Expand All @@ -4335,6 +4335,18 @@ void ecs_id_str_buf(
ecs_id_t id,
ecs_strbuf_t *buf);

/** Convert string to a (component) id.
* This operation is the reverse of ecs_id_str(). The FLECS_SCRIPT addon
* is required for this operation to work.
*
* @param world The world.
* @param expr The string to convert to an id.
*/
FLECS_API
ecs_id_t ecs_id_from_str(
const ecs_world_t *world,
const char *expr);

/** @} */

/**
Expand Down
4 changes: 4 additions & 0 deletions include/flecs/addons/cpp/mixins/id/decl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ struct id {
: world_(world)
, id_(ecs_pair(first, second)) { }

explicit id(flecs::world_t *world, const char *expr)
: world_(world)
, id_(ecs_id_from_str(world, expr)) { }

explicit id(flecs::id_t first, flecs::id_t second)
: world_(nullptr)
, id_(ecs_pair(first, second)) { }
Expand Down
6 changes: 0 additions & 6 deletions src/addons/script/query_parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -666,12 +666,6 @@ const char* flecs_id_parse(
return NULL;
}

if (!ecs_id_is_valid(world, term.id)) {
ecs_parser_error(name, expr, (result - expr),
"invalid term for add expression");
return NULL;
}

if (term.oper != EcsAnd) {
ecs_parser_error(name, expr, (result - expr),
"invalid operator for add expression");
Expand Down
9 changes: 9 additions & 0 deletions src/entity.c
Original file line number Diff line number Diff line change
Expand Up @@ -1616,6 +1616,15 @@ int flecs_traverse_from_expr(
if (!id) {
break;
}

if (!ecs_id_is_valid(world, id)) {
char *idstr = ecs_id_str(world, id);
ecs_parser_error(name, expr, (ptr - expr),
"id %s is invalid for add expression", idstr);
ecs_os_free(idstr);
goto error;
}

ecs_vec_append_t(&world->allocator, ids, ecs_id_t)[0] = id;
}

Expand Down
27 changes: 27 additions & 0 deletions src/id.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@

#include "private_api.h"

#ifdef FLECS_SCRIPT
#include "addons/script/script.h"
#endif

bool ecs_id_match(
ecs_id_t id,
ecs_id_t pattern)
Expand Down Expand Up @@ -130,3 +134,26 @@ ecs_flags32_t ecs_id_get_flags(
return 0;
}
}

ecs_id_t ecs_id_from_str(
const ecs_world_t *world,
const char *expr)
{
#ifdef FLECS_SCRIPT
ecs_id_t result;

/* Temporarily disable parser logging */
int prev_level = ecs_log_set_level(-3);
if (!flecs_id_parse(world, NULL, expr, &result)) {
/* Invalid expression */
ecs_log_set_level(prev_level);
return 0;
}
ecs_log_set_level(prev_level);
return result;
#else
(void)world;
(void)expr;
ecs_abort(ECS_UNSUPPORTED, "ecs_id_from_str requires FLECS_SCRIPT addon");
#endif
}
12 changes: 11 additions & 1 deletion test/core/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,17 @@
"pair_id_toggle_is_tag",
"make_pair",
"make_pair_of_pair",
"make_pair_of_pair_tgt"
"make_pair_of_pair_tgt",
"0_entity",
"entity_from_str",
"unresolved_entity_from_str",
"scoped_entity_from_str",
"template_entity_from_str",
"pair_from_str",
"unresolved_pair_from_str",
"wildcard_pair_from_str",
"any_pair_from_str",
"invalid_pair"
]
}, {
"id": "Entity",
Expand Down
115 changes: 115 additions & 0 deletions test/core/src/Id.c
Original file line number Diff line number Diff line change
Expand Up @@ -267,3 +267,118 @@ void Id_make_pair_of_pair_tgt(void) {
test_expect_abort();
ecs_make_pair(r, id);
}

void Id_0_entity(void) {
ecs_world_t *world = ecs_mini();

ecs_id_t id = ecs_id_from_str(world, "#0");
test_assert(id == 0);

ecs_fini(world);
}

void Id_entity_from_str(void) {
ecs_world_t *world = ecs_mini();

ECS_TAG(world, Foo);

ecs_id_t id = ecs_id_from_str(world, "Foo");
test_assert(id != 0);
test_assert(id == Foo);

ecs_fini(world);
}

void Id_unresolved_entity_from_str(void) {
ecs_world_t *world = ecs_mini();

ecs_id_t id = ecs_id_from_str(world, "Foo");
test_assert(id == 0);

ecs_fini(world);
}

void Id_scoped_entity_from_str(void) {
ecs_world_t *world = ecs_mini();

ecs_entity_t parent = ecs_entity(world, {.name = "Parent"});
ecs_entity_t child = ecs_entity(world, {.name = "Child", .parent = parent});

ecs_id_t id = ecs_id_from_str(world, "Parent.Child");
test_assert(id != 0);
test_assert(id == child);

ecs_fini(world);
}

void Id_template_entity_from_str(void) {
ecs_world_t *world = ecs_mini();

ecs_entity_t foo = ecs_entity(world, {.name = "Foo<Bar>"});

ecs_id_t id = ecs_id_from_str(world, "Foo<Bar>");
test_assert(id != 0);
test_assert(id == foo);

ecs_fini(world);
}

void Id_pair_from_str(void) {
ecs_world_t *world = ecs_mini();

ECS_TAG(world, Rel);
ECS_TAG(world, Tgt);

ecs_id_t id = ecs_id_from_str(world, "(Rel, Tgt)");
test_assert(id != 0);
test_assert(id == ecs_pair(Rel, Tgt));

ecs_fini(world);
}

void Id_unresolved_pair_from_str(void) {
ecs_world_t *world = ecs_mini();

ECS_TAG(world, Rel);

ecs_id_t id = ecs_id_from_str(world, "(Rel, Tgt)");
test_assert(id == 0);

ecs_fini(world);
}

void Id_wildcard_pair_from_str(void) {
ecs_world_t *world = ecs_mini();

ECS_TAG(world, Rel);

ecs_id_t id = ecs_id_from_str(world, "(Rel, *)");
test_assert(id != 0);
test_assert(id == ecs_pair(Rel, EcsWildcard));

ecs_fini(world);
}

void Id_any_pair_from_str(void) {
ecs_world_t *world = ecs_mini();

ECS_TAG(world, Rel);

ecs_id_t id = ecs_id_from_str(world, "(Rel, _)");
test_assert(id != 0);
test_assert(id == ecs_pair(Rel, EcsAny));

ecs_fini(world);
}

void Id_invalid_pair(void) {
ecs_world_t *world = ecs_mini();

ECS_TAG(world, Rel);
ECS_TAG(world, Tgt);

ecs_id_t id = ecs_id_from_str(world, "(Rel, Tgt");
test_assert(id == 0);

ecs_fini(world);
}
Loading

0 comments on commit c51cf55

Please sign in to comment.