diff --git a/distr/flecs.c b/distr/flecs.c index 2d2dabb8f..731dcdd53 100644 --- a/distr/flecs.c +++ b/distr/flecs.c @@ -4957,6 +4957,7 @@ typedef enum ecs_expr_node_kind_t { EcsExprBinary, EcsExprIdentifier, EcsExprVariable, + EcsExprGlobalVariable, EcsExprFunction, EcsExprMethod, EcsExprMember, @@ -5001,6 +5002,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 { @@ -5145,6 +5147,10 @@ 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 @@ -5437,7 +5443,7 @@ int flecs_script_eval_node( #ifndef FLECS_SCRIPT_TEMPLATE_H #define FLECS_SCRIPT_TEMPLATE_H -extern ECS_COMPONENT_DECLARE(EcsTemplateSetEvent); +extern ECS_COMPONENT_DECLARE(EcsScriptTemplateSetEvent); struct ecs_script_template_t { /* Template handle */ @@ -5462,7 +5468,7 @@ struct ecs_script_template_t { #define ECS_TEMPLATE_SMALL_SIZE (36) /* Event used for deferring template instantiation */ -typedef struct EcsTemplateSetEvent { +typedef struct EcsScriptTemplateSetEvent { ecs_entity_t template_entity; ecs_entity_t *entities; void *data; @@ -5471,7 +5477,7 @@ typedef struct EcsTemplateSetEvent { /* Storage for small template types */ char data_storage[ECS_TEMPLATE_SMALL_SIZE]; ecs_entity_t entity_storage; -} EcsTemplateSetEvent; +} EcsScriptTemplateSetEvent; int flecs_script_eval_template( ecs_script_eval_visitor_t *v, @@ -55143,6 +55149,26 @@ void ecs_script_params_free(ecs_vec_t *params) { ecs_vec_fini_t(NULL, params, ecs_script_parameter_t); } +static +ECS_MOVE(EcsScriptConstVar, dst, src, { + if (dst->type_info->hooks.dtor) { + dst->type_info->hooks.dtor(dst->value.ptr, 1, dst->type_info); + } + + *dst = *src; + + src->value.ptr = NULL; + src->value.type = 0; + src->type_info = NULL; +}) + +static +ECS_DTOR(EcsScriptConstVar, ptr, { + if (ptr->type_info->hooks.dtor) { + ptr->type_info->hooks.dtor(ptr->value.ptr, 1, ptr->type_info); + } +}) + static ECS_MOVE(EcsScriptFunction, dst, src, { ecs_script_params_free(&dst->params); @@ -55167,21 +55193,61 @@ ECS_DTOR(EcsScriptMethod, ptr, { ecs_script_params_free(&ptr->params); }) +ecs_entity_t ecs_const_var_init( + ecs_world_t *world, + ecs_const_var_desc_t *desc) +{ + flecs_poly_assert(world, ecs_world_t); + ecs_check(desc != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(desc->name != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(desc->type != 0, ECS_INVALID_PARAMETER, NULL); + ecs_check(desc->value != NULL, ECS_INVALID_PARAMETER, NULL); + + const ecs_type_info_t *ti = ecs_get_type_info(world, desc->type); + ecs_check(ti != NULL, ECS_INVALID_PARAMETER, + "ecs_const_var_desc_t::type is not a valid type"); + + ecs_entity_t result = ecs_entity(world, { + .name = desc->name, + .parent = desc->parent + }); + + if (!result) { + goto error; + } + + EcsScriptConstVar *v = ecs_ensure(world, result, EcsScriptConstVar); + v->value.ptr = ecs_os_malloc(ti->size); + v->value.type = desc->type; + v->type_info = ti; + ecs_value_init(world, desc->type, v->value.ptr); + ecs_value_copy(world, desc->type, v->value.ptr, desc->value); + ecs_modified(world, result, EcsScriptConstVar); + + return result; +error: + return 0; +} + ecs_entity_t ecs_function_init( ecs_world_t *world, const ecs_function_desc_t *desc) { flecs_poly_assert(world, ecs_world_t); - ecs_assert(desc != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_assert(desc->name != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_assert(desc->callback != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_assert(desc->return_type != 0, ECS_INVALID_PARAMETER, NULL); + ecs_check(desc != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(desc->name != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(desc->callback != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(desc->return_type != 0, ECS_INVALID_PARAMETER, NULL); ecs_entity_t result = ecs_entity(world, { .name = desc->name, .parent = desc->parent }); + if (!result) { + goto error; + } + EcsScriptFunction *f = ecs_ensure(world, result, EcsScriptFunction); f->return_type = desc->return_type; f->callback = desc->callback; @@ -55206,6 +55272,8 @@ ecs_entity_t ecs_function_init( ecs_modified(world, result, EcsScriptFunction); return result; +error: + return 0; } ecs_entity_t ecs_method_init( @@ -55213,17 +55281,21 @@ ecs_entity_t ecs_method_init( const ecs_function_desc_t *desc) { flecs_poly_assert(world, ecs_world_t); - ecs_assert(desc != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_assert(desc->name != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_assert(desc->callback != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_assert(desc->parent != 0, ECS_INVALID_PARAMETER, NULL); - ecs_assert(desc->return_type != 0, ECS_INVALID_PARAMETER, NULL); + ecs_check(desc != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(desc->name != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(desc->callback != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(desc->parent != 0, ECS_INVALID_PARAMETER, NULL); + ecs_check(desc->return_type != 0, ECS_INVALID_PARAMETER, NULL); ecs_entity_t result = ecs_entity(world, { .name = desc->name, .parent = desc->parent }); + if (!result) { + goto error; + } + EcsScriptMethod *f = ecs_ensure(world, result, EcsScriptMethod); f->return_type = desc->return_type; f->callback = desc->callback; @@ -55248,12 +55320,15 @@ ecs_entity_t ecs_method_init( ecs_modified(world, result, EcsScriptMethod); return result; +error: + return 0; } void flecs_function_import( ecs_world_t *world) { ecs_set_name_prefix(world, "EcsScript"); + ECS_COMPONENT_DEFINE(world, EcsScriptConstVar); ECS_COMPONENT_DEFINE(world, EcsScriptFunction); ECS_COMPONENT_DEFINE(world, EcsScriptMethod); @@ -55271,6 +55346,13 @@ void flecs_function_import( } }); + ecs_set_hooks(world, EcsScriptConstVar, { + .ctor = flecs_default_ctor, + .dtor = ecs_dtor(EcsScriptConstVar), + .move = ecs_move(EcsScriptConstVar), + .flags = ECS_TYPE_HOOK_COPY_ILLEGAL + }); + ecs_set_hooks(world, EcsScriptFunction, { .ctor = flecs_default_ctor, .dtor = ecs_dtor(EcsScriptFunction), @@ -55549,6 +55631,23 @@ void FlecsScriptMathImport( ECS_IMPORT(world, FlecsScript); + /* Constants */ + double E = 2.71828182845904523536028747135266250; + ecs_const_var(world, { + .name = "E", + .parent = ecs_id(FlecsScriptMath), + .type = ecs_id(ecs_f64_t), + .value = &E + }); + + double PI = 3.14159265358979323846264338327950288; + ecs_const_var(world, { + .name = "PI", + .parent = ecs_id(FlecsScriptMath), + .type = ecs_id(ecs_f64_t), + .value = &PI + }); + /* Trigonometric functions */ FLECS_MATH_FUNC_DEF_F64(cos, "Compute cosine"); FLECS_MATH_FUNC_DEF_F64(sin, "Compute sine"); @@ -57676,6 +57775,7 @@ const char* ecs_query_args_parse( #ifdef FLECS_SCRIPT ECS_COMPONENT_DECLARE(EcsScript); +ECS_COMPONENT_DECLARE(EcsScriptConstVar); ECS_COMPONENT_DECLARE(EcsScriptFunction); ECS_COMPONENT_DECLARE(EcsScriptMethod); @@ -57740,7 +57840,7 @@ void ecs_script_clear( ecs_defer_begin(world); ecs_iter_t it = ecs_children(world, instance); while (ecs_children_next(&it)) { - if (ecs_table_has_id(world, it.table, ecs_pair(EcsTemplate, script))) { + if (ecs_table_has_id(world, it.table, ecs_pair(EcsScriptTemplate, script))) { int32_t i, count = it.count; for (i = 0; i < count; i ++) { ecs_delete(world, it.entities[i]); @@ -58503,11 +58603,11 @@ char* ecs_ptr_to_str( #ifdef FLECS_SCRIPT -ECS_COMPONENT_DECLARE(EcsTemplateSetEvent); -ECS_DECLARE(EcsTemplate); +ECS_COMPONENT_DECLARE(EcsScriptTemplateSetEvent); +ECS_DECLARE(EcsScriptTemplate); static -void flecs_template_set_event_free(EcsTemplateSetEvent *ptr) { +void flecs_template_set_event_free(EcsScriptTemplateSetEvent *ptr) { if (ptr->entities != &ptr->entity_storage) { ecs_os_free(ptr->entities); } @@ -58517,7 +58617,7 @@ void flecs_template_set_event_free(EcsTemplateSetEvent *ptr) { } static -ECS_MOVE(EcsTemplateSetEvent, dst, src, { +ECS_MOVE(EcsScriptTemplateSetEvent, dst, src, { flecs_template_set_event_free(dst); *dst = *src; @@ -58535,7 +58635,7 @@ ECS_MOVE(EcsTemplateSetEvent, dst, src, { }) static -ECS_DTOR(EcsTemplateSetEvent, ptr, { +ECS_DTOR(EcsScriptTemplateSetEvent, ptr, { flecs_template_set_event_free(ptr); }) @@ -58595,7 +58695,7 @@ void flecs_script_template_defer_on_set( const ecs_type_info_t *ti, void *data) { - EcsTemplateSetEvent evt; + EcsScriptTemplateSetEvent evt; if ((it->count == 1) && ti->size <= ECS_TEMPLATE_SMALL_SIZE && !ti->hooks.dtor) { /* This should be true for the vast majority of templates */ @@ -58612,7 +58712,7 @@ void flecs_script_template_defer_on_set( evt.template_entity = template_entity; ecs_enqueue(it->world, &(ecs_event_desc_t){ - .event = ecs_id(EcsTemplateSetEvent), + .event = ecs_id(EcsScriptTemplateSetEvent), .entity = EcsAny, .param = &evt }); @@ -58724,7 +58824,7 @@ void flecs_on_template_set_event( { ecs_assert(ecs_is_deferred(it->world), ECS_INTERNAL_ERROR, NULL); - EcsTemplateSetEvent *evt = it->param; + EcsScriptTemplateSetEvent *evt = it->param; ecs_world_t *world = it->real_world; ecs_assert(flecs_poly_is(world, ecs_world_t), ECS_INTERNAL_ERROR, NULL); @@ -59059,22 +59159,22 @@ int flecs_script_eval_template( void flecs_script_template_import( ecs_world_t *world) { - ECS_COMPONENT_DEFINE(world, EcsTemplateSetEvent); - ECS_TAG_DEFINE(world, EcsTemplate); + ECS_COMPONENT_DEFINE(world, EcsScriptTemplateSetEvent); + ECS_TAG_DEFINE(world, EcsScriptTemplate); - ecs_add_id(world, EcsTemplate, EcsPairIsTag); + ecs_add_id(world, EcsScriptTemplate, EcsPairIsTag); - ecs_set_hooks(world, EcsTemplateSetEvent, { + ecs_set_hooks(world, EcsScriptTemplateSetEvent, { .ctor = flecs_default_ctor, - .move = ecs_move(EcsTemplateSetEvent), - .dtor = ecs_dtor(EcsTemplateSetEvent), + .move = ecs_move(EcsScriptTemplateSetEvent), + .dtor = ecs_dtor(EcsScriptTemplateSetEvent), .flags = ECS_TYPE_HOOK_COPY_ILLEGAL }); ecs_observer(world, { .entity = ecs_entity(world, { .name = "TemplateSetObserver" }), .query.terms = {{ .id = EcsAny }}, - .events = { ecs_id(EcsTemplateSetEvent) }, + .events = { ecs_id(EcsScriptTemplateSetEvent) }, .callback = flecs_on_template_set_event }); } @@ -60551,7 +60651,7 @@ int flecs_script_eval_entity( node->parent = v->entity; if (v->template_entity) { - ecs_add_pair(v->world, node->eval, EcsTemplate, v->template_entity); + ecs_add_pair(v->world, node->eval, EcsScriptTemplate, v->template_entity); } if (is_slot) { @@ -74714,7 +74814,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; } @@ -75575,6 +75686,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, @@ -75858,6 +75986,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)) @@ -76307,6 +76442,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, @@ -76426,6 +76584,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)) { @@ -76588,6 +76751,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: @@ -76884,6 +77048,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)) { @@ -77676,6 +77841,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, @@ -77685,15 +77883,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: @@ -78068,6 +78284,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)) diff --git a/distr/flecs.h b/distr/flecs.h index f7947fd65..a9a17c1d5 100644 --- a/distr/flecs.h +++ b/distr/flecs.h @@ -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); @@ -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. */ @@ -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 */ diff --git a/include/flecs/addons/script.h b/include/flecs/addons/script.h index dabb65bb3..d642f0f38 100644 --- a/include/flecs/addons/script.h +++ b/include/flecs/addons/script.h @@ -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); @@ -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. */ @@ -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 */ diff --git a/src/addons/script/expr/ast.h b/src/addons/script/expr/ast.h index 49a1833d7..6cf645478 100644 --- a/src/addons/script/expr/ast.h +++ b/src/addons/script/expr/ast.h @@ -16,6 +16,7 @@ typedef enum ecs_expr_node_kind_t { EcsExprBinary, EcsExprIdentifier, EcsExprVariable, + EcsExprGlobalVariable, EcsExprFunction, EcsExprMethod, EcsExprMember, @@ -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 { diff --git a/src/addons/script/expr/parser.c b/src/addons/script/expr/parser.c index 3f3782731..39aea3361 100644 --- a/src/addons/script/expr/parser.c +++ b/src/addons/script/expr/parser.c @@ -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; } diff --git a/src/addons/script/expr/visit.h b/src/addons/script/expr/visit.h index 8874d0c38..732ba8051 100644 --- a/src/addons/script/expr/visit.h +++ b/src/addons/script/expr/visit.h @@ -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 diff --git a/src/addons/script/expr/visit_eval.c b/src/addons/script/expr/visit_eval.c index 1aa83be06..4f99b80bb 100644 --- a/src/addons/script/expr/visit_eval.c +++ b/src/addons/script/expr/visit_eval.c @@ -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, @@ -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)) diff --git a/src/addons/script/expr/visit_fold.c b/src/addons/script/expr/visit_fold.c index e641cee97..79f6165d4 100644 --- a/src/addons/script/expr/visit_fold.c +++ b/src/addons/script/expr/visit_fold.c @@ -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, @@ -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)) { diff --git a/src/addons/script/expr/visit_free.c b/src/addons/script/expr/visit_free.c index 32b6a9de9..a6b213240 100644 --- a/src/addons/script/expr/visit_free.c +++ b/src/addons/script/expr/visit_free.c @@ -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: diff --git a/src/addons/script/expr/visit_to_str.c b/src/addons/script/expr/visit_to_str.c index abe4c5200..98d02607e 100644 --- a/src/addons/script/expr/visit_to_str.c +++ b/src/addons/script/expr/visit_to_str.c @@ -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)) { diff --git a/src/addons/script/expr/visit_type.c b/src/addons/script/expr/visit_type.c index 4cfa4832c..03822b09b 100644 --- a/src/addons/script/expr/visit_type.c +++ b/src/addons/script/expr/visit_type.c @@ -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, @@ -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: @@ -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)) diff --git a/src/addons/script/function.c b/src/addons/script/function.c index 504152351..1bc3e76fb 100644 --- a/src/addons/script/function.c +++ b/src/addons/script/function.c @@ -19,6 +19,26 @@ void ecs_script_params_free(ecs_vec_t *params) { ecs_vec_fini_t(NULL, params, ecs_script_parameter_t); } +static +ECS_MOVE(EcsScriptConstVar, dst, src, { + if (dst->type_info->hooks.dtor) { + dst->type_info->hooks.dtor(dst->value.ptr, 1, dst->type_info); + } + + *dst = *src; + + src->value.ptr = NULL; + src->value.type = 0; + src->type_info = NULL; +}) + +static +ECS_DTOR(EcsScriptConstVar, ptr, { + if (ptr->type_info->hooks.dtor) { + ptr->type_info->hooks.dtor(ptr->value.ptr, 1, ptr->type_info); + } +}) + static ECS_MOVE(EcsScriptFunction, dst, src, { ecs_script_params_free(&dst->params); @@ -43,21 +63,61 @@ ECS_DTOR(EcsScriptMethod, ptr, { ecs_script_params_free(&ptr->params); }) +ecs_entity_t ecs_const_var_init( + ecs_world_t *world, + ecs_const_var_desc_t *desc) +{ + flecs_poly_assert(world, ecs_world_t); + ecs_check(desc != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(desc->name != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(desc->type != 0, ECS_INVALID_PARAMETER, NULL); + ecs_check(desc->value != NULL, ECS_INVALID_PARAMETER, NULL); + + const ecs_type_info_t *ti = ecs_get_type_info(world, desc->type); + ecs_check(ti != NULL, ECS_INVALID_PARAMETER, + "ecs_const_var_desc_t::type is not a valid type"); + + ecs_entity_t result = ecs_entity(world, { + .name = desc->name, + .parent = desc->parent + }); + + if (!result) { + goto error; + } + + EcsScriptConstVar *v = ecs_ensure(world, result, EcsScriptConstVar); + v->value.ptr = ecs_os_malloc(ti->size); + v->value.type = desc->type; + v->type_info = ti; + ecs_value_init(world, desc->type, v->value.ptr); + ecs_value_copy(world, desc->type, v->value.ptr, desc->value); + ecs_modified(world, result, EcsScriptConstVar); + + return result; +error: + return 0; +} + ecs_entity_t ecs_function_init( ecs_world_t *world, const ecs_function_desc_t *desc) { flecs_poly_assert(world, ecs_world_t); - ecs_assert(desc != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_assert(desc->name != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_assert(desc->callback != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_assert(desc->return_type != 0, ECS_INVALID_PARAMETER, NULL); + ecs_check(desc != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(desc->name != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(desc->callback != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(desc->return_type != 0, ECS_INVALID_PARAMETER, NULL); ecs_entity_t result = ecs_entity(world, { .name = desc->name, .parent = desc->parent }); + if (!result) { + goto error; + } + EcsScriptFunction *f = ecs_ensure(world, result, EcsScriptFunction); f->return_type = desc->return_type; f->callback = desc->callback; @@ -82,6 +142,8 @@ ecs_entity_t ecs_function_init( ecs_modified(world, result, EcsScriptFunction); return result; +error: + return 0; } ecs_entity_t ecs_method_init( @@ -89,17 +151,21 @@ ecs_entity_t ecs_method_init( const ecs_function_desc_t *desc) { flecs_poly_assert(world, ecs_world_t); - ecs_assert(desc != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_assert(desc->name != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_assert(desc->callback != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_assert(desc->parent != 0, ECS_INVALID_PARAMETER, NULL); - ecs_assert(desc->return_type != 0, ECS_INVALID_PARAMETER, NULL); + ecs_check(desc != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(desc->name != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(desc->callback != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_check(desc->parent != 0, ECS_INVALID_PARAMETER, NULL); + ecs_check(desc->return_type != 0, ECS_INVALID_PARAMETER, NULL); ecs_entity_t result = ecs_entity(world, { .name = desc->name, .parent = desc->parent }); + if (!result) { + goto error; + } + EcsScriptMethod *f = ecs_ensure(world, result, EcsScriptMethod); f->return_type = desc->return_type; f->callback = desc->callback; @@ -124,12 +190,15 @@ ecs_entity_t ecs_method_init( ecs_modified(world, result, EcsScriptMethod); return result; +error: + return 0; } void flecs_function_import( ecs_world_t *world) { ecs_set_name_prefix(world, "EcsScript"); + ECS_COMPONENT_DEFINE(world, EcsScriptConstVar); ECS_COMPONENT_DEFINE(world, EcsScriptFunction); ECS_COMPONENT_DEFINE(world, EcsScriptMethod); @@ -147,6 +216,13 @@ void flecs_function_import( } }); + ecs_set_hooks(world, EcsScriptConstVar, { + .ctor = flecs_default_ctor, + .dtor = ecs_dtor(EcsScriptConstVar), + .move = ecs_move(EcsScriptConstVar), + .flags = ECS_TYPE_HOOK_COPY_ILLEGAL + }); + ecs_set_hooks(world, EcsScriptFunction, { .ctor = flecs_default_ctor, .dtor = ecs_dtor(EcsScriptFunction), diff --git a/src/addons/script/functions_math.c b/src/addons/script/functions_math.c index e10239062..0a728967f 100644 --- a/src/addons/script/functions_math.c +++ b/src/addons/script/functions_math.c @@ -141,6 +141,23 @@ void FlecsScriptMathImport( ECS_IMPORT(world, FlecsScript); + /* Constants */ + double E = 2.71828182845904523536028747135266250; + ecs_const_var(world, { + .name = "E", + .parent = ecs_id(FlecsScriptMath), + .type = ecs_id(ecs_f64_t), + .value = &E + }); + + double PI = 3.14159265358979323846264338327950288; + ecs_const_var(world, { + .name = "PI", + .parent = ecs_id(FlecsScriptMath), + .type = ecs_id(ecs_f64_t), + .value = &PI + }); + /* Trigonometric functions */ FLECS_MATH_FUNC_DEF_F64(cos, "Compute cosine"); FLECS_MATH_FUNC_DEF_F64(sin, "Compute sine"); diff --git a/src/addons/script/script.c b/src/addons/script/script.c index cb49e8529..9244969f2 100644 --- a/src/addons/script/script.c +++ b/src/addons/script/script.c @@ -9,6 +9,7 @@ #include "script.h" ECS_COMPONENT_DECLARE(EcsScript); +ECS_COMPONENT_DECLARE(EcsScriptConstVar); ECS_COMPONENT_DECLARE(EcsScriptFunction); ECS_COMPONENT_DECLARE(EcsScriptMethod); @@ -73,7 +74,7 @@ void ecs_script_clear( ecs_defer_begin(world); ecs_iter_t it = ecs_children(world, instance); while (ecs_children_next(&it)) { - if (ecs_table_has_id(world, it.table, ecs_pair(EcsTemplate, script))) { + if (ecs_table_has_id(world, it.table, ecs_pair(EcsScriptTemplate, script))) { int32_t i, count = it.count; for (i = 0; i < count; i ++) { ecs_delete(world, it.entities[i]); diff --git a/src/addons/script/template.c b/src/addons/script/template.c index c3d49de07..30692c679 100644 --- a/src/addons/script/template.c +++ b/src/addons/script/template.c @@ -8,11 +8,11 @@ #ifdef FLECS_SCRIPT #include "script.h" -ECS_COMPONENT_DECLARE(EcsTemplateSetEvent); -ECS_DECLARE(EcsTemplate); +ECS_COMPONENT_DECLARE(EcsScriptTemplateSetEvent); +ECS_DECLARE(EcsScriptTemplate); static -void flecs_template_set_event_free(EcsTemplateSetEvent *ptr) { +void flecs_template_set_event_free(EcsScriptTemplateSetEvent *ptr) { if (ptr->entities != &ptr->entity_storage) { ecs_os_free(ptr->entities); } @@ -22,7 +22,7 @@ void flecs_template_set_event_free(EcsTemplateSetEvent *ptr) { } static -ECS_MOVE(EcsTemplateSetEvent, dst, src, { +ECS_MOVE(EcsScriptTemplateSetEvent, dst, src, { flecs_template_set_event_free(dst); *dst = *src; @@ -40,7 +40,7 @@ ECS_MOVE(EcsTemplateSetEvent, dst, src, { }) static -ECS_DTOR(EcsTemplateSetEvent, ptr, { +ECS_DTOR(EcsScriptTemplateSetEvent, ptr, { flecs_template_set_event_free(ptr); }) @@ -100,7 +100,7 @@ void flecs_script_template_defer_on_set( const ecs_type_info_t *ti, void *data) { - EcsTemplateSetEvent evt; + EcsScriptTemplateSetEvent evt; if ((it->count == 1) && ti->size <= ECS_TEMPLATE_SMALL_SIZE && !ti->hooks.dtor) { /* This should be true for the vast majority of templates */ @@ -117,7 +117,7 @@ void flecs_script_template_defer_on_set( evt.template_entity = template_entity; ecs_enqueue(it->world, &(ecs_event_desc_t){ - .event = ecs_id(EcsTemplateSetEvent), + .event = ecs_id(EcsScriptTemplateSetEvent), .entity = EcsAny, .param = &evt }); @@ -229,7 +229,7 @@ void flecs_on_template_set_event( { ecs_assert(ecs_is_deferred(it->world), ECS_INTERNAL_ERROR, NULL); - EcsTemplateSetEvent *evt = it->param; + EcsScriptTemplateSetEvent *evt = it->param; ecs_world_t *world = it->real_world; ecs_assert(flecs_poly_is(world, ecs_world_t), ECS_INTERNAL_ERROR, NULL); @@ -564,22 +564,22 @@ int flecs_script_eval_template( void flecs_script_template_import( ecs_world_t *world) { - ECS_COMPONENT_DEFINE(world, EcsTemplateSetEvent); - ECS_TAG_DEFINE(world, EcsTemplate); + ECS_COMPONENT_DEFINE(world, EcsScriptTemplateSetEvent); + ECS_TAG_DEFINE(world, EcsScriptTemplate); - ecs_add_id(world, EcsTemplate, EcsPairIsTag); + ecs_add_id(world, EcsScriptTemplate, EcsPairIsTag); - ecs_set_hooks(world, EcsTemplateSetEvent, { + ecs_set_hooks(world, EcsScriptTemplateSetEvent, { .ctor = flecs_default_ctor, - .move = ecs_move(EcsTemplateSetEvent), - .dtor = ecs_dtor(EcsTemplateSetEvent), + .move = ecs_move(EcsScriptTemplateSetEvent), + .dtor = ecs_dtor(EcsScriptTemplateSetEvent), .flags = ECS_TYPE_HOOK_COPY_ILLEGAL }); ecs_observer(world, { .entity = ecs_entity(world, { .name = "TemplateSetObserver" }), .query.terms = {{ .id = EcsAny }}, - .events = { ecs_id(EcsTemplateSetEvent) }, + .events = { ecs_id(EcsScriptTemplateSetEvent) }, .callback = flecs_on_template_set_event }); } diff --git a/src/addons/script/template.h b/src/addons/script/template.h index 28e49f263..a3ab59b03 100644 --- a/src/addons/script/template.h +++ b/src/addons/script/template.h @@ -6,7 +6,7 @@ #ifndef FLECS_SCRIPT_TEMPLATE_H #define FLECS_SCRIPT_TEMPLATE_H -extern ECS_COMPONENT_DECLARE(EcsTemplateSetEvent); +extern ECS_COMPONENT_DECLARE(EcsScriptTemplateSetEvent); struct ecs_script_template_t { /* Template handle */ @@ -31,7 +31,7 @@ struct ecs_script_template_t { #define ECS_TEMPLATE_SMALL_SIZE (36) /* Event used for deferring template instantiation */ -typedef struct EcsTemplateSetEvent { +typedef struct EcsScriptTemplateSetEvent { ecs_entity_t template_entity; ecs_entity_t *entities; void *data; @@ -40,7 +40,7 @@ typedef struct EcsTemplateSetEvent { /* Storage for small template types */ char data_storage[ECS_TEMPLATE_SMALL_SIZE]; ecs_entity_t entity_storage; -} EcsTemplateSetEvent; +} EcsScriptTemplateSetEvent; int flecs_script_eval_template( ecs_script_eval_visitor_t *v, diff --git a/src/addons/script/visit_eval.c b/src/addons/script/visit_eval.c index 9c63aeed2..2508e74da 100644 --- a/src/addons/script/visit_eval.c +++ b/src/addons/script/visit_eval.c @@ -473,7 +473,7 @@ int flecs_script_eval_entity( node->parent = v->entity; if (v->template_entity) { - ecs_add_pair(v->world, node->eval, EcsTemplate, v->template_entity); + ecs_add_pair(v->world, node->eval, EcsScriptTemplate, v->template_entity); } if (is_slot) { diff --git a/test/script/project.json b/test/script/project.json index 4e99947c9..b49346e1d 100644 --- a/test/script/project.json +++ b/test/script/project.json @@ -609,7 +609,9 @@ "remainder_after_initializer_w_newlines", "remainder_after_initializer_before_parens", "space_at_start", - "newline_at_start" + "newline_at_start", + "global_const_var", + "scoped_global_const_var" ] }, { "id": "ExprAst", diff --git a/test/script/src/Expr.c b/test/script/src/Expr.c index e754a3f55..23b1e6a92 100644 --- a/test/script/src/Expr.c +++ b/test/script/src/Expr.c @@ -5099,3 +5099,51 @@ void Expr_newline_at_start(void) { ecs_fini(world); } + +void Expr_global_const_var(void) { + ecs_world_t *world = ecs_init(); + + int32_t v = 10; + + test_assert(0 != ecs_const_var(world, { + .name = "FOO", + .type = ecs_id(ecs_i32_t), + .value = &v + })); + + ecs_expr_eval_desc_t desc = { .disable_folding = disable_folding }; + const char *ptr = ecs_expr_run(world, "$FOO + 20", + &ecs_value_ptr(ecs_i32_t, &v), &desc); + test_assert(ptr != NULL); + test_assert(ptr[0] == 0); + test_int(v, 30); + + ecs_fini(world); +} + +void Expr_scoped_global_const_var(void) { + ecs_world_t *world = ecs_init(); + + int32_t v = 10; + + ecs_entity_t parent = ecs_entity(world, { + .name = "parent" + }); + + test_assert(0 != ecs_const_var(world, { + .name = "FOO", + .parent = parent, + .type = ecs_id(ecs_i32_t), + .value = &v + })); + + ecs_expr_eval_desc_t desc = { .disable_folding = disable_folding }; + const char *ptr = ecs_expr_run(world, + "parent.$FOO + 20", + &ecs_value_ptr(ecs_i32_t, &v), &desc); + test_assert(ptr != NULL); + test_assert(ptr[0] == 0); + test_int(v, 30); + + ecs_fini(world); +} diff --git a/test/script/src/main.c b/test/script/src/main.c index 092e82a32..a278776aa 100644 --- a/test/script/src/main.c +++ b/test/script/src/main.c @@ -597,6 +597,8 @@ void Expr_remainder_after_initializer_w_newlines(void); void Expr_remainder_after_initializer_before_parens(void); void Expr_space_at_start(void); void Expr_newline_at_start(void); +void Expr_global_const_var(void); +void Expr_scoped_global_const_var(void); // Testsuite 'ExprAst' void ExprAst_binary_f32_var_add_f32_var(void); @@ -3104,6 +3106,14 @@ bake_test_case Expr_testcases[] = { { "newline_at_start", Expr_newline_at_start + }, + { + "global_const_var", + Expr_global_const_var + }, + { + "scoped_global_const_var", + Expr_scoped_global_const_var } }; @@ -3810,7 +3820,7 @@ static bake_test_suite suites[] = { "Expr", Expr_setup, NULL, - 191, + 193, Expr_testcases, 1, Expr_params