Skip to content

Commit

Permalink
Implement ability to define global script variables
Browse files Browse the repository at this point in the history
  • Loading branch information
SanderMertens committed Dec 12, 2024
1 parent 61c5088 commit ad1d496
Show file tree
Hide file tree
Showing 20 changed files with 668 additions and 76 deletions.
295 changes: 259 additions & 36 deletions distr/flecs.c

Large diffs are not rendered by default.

45 changes: 44 additions & 1 deletion distr/flecs.h
Original file line number Diff line number Diff line change
Expand Up @@ -14311,7 +14311,10 @@ FLECS_API
extern ECS_COMPONENT_DECLARE(EcsScript);

FLECS_API
extern ECS_DECLARE(EcsTemplate);
extern ECS_DECLARE(EcsScriptTemplate);

FLECS_API
extern ECS_COMPONENT_DECLARE(EcsScriptConstVar);

FLECS_API
extern ECS_COMPONENT_DECLARE(EcsScriptFunction);
Expand Down Expand Up @@ -14380,6 +14383,14 @@ typedef struct ecs_script_parameter_t {
ecs_entity_t type;
} ecs_script_parameter_t;

/** Const component.
* This component describes a const variable that can be used from scripts.
*/
typedef struct EcsScriptConstVar {
ecs_value_t value;
const ecs_type_info_t *type_info;
} EcsScriptConstVar;

/** Function component.
* This component describes a function that can be called from a script.
*/
Expand Down Expand Up @@ -14846,6 +14857,38 @@ char* ecs_script_string_interpolate(
const ecs_script_vars_t *vars);


/* Global const variables */

/** Used with ecs_const_var_init */
typedef struct ecs_const_var_desc_t {
/* Variable name. */
const char *name;

/* Variable parent (namespace). */
ecs_entity_t parent;

/* Variable type. */
ecs_entity_t type;

/* Pointer to value of variable. The value will be copied to an internal
* storage and does not need to be kept alive. */
void *value;
} ecs_const_var_desc_t;

/** Create a const variable that can be accessed by scripts.
*
* @param world The world.
* @param desc Const var parameters.
* @return The const var, or 0 if failed.
*/
FLECS_API
ecs_entity_t ecs_const_var_init(
ecs_world_t *world,
ecs_const_var_desc_t *desc);

#define ecs_const_var(world, ...)\
ecs_const_var_init(world, &(ecs_const_var_desc_t)__VA_ARGS__)

/* Functions */

/** Used with ecs_function_init and ecs_method_init */
Expand Down
45 changes: 44 additions & 1 deletion include/flecs/addons/script.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,10 @@ FLECS_API
extern ECS_COMPONENT_DECLARE(EcsScript);

FLECS_API
extern ECS_DECLARE(EcsTemplate);
extern ECS_DECLARE(EcsScriptTemplate);

FLECS_API
extern ECS_COMPONENT_DECLARE(EcsScriptConstVar);

FLECS_API
extern ECS_COMPONENT_DECLARE(EcsScriptFunction);
Expand Down Expand Up @@ -106,6 +109,14 @@ typedef struct ecs_script_parameter_t {
ecs_entity_t type;
} ecs_script_parameter_t;

/** Const component.
* This component describes a const variable that can be used from scripts.
*/
typedef struct EcsScriptConstVar {
ecs_value_t value;
const ecs_type_info_t *type_info;
} EcsScriptConstVar;

/** Function component.
* This component describes a function that can be called from a script.
*/
Expand Down Expand Up @@ -572,6 +583,38 @@ char* ecs_script_string_interpolate(
const ecs_script_vars_t *vars);


/* Global const variables */

/** Used with ecs_const_var_init */
typedef struct ecs_const_var_desc_t {
/* Variable name. */
const char *name;

/* Variable parent (namespace). */
ecs_entity_t parent;

/* Variable type. */
ecs_entity_t type;

/* Pointer to value of variable. The value will be copied to an internal
* storage and does not need to be kept alive. */
void *value;
} ecs_const_var_desc_t;

/** Create a const variable that can be accessed by scripts.
*
* @param world The world.
* @param desc Const var parameters.
* @return The const var, or 0 if failed.
*/
FLECS_API
ecs_entity_t ecs_const_var_init(
ecs_world_t *world,
ecs_const_var_desc_t *desc);

#define ecs_const_var(world, ...)\
ecs_const_var_init(world, &(ecs_const_var_desc_t)__VA_ARGS__)

/* Functions */

/** Used with ecs_function_init and ecs_method_init */
Expand Down
2 changes: 2 additions & 0 deletions src/addons/script/expr/ast.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ typedef enum ecs_expr_node_kind_t {
EcsExprBinary,
EcsExprIdentifier,
EcsExprVariable,
EcsExprGlobalVariable,
EcsExprFunction,
EcsExprMethod,
EcsExprMember,
Expand Down Expand Up @@ -60,6 +61,7 @@ typedef struct ecs_expr_identifier_t {
typedef struct ecs_expr_variable_t {
ecs_expr_node_t node;
const char *name;
ecs_value_t global_value; /* Only set for global variables */
} ecs_expr_variable_t;

typedef struct ecs_expr_unary_t {
Expand Down
13 changes: 12 additions & 1 deletion src/addons/script/expr/parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,18 @@ const char* flecs_script_parse_lhs(
} else if (!ecs_os_strcmp(expr, "false")) {
*out = (ecs_expr_node_t*)flecs_expr_bool(parser, false);
} else {
*out = (ecs_expr_node_t*)flecs_expr_identifier(parser, expr);
char *last_elem = strrchr(expr, '.');
if (last_elem && last_elem[1] == '$') {
/* Scoped global variable */
ecs_expr_variable_t *v = flecs_expr_variable(parser, expr);
memmove(&last_elem[1], &last_elem[2],
ecs_os_strlen(&last_elem[2]) + 1);
v->node.kind = EcsExprGlobalVariable;
*out = (ecs_expr_node_t*)v;
} else {
/* Entity identifier */
*out = (ecs_expr_node_t*)flecs_expr_identifier(parser, expr);
}
}
break;
}
Expand Down
4 changes: 4 additions & 0 deletions src/addons/script/expr/visit.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,8 @@ void flecs_expr_visit_free(
ecs_script_t *script,
ecs_expr_node_t *node);

ecs_script_var_t flecs_expr_find_var(
ecs_script_t *script,
const char *name);

#endif
24 changes: 24 additions & 0 deletions src/addons/script/expr/visit_eval.c
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,23 @@ int flecs_expr_variable_visit_eval(
return -1;
}

static
int flecs_expr_global_variable_visit_eval(
ecs_script_eval_ctx_t *ctx,
ecs_expr_variable_t *node,
ecs_expr_value_t *out)
{
ecs_assert(ctx->desc != NULL, ECS_INVALID_OPERATION,
"variables available at parse time are not provided");

ecs_assert(node->global_value.type == node->node.type,
ECS_INTERNAL_ERROR, NULL);
out->value = node->global_value;
out->owned = false;

return 0;
}

static
int flecs_expr_cast_visit_eval(
ecs_script_eval_ctx_t *ctx,
Expand Down Expand Up @@ -558,6 +575,13 @@ int flecs_expr_visit_eval_priv(
goto error;
}
break;
case EcsExprGlobalVariable:
if (flecs_expr_global_variable_visit_eval(
ctx, (ecs_expr_variable_t*)node, out))
{
goto error;
}
break;
case EcsExprFunction:
if (flecs_expr_function_visit_eval(
ctx, (ecs_expr_function_t*)node, out))
Expand Down
28 changes: 28 additions & 0 deletions src/addons/script/expr/visit_fold.c
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,29 @@ int flecs_expr_variable_visit_fold(
return 0;
}

static
int flecs_expr_global_variable_visit_fold(
ecs_script_t *script,
ecs_expr_node_t **node_ptr,
const ecs_expr_eval_desc_t *desc)
{
(void)desc;

ecs_expr_variable_t *node = (ecs_expr_variable_t*)*node_ptr;
ecs_entity_t type = node->node.type;

/* Global const variables are always const, so we can always fold */

ecs_expr_value_node_t *result = flecs_expr_value_from(
script, (ecs_expr_node_t*)node, type);
void *value = ecs_value_new(script->world, type);
ecs_value_copy(script->world, type, value, node->global_value.ptr);
result->ptr = value;
flecs_visit_fold_replace(script, node_ptr, (ecs_expr_node_t*)result);

return 0;
}

static
int flecs_expr_arguments_visit_fold(
ecs_script_t *script,
Expand Down Expand Up @@ -447,6 +470,11 @@ int flecs_expr_visit_fold(
goto error;
}
break;
case EcsExprGlobalVariable:
if (flecs_expr_global_variable_visit_fold(script, node_ptr, desc)) {
goto error;
}
break;
case EcsExprFunction:
case EcsExprMethod:
if (flecs_expr_function_visit_fold(script, node_ptr, desc)) {
Expand Down
1 change: 1 addition & 0 deletions src/addons/script/expr/visit_free.c
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ void flecs_expr_visit_free(
flecs_free_t(a, ecs_expr_identifier_t, node);
break;
case EcsExprVariable:
case EcsExprGlobalVariable:
flecs_free_t(a, ecs_expr_variable_t, node);
break;
case EcsExprFunction:
Expand Down
1 change: 1 addition & 0 deletions src/addons/script/expr/visit_to_str.c
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,7 @@ int flecs_expr_node_to_str(
}
break;
case EcsExprVariable:
case EcsExprGlobalVariable:
if (flecs_expr_variable_to_str(v,
(const ecs_expr_variable_t*)node))
{
Expand Down
70 changes: 64 additions & 6 deletions src/addons/script/expr/visit_type.c
Original file line number Diff line number Diff line change
Expand Up @@ -733,6 +733,39 @@ int flecs_expr_identifier_visit_type(
return -1;
}

static
int flecs_expr_global_variable_resolve(
ecs_script_t *script,
ecs_expr_variable_t *node,
const ecs_expr_eval_desc_t *desc)
{
ecs_world_t *world = script->world;
ecs_entity_t global = desc->lookup_action(
world, node->name, desc->lookup_ctx);
if (!global) {
flecs_expr_visit_error(script, node, "unresolved variable '%s'",
node->name);
goto error;
}

const EcsScriptConstVar *v = ecs_get(world, global, EcsScriptConstVar);
if (!v) {
char *str = ecs_get_path(world, global);
flecs_expr_visit_error(script, node,
"entity '%s' is not a variable", node->name);
ecs_os_free(str);
goto error;
}

node->node.kind = EcsExprGlobalVariable;
node->node.type = v->value.type;
node->global_value = v->value;

return 0;
error:
return -1;
}

static
int flecs_expr_variable_visit_type(
ecs_script_t *script,
Expand All @@ -742,15 +775,33 @@ int flecs_expr_variable_visit_type(
{
ecs_script_var_t *var = ecs_script_vars_lookup(
desc->vars, node->name);
if (!var) {
flecs_expr_visit_error(script, node, "unresolved variable '%s'",
node->name);
goto error;
if (var) {
node->node.type = var->value.type;
} else {
if (flecs_expr_global_variable_resolve(script, node, desc)) {
goto error;
}
}

node->node.type = var->value.type;
*cur = ecs_meta_cursor(script->world, node->node.type, NULL);

*cur = ecs_meta_cursor(script->world, var->value.type, NULL);
return 0;
error:
return -1;
}

static
int flecs_expr_global_variable_visit_type(
ecs_script_t *script,
ecs_expr_variable_t *node,
ecs_meta_cursor_t *cur,
const ecs_expr_eval_desc_t *desc)
{
(void)cur;

if (flecs_expr_global_variable_resolve(script, node, desc)) {
goto error;
}

return 0;
error:
Expand Down Expand Up @@ -1125,6 +1176,13 @@ int flecs_expr_visit_type_priv(
goto error;
}
break;
case EcsExprGlobalVariable:
if (flecs_expr_global_variable_visit_type(
script, (ecs_expr_variable_t*)node, cur, desc))
{
goto error;
}
break;
case EcsExprFunction:
if (flecs_expr_function_visit_type(
script, (ecs_expr_function_t*)node, cur, desc))
Expand Down
Loading

0 comments on commit ad1d496

Please sign in to comment.