diff --git a/distr/flecs.c b/distr/flecs.c index 71a2b5072..e75d1271c 100644 --- a/distr/flecs.c +++ b/distr/flecs.c @@ -55061,124 +55061,6 @@ ecs_script_if_t* flecs_script_insert_if( #endif -/** - * @file addons/script/builtin_functions.c - * @brief Builtin script functions. - */ - - -#ifdef FLECS_SCRIPT - -static -void flecs_meta_entity_name( - const ecs_function_ctx_t *ctx, - int32_t argc, - const ecs_value_t *argv, - ecs_value_t *result) -{ - (void)argc; - ecs_entity_t entity = *(ecs_entity_t*)argv[0].ptr; - *(char**)result->ptr = ecs_os_strdup(ecs_get_name(ctx->world, entity)); -} - -static -void flecs_meta_entity_path( - const ecs_function_ctx_t *ctx, - int32_t argc, - const ecs_value_t *argv, - ecs_value_t *result) -{ - (void)argc; - ecs_entity_t entity = *(ecs_entity_t*)argv[0].ptr; - *(char**)result->ptr = ecs_get_path(ctx->world, entity); -} - -static -void flecs_meta_entity_parent( - const ecs_function_ctx_t *ctx, - int32_t argc, - const ecs_value_t *argv, - ecs_value_t *result) -{ - (void)argc; - ecs_entity_t entity = *(ecs_entity_t*)argv[0].ptr; - *(ecs_entity_t*)result->ptr = ecs_get_parent(ctx->world, entity); -} - -#ifdef FLECS_DOC - -static -void flecs_meta_entity_doc_name( - const ecs_function_ctx_t *ctx, - int32_t argc, - const ecs_value_t *argv, - ecs_value_t *result) -{ - (void)argc; - ecs_entity_t entity = *(ecs_entity_t*)argv[0].ptr; - *(char**)result->ptr = ecs_os_strdup(ecs_doc_get_name(ctx->world, entity)); -} - -static -void flecs_script_register_builtin_doc_functions( - ecs_world_t *world) -{ - ecs_entity_t name = ecs_script_method(world, { - .name = "doc_name", - .parent = ecs_id(ecs_entity_t), - .return_type = ecs_id(ecs_string_t), - .callback = flecs_meta_entity_doc_name - }); - - ecs_doc_set_brief(world, name, "Returns entity doc name"); -} - -#else - -static -void flecs_script_register_builtin_doc_functions( - ecs_world_t *world) -{ - (void)world; -} - -#endif - -void flecs_script_register_builtin_functions( - ecs_world_t *world) -{ - ecs_entity_t name = ecs_script_method(world, { - .name = "name", - .parent = ecs_id(ecs_entity_t), - .return_type = ecs_id(ecs_string_t), - .callback = flecs_meta_entity_name - }); - - ecs_doc_set_brief(world, name, "Returns entity name"); - - ecs_entity_t path = ecs_script_method(world, { - .name = "path", - .parent = ecs_id(ecs_entity_t), - .return_type = ecs_id(ecs_string_t), - .callback = flecs_meta_entity_path - }); - - ecs_doc_set_brief(world, path, "Returns entity path"); - - ecs_entity_t parent = ecs_script_method(world, { - .name = "parent", - .parent = ecs_id(ecs_entity_t), - .return_type = ecs_id(ecs_entity_t), - .callback = flecs_meta_entity_parent - }); - - ecs_doc_set_brief(world, parent, "Returns entity parent"); - - flecs_script_register_builtin_doc_functions(world); -} - -#endif - /** * @file addons/script/function.c * @brief Script function API. @@ -55345,6 +55227,303 @@ void flecs_script_function_import( #endif +/** + * @file addons/script/builtin_functions.c + * @brief Flecs functions for flecs script. + */ + + +#ifdef FLECS_SCRIPT + +static +void flecs_meta_entity_name( + const ecs_function_ctx_t *ctx, + int32_t argc, + const ecs_value_t *argv, + ecs_value_t *result) +{ + (void)argc; + ecs_entity_t entity = *(ecs_entity_t*)argv[0].ptr; + *(char**)result->ptr = ecs_os_strdup(ecs_get_name(ctx->world, entity)); +} + +static +void flecs_meta_entity_path( + const ecs_function_ctx_t *ctx, + int32_t argc, + const ecs_value_t *argv, + ecs_value_t *result) +{ + (void)argc; + ecs_entity_t entity = *(ecs_entity_t*)argv[0].ptr; + *(char**)result->ptr = ecs_get_path(ctx->world, entity); +} + +static +void flecs_meta_entity_parent( + const ecs_function_ctx_t *ctx, + int32_t argc, + const ecs_value_t *argv, + ecs_value_t *result) +{ + (void)argc; + ecs_entity_t entity = *(ecs_entity_t*)argv[0].ptr; + *(ecs_entity_t*)result->ptr = ecs_get_parent(ctx->world, entity); +} + +#ifdef FLECS_DOC + +static +void flecs_meta_entity_doc_name( + const ecs_function_ctx_t *ctx, + int32_t argc, + const ecs_value_t *argv, + ecs_value_t *result) +{ + (void)argc; + ecs_entity_t entity = *(ecs_entity_t*)argv[0].ptr; + *(char**)result->ptr = ecs_os_strdup(ecs_doc_get_name(ctx->world, entity)); +} + +static +void flecs_script_register_builtin_doc_functions( + ecs_world_t *world) +{ + ecs_entity_t name = ecs_script_method(world, { + .name = "doc_name", + .parent = ecs_id(ecs_entity_t), + .return_type = ecs_id(ecs_string_t), + .callback = flecs_meta_entity_doc_name + }); + + ecs_doc_set_brief(world, name, "Returns entity doc name"); +} + +#else + +static +void flecs_script_register_builtin_doc_functions( + ecs_world_t *world) +{ + (void)world; +} + +#endif + +void flecs_script_register_builtin_functions( + ecs_world_t *world) +{ + ecs_entity_t name = ecs_script_method(world, { + .name = "name", + .parent = ecs_id(ecs_entity_t), + .return_type = ecs_id(ecs_string_t), + .callback = flecs_meta_entity_name + }); + + ecs_doc_set_brief(world, name, "Returns entity name"); + + ecs_entity_t path = ecs_script_method(world, { + .name = "path", + .parent = ecs_id(ecs_entity_t), + .return_type = ecs_id(ecs_string_t), + .callback = flecs_meta_entity_path + }); + + ecs_doc_set_brief(world, path, "Returns entity path"); + + ecs_entity_t parent = ecs_script_method(world, { + .name = "parent", + .parent = ecs_id(ecs_entity_t), + .return_type = ecs_id(ecs_entity_t), + .callback = flecs_meta_entity_parent + }); + + ecs_doc_set_brief(world, parent, "Returns entity parent"); + + flecs_script_register_builtin_doc_functions(world); +} + +#endif + +/** + * @file addons/script/functions_math.c + * @brief Math functions for flecs script. + */ + + +#ifdef FLECS_SCRIPT_MATH +#include + +#define FLECS_MATH_FUNC_F64(name, ...)\ + static\ + void flecs_math_##name(\ + const ecs_function_ctx_t *ctx,\ + int32_t argc,\ + const ecs_value_t *argv,\ + ecs_value_t *result)\ + {\ + (void)ctx;\ + (void)argc;\ + ecs_assert(argc == 1, ECS_INTERNAL_ERROR, NULL);\ + double x = *(double*)argv[0].ptr;\ + *(double*)result->ptr = __VA_ARGS__;\ + } + +#define FLECS_MATH_FUNC_F64_F64(name, ...)\ + static\ + void flecs_math_##name(\ + const ecs_function_ctx_t *ctx,\ + int32_t argc,\ + const ecs_value_t *argv,\ + ecs_value_t *result)\ + {\ + (void)ctx;\ + (void)argc;\ + ecs_assert(argc == 2, ECS_INTERNAL_ERROR, NULL);\ + double x = *(double*)argv[0].ptr;\ + double y = *(double*)argv[1].ptr;\ + *(double*)result->ptr = __VA_ARGS__;\ + } + +#define FLECS_MATH_FUNC_F64_I32(name, ...)\ + static\ + void flecs_math_##name(\ + const ecs_function_ctx_t *ctx,\ + int32_t argc,\ + const ecs_value_t *argv,\ + ecs_value_t *result)\ + {\ + (void)ctx;\ + (void)argc;\ + ecs_assert(argc == 2, ECS_INTERNAL_ERROR, NULL);\ + double x = *(double*)argv[0].ptr;\ + ecs_i32_t y = *(ecs_i32_t*)argv[1].ptr;\ + *(double*)result->ptr = __VA_ARGS__;\ + } + +#define FLECS_MATH_FUNC_DEF_F64(_name, brief)\ + {\ + ecs_entity_t f = ecs_script_function(world, {\ + .name = #_name,\ + .parent = ecs_id(FlecsScriptMath),\ + .return_type = ecs_id(ecs_f64_t),\ + .params = {{ .name = "x", .type = ecs_id(ecs_f64_t) }},\ + .callback = flecs_math_##_name\ + });\ + ecs_doc_set_brief(world, f, brief);\ + } + +#define FLECS_MATH_FUNC_DEF_F64_F64(_name, brief)\ + {\ + ecs_entity_t f = ecs_script_function(world, {\ + .name = #_name,\ + .parent = ecs_id(FlecsScriptMath),\ + .return_type = ecs_id(ecs_f64_t),\ + .params = {\ + { .name = "x", .type = ecs_id(ecs_f64_t) },\ + { .name = "y", .type = ecs_id(ecs_f64_t) }\ + },\ + .callback = flecs_math_##_name\ + });\ + ecs_doc_set_brief(world, f, brief);\ + } + +#define FLECS_MATH_FUNC_DEF_F64_F32(_name, brief)\ + {\ + ecs_entity_t f = ecs_script_function(world, {\ + .name = #_name,\ + .parent = ecs_id(FlecsScriptMath),\ + .return_type = ecs_id(ecs_f64_t),\ + .params = {\ + { .name = "x", .type = ecs_id(ecs_f64_t) },\ + { .name = "y", .type = ecs_id(ecs_i32_t) }\ + },\ + .callback = flecs_math_##_name\ + });\ + ecs_doc_set_brief(world, f, brief);\ + } + +/* Trigonometric functions */ +FLECS_MATH_FUNC_F64(cos, cos(x)) +FLECS_MATH_FUNC_F64(sin, sin(x)) +FLECS_MATH_FUNC_F64(tan, tan(x)) +FLECS_MATH_FUNC_F64(acos, acos(x)) +FLECS_MATH_FUNC_F64(asin, asin(x)) +FLECS_MATH_FUNC_F64(atan, atan(x)) +FLECS_MATH_FUNC_F64_F64(atan2, atan2(x, y)) + +/* Hyperbolic functions */ +FLECS_MATH_FUNC_F64(cosh, cosh(x)) +FLECS_MATH_FUNC_F64(sinh, sinh(x)) +FLECS_MATH_FUNC_F64(tanh, tanh(x)) +FLECS_MATH_FUNC_F64(acosh, acosh(x)) +FLECS_MATH_FUNC_F64(asinh, asinh(x)) +FLECS_MATH_FUNC_F64(atanh, atanh(x)) + +/* Exponential and logarithmic functions */ +FLECS_MATH_FUNC_F64(exp, exp(x)) +FLECS_MATH_FUNC_F64_I32(ldexp, ldexp(x, y)) +FLECS_MATH_FUNC_F64(log, log(x)) +FLECS_MATH_FUNC_F64(log10, log10(x)) +FLECS_MATH_FUNC_F64(exp2, exp2(x)) +FLECS_MATH_FUNC_F64(log2, log2(x)) + +/* Power functions */ +FLECS_MATH_FUNC_F64_F64(pow, pow(x, y)) +FLECS_MATH_FUNC_F64(sqrt, sqrt(x)) +FLECS_MATH_FUNC_F64(sqr, x * x) + +/* Rounding functions */ +FLECS_MATH_FUNC_F64(ceil, ceil(x)) +FLECS_MATH_FUNC_F64(floor, floor(x)) +FLECS_MATH_FUNC_F64(round, round(x)) + +FLECS_API +void FlecsScriptMathImport( + ecs_world_t *world) +{ + ECS_MODULE(world, FlecsScriptMath); + + ECS_IMPORT(world, FlecsScript); + + /* Trigonometric functions */ + FLECS_MATH_FUNC_DEF_F64(cos, "Compute cosine"); + FLECS_MATH_FUNC_DEF_F64(sin, "Compute sine"); + FLECS_MATH_FUNC_DEF_F64(tan, "Compute tangent"); + FLECS_MATH_FUNC_DEF_F64(acos, "Compute arc cosine"); + FLECS_MATH_FUNC_DEF_F64(asin, "Compute arc sine"); + FLECS_MATH_FUNC_DEF_F64(atan, "Compute arc tangent"); + FLECS_MATH_FUNC_DEF_F64_F64(atan2, "Compute arc tangent with two parameters"); + + /* Hyperbolic functions */ + FLECS_MATH_FUNC_DEF_F64(cosh, "Compute hyperbolic cosine"); + FLECS_MATH_FUNC_DEF_F64(sinh, "Compute hyperbolic sine"); + FLECS_MATH_FUNC_DEF_F64(tanh, "Compute hyperbolic tangent"); + FLECS_MATH_FUNC_DEF_F64(acosh, "Compute area hyperbolic cosine"); + FLECS_MATH_FUNC_DEF_F64(asinh, "Compute area hyperbolic sine"); + FLECS_MATH_FUNC_DEF_F64(atanh, "Compute area hyperbolic tangent"); + + /* Exponential and logarithmic functions */ + FLECS_MATH_FUNC_DEF_F64(exp, "Compute exponential function"); + FLECS_MATH_FUNC_DEF_F64_F32(ldexp, "Generate value from significand and exponent"); + FLECS_MATH_FUNC_DEF_F64(log, "Compute natural logarithm"); + FLECS_MATH_FUNC_DEF_F64(log10, "Compute common logarithm"); + FLECS_MATH_FUNC_DEF_F64(exp2, "Compute binary exponential function"); + FLECS_MATH_FUNC_DEF_F64(log2, "Compute binary logarithm"); + + /* Power functions */ + FLECS_MATH_FUNC_DEF_F64_F64(pow, "Raise to power"); + FLECS_MATH_FUNC_DEF_F64(sqrt, "Compute square root"); + FLECS_MATH_FUNC_DEF_F64(sqr, "Compute square"); + + /* Rounding functions */ + FLECS_MATH_FUNC_DEF_F64(ceil, "Round up value"); + FLECS_MATH_FUNC_DEF_F64(floor, "Round down value"); + FLECS_MATH_FUNC_DEF_F64(round, "Round to nearest"); +} + +#endif + /** * @file addons/script/interpolate.c * @brief String interpolation. @@ -77096,7 +77275,8 @@ int flecs_expr_function_visit_type( try_function: if (!is_method) { - ecs_entity_t func = ecs_lookup(world, node->function_name); + ecs_entity_t func = desc->lookup_action( + world, node->function_name, desc->lookup_ctx); if (!func) { flecs_expr_visit_error(script, node, "unresolved function identifier '%s'", diff --git a/distr/flecs.h b/distr/flecs.h index c5f511a42..6c3865b45 100644 --- a/distr/flecs.h +++ b/distr/flecs.h @@ -146,6 +146,16 @@ */ // #define FLECS_CPP_NO_AUTO_REGISTRATION +/** @def FLECS_CPP_NO_AUTO_REGISTRATION + * When set, the C++ API will require that components are registered before they + * are used. This is useful in multithreaded applications, where components need + * to be registered beforehand, and to catch issues in projects where component + * registration is mandatory. Disabling automatic component registration also + * slightly improves performance. + * The C API is not affected by this feature. + */ +// #define FLECS_CPP_NO_AUTO_REGISTRATION + /** \def FLECS_CUSTOM_BUILD * This macro lets you customize which addons to build flecs with. * Without any addons Flecs is just a minimal ECS storage, but addons add @@ -175,38 +185,29 @@ */ // #define FLECS_CUSTOM_BUILD -/** @def FLECS_CPP_NO_AUTO_REGISTRATION - * When set, the C++ API will require that components are registered before they - * are used. This is useful in multithreaded applications, where components need - * to be registered beforehand, and to catch issues in projects where component - * registration is mandatory. Disabling automatic component registration also - * slightly improves performance. - * The C API is not affected by this feature. - */ -// #define FLECS_CPP_NO_AUTO_REGISTRATION - #ifndef FLECS_CUSTOM_BUILD -// #define FLECS_C /**< C API convenience macros, always enabled */ -#define FLECS_CPP /**< C++ API */ -#define FLECS_MODULE /**< Module support */ -#define FLECS_SCRIPT /**< ECS data definition format */ -#define FLECS_STATS /**< Track runtime statistics */ -#define FLECS_METRICS /**< Expose component data as statistics */ -#define FLECS_ALERTS /**< Monitor conditions for errors */ -#define FLECS_SYSTEM /**< System support */ -#define FLECS_PIPELINE /**< Pipeline support */ -#define FLECS_TIMER /**< Timer support */ -#define FLECS_META /**< Reflection support */ -#define FLECS_UNITS /**< Builtin standard units */ -#define FLECS_JSON /**< Parsing JSON to/from component values */ -#define FLECS_DOC /**< Document entities & components */ -#define FLECS_LOG /**< When enabled ECS provides more detailed logs */ -#define FLECS_APP /**< Application addon */ -#define FLECS_OS_API_IMPL /**< Default implementation for OS API */ -#define FLECS_HTTP /**< Tiny HTTP server for connecting to remote UI */ -#define FLECS_REST /**< REST API for querying application data */ -// #define FLECS_JOURNAL /**< Journaling addon (disabled by default) */ -// #define FLECS_PERF_TRACE /**< Enable performance tracing (disabled by default) */ +#define FLECS_ALERTS /**< Monitor conditions for errors */ +#define FLECS_APP /**< Application addon */ +// #define FLECS_C /**< C API convenience macros, always enabled */ +#define FLECS_CPP /**< C++ API */ +#define FLECS_DOC /**< Document entities & components */ +// #define FLECS_JOURNAL /**< Journaling addon (disabled by default) */ +#define FLECS_JSON /**< Parsing JSON to/from component values */ +#define FLECS_HTTP /**< Tiny HTTP server for connecting to remote UI */ +#define FLECS_LOG /**< When enabled ECS provides more detailed logs */ +#define FLECS_META /**< Reflection support */ +#define FLECS_METRICS /**< Expose component data as statistics */ +#define FLECS_MODULE /**< Module support */ +#define FLECS_OS_API_IMPL /**< Default implementation for OS API */ +// #define FLECS_PERF_TRACE /**< Enable performance tracing (disabled by default) */ +#define FLECS_PIPELINE /**< Pipeline support */ +#define FLECS_REST /**< REST API for querying application data */ +#define FLECS_SCRIPT /**< Flecs entity notation language */ +// #define FLECS_SCRIPT_MATH /**< Math functions for flecs script (may require linking with libm) */ +#define FLECS_SYSTEM /**< System support */ +#define FLECS_STATS /**< Track runtime statistics */ +#define FLECS_TIMER /**< Timer support */ +#define FLECS_UNITS /**< Builtin standard units */ #endif // ifndef FLECS_CUSTOM_BUILD /** @def FLECS_LOW_FOOTPRINT @@ -10395,6 +10396,9 @@ int ecs_value_move_ctor( #ifdef FLECS_NO_SCRIPT #undef FLECS_SCRIPT #endif +#ifdef FLECS_NO_SCRIPT_MATH +#undef FLECS_SCRIPT_MATH +#endif #ifdef FLECS_NO_STATS #undef FLECS_STATS #endif @@ -14211,6 +14215,59 @@ void FlecsUnitsImport( #endif +#ifdef FLECS_SCRIPT_MATH +#ifdef FLECS_NO_SCRIPT_MATH +#error "FLECS_NO_SCRIPT_MATH failed: SCRIPT_MATH is required by other addons" +#endif +/** + * @file addons/script_math.h + * @brief Math functions for flecs script. + */ + +#ifdef FLECS_SCRIPT_MATH + +#ifndef FLECS_SCRIPT +#define FLECS_SCRIPT +#endif + +/** + * @defgroup c_addons_script_math Script Math + * @ingroup c_addons + * Math functions for flecs script. + * @{ + */ + +#ifndef FLECS_SCRIPT_MATH_H +#define FLECS_SCRIPT_MATH_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** Script math import function. + * Usage: + * @code + * ECS_IMPORT(world, FlecsScriptMath) + * @endcode + * + * @param world The world. + */ +FLECS_API +void FlecsScriptMathImport( + ecs_world_t *world); + +#ifdef __cplusplus +} +#endif + +#endif + +/** @} */ + +#endif + +#endif + #ifdef FLECS_SCRIPT #ifdef FLECS_NO_SCRIPT #error "FLECS_NO_SCRIPT failed: SCRIPT is required by other addons" diff --git a/include/flecs.h b/include/flecs.h index 8dde6591f..027abc239 100644 --- a/include/flecs.h +++ b/include/flecs.h @@ -144,6 +144,16 @@ */ // #define FLECS_CPP_NO_AUTO_REGISTRATION +/** @def FLECS_CPP_NO_AUTO_REGISTRATION + * When set, the C++ API will require that components are registered before they + * are used. This is useful in multithreaded applications, where components need + * to be registered beforehand, and to catch issues in projects where component + * registration is mandatory. Disabling automatic component registration also + * slightly improves performance. + * The C API is not affected by this feature. + */ +// #define FLECS_CPP_NO_AUTO_REGISTRATION + /** \def FLECS_CUSTOM_BUILD * This macro lets you customize which addons to build flecs with. * Without any addons Flecs is just a minimal ECS storage, but addons add @@ -173,38 +183,29 @@ */ // #define FLECS_CUSTOM_BUILD -/** @def FLECS_CPP_NO_AUTO_REGISTRATION - * When set, the C++ API will require that components are registered before they - * are used. This is useful in multithreaded applications, where components need - * to be registered beforehand, and to catch issues in projects where component - * registration is mandatory. Disabling automatic component registration also - * slightly improves performance. - * The C API is not affected by this feature. - */ -// #define FLECS_CPP_NO_AUTO_REGISTRATION - #ifndef FLECS_CUSTOM_BUILD -// #define FLECS_C /**< C API convenience macros, always enabled */ -#define FLECS_CPP /**< C++ API */ -#define FLECS_MODULE /**< Module support */ -#define FLECS_SCRIPT /**< ECS data definition format */ -#define FLECS_STATS /**< Track runtime statistics */ -#define FLECS_METRICS /**< Expose component data as statistics */ -#define FLECS_ALERTS /**< Monitor conditions for errors */ -#define FLECS_SYSTEM /**< System support */ -#define FLECS_PIPELINE /**< Pipeline support */ -#define FLECS_TIMER /**< Timer support */ -#define FLECS_META /**< Reflection support */ -#define FLECS_UNITS /**< Builtin standard units */ -#define FLECS_JSON /**< Parsing JSON to/from component values */ -#define FLECS_DOC /**< Document entities & components */ -#define FLECS_LOG /**< When enabled ECS provides more detailed logs */ -#define FLECS_APP /**< Application addon */ -#define FLECS_OS_API_IMPL /**< Default implementation for OS API */ -#define FLECS_HTTP /**< Tiny HTTP server for connecting to remote UI */ -#define FLECS_REST /**< REST API for querying application data */ -// #define FLECS_JOURNAL /**< Journaling addon (disabled by default) */ -// #define FLECS_PERF_TRACE /**< Enable performance tracing (disabled by default) */ +#define FLECS_ALERTS /**< Monitor conditions for errors */ +#define FLECS_APP /**< Application addon */ +// #define FLECS_C /**< C API convenience macros, always enabled */ +#define FLECS_CPP /**< C++ API */ +#define FLECS_DOC /**< Document entities & components */ +// #define FLECS_JOURNAL /**< Journaling addon (disabled by default) */ +#define FLECS_JSON /**< Parsing JSON to/from component values */ +#define FLECS_HTTP /**< Tiny HTTP server for connecting to remote UI */ +#define FLECS_LOG /**< When enabled ECS provides more detailed logs */ +#define FLECS_META /**< Reflection support */ +#define FLECS_METRICS /**< Expose component data as statistics */ +#define FLECS_MODULE /**< Module support */ +#define FLECS_OS_API_IMPL /**< Default implementation for OS API */ +// #define FLECS_PERF_TRACE /**< Enable performance tracing (disabled by default) */ +#define FLECS_PIPELINE /**< Pipeline support */ +#define FLECS_REST /**< REST API for querying application data */ +#define FLECS_SCRIPT /**< Flecs entity notation language */ +// #define FLECS_SCRIPT_MATH /**< Math functions for flecs script (may require linking with libm) */ +#define FLECS_SYSTEM /**< System support */ +#define FLECS_STATS /**< Track runtime statistics */ +#define FLECS_TIMER /**< Timer support */ +#define FLECS_UNITS /**< Builtin standard units */ #endif // ifndef FLECS_CUSTOM_BUILD /** @def FLECS_LOW_FOOTPRINT diff --git a/include/flecs/addons/script_math.h b/include/flecs/addons/script_math.h new file mode 100644 index 000000000..918576cb0 --- /dev/null +++ b/include/flecs/addons/script_math.h @@ -0,0 +1,46 @@ +/** + * @file addons/script_math.h + * @brief Math functions for flecs script. + */ + +#ifdef FLECS_SCRIPT_MATH + +#ifndef FLECS_SCRIPT +#define FLECS_SCRIPT +#endif + +/** + * @defgroup c_addons_script_math Script Math + * @ingroup c_addons + * Math functions for flecs script. + * @{ + */ + +#ifndef FLECS_SCRIPT_MATH_H +#define FLECS_SCRIPT_MATH_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** Script math import function. + * Usage: + * @code + * ECS_IMPORT(world, FlecsScriptMath) + * @endcode + * + * @param world The world. + */ +FLECS_API +void FlecsScriptMathImport( + ecs_world_t *world); + +#ifdef __cplusplus +} +#endif + +#endif + +/** @} */ + +#endif diff --git a/include/flecs/private/addons.h b/include/flecs/private/addons.h index 6f2775bef..17945b5a2 100644 --- a/include/flecs/private/addons.h +++ b/include/flecs/private/addons.h @@ -18,6 +18,9 @@ #ifdef FLECS_NO_SCRIPT #undef FLECS_SCRIPT #endif +#ifdef FLECS_NO_SCRIPT_MATH +#undef FLECS_SCRIPT_MATH +#endif #ifdef FLECS_NO_STATS #undef FLECS_STATS #endif @@ -161,6 +164,13 @@ #include "../addons/units.h" #endif +#ifdef FLECS_SCRIPT_MATH +#ifdef FLECS_NO_SCRIPT_MATH +#error "FLECS_NO_SCRIPT_MATH failed: SCRIPT_MATH is required by other addons" +#endif +#include "../addons/script_math.h" +#endif + #ifdef FLECS_SCRIPT #ifdef FLECS_NO_SCRIPT #error "FLECS_NO_SCRIPT failed: SCRIPT is required by other addons" diff --git a/meson.build b/meson.build index 893a5c081..932bf7e6d 100644 --- a/meson.build +++ b/meson.build @@ -59,8 +59,9 @@ flecs_src = files( 'src/addons/rest.c', 'src/addons/script/template.c', 'src/addons/script/ast.c', - 'src/addons/script/builtin_functions.c', 'src/addons/script/function.c', + 'src/addons/script/functions_builtin.c', + 'src/addons/script/functions_math.c', 'src/addons/script/interpolate.c', 'src/addons/script/parser.c', 'src/addons/script/query_parser.c', diff --git a/project.json b/project.json index 85a1c2e79..53bb4643d 100644 --- a/project.json +++ b/project.json @@ -21,7 +21,7 @@ }, "lang.c": { "${os linux}": { - "lib": ["rt", "pthread"], + "lib": ["rt", "pthread", "m"], "${cfg debug}": { "export-symbols": true }, diff --git a/src/addons/script/expr/visit_type.c b/src/addons/script/expr/visit_type.c index f22b7ea46..8b642bde5 100644 --- a/src/addons/script/expr/visit_type.c +++ b/src/addons/script/expr/visit_type.c @@ -744,7 +744,8 @@ int flecs_expr_function_visit_type( try_function: if (!is_method) { - ecs_entity_t func = ecs_lookup(world, node->function_name); + ecs_entity_t func = desc->lookup_action( + world, node->function_name, desc->lookup_ctx); if (!func) { flecs_expr_visit_error(script, node, "unresolved function identifier '%s'", diff --git a/src/addons/script/builtin_functions.c b/src/addons/script/functions_builtin.c similarity index 98% rename from src/addons/script/builtin_functions.c rename to src/addons/script/functions_builtin.c index af4f0c18b..a855d8a22 100644 --- a/src/addons/script/builtin_functions.c +++ b/src/addons/script/functions_builtin.c @@ -1,6 +1,6 @@ /** * @file addons/script/builtin_functions.c - * @brief Builtin script functions. + * @brief Flecs functions for flecs script. */ #include "flecs.h" diff --git a/src/addons/script/functions_math.c b/src/addons/script/functions_math.c new file mode 100644 index 000000000..0a12186eb --- /dev/null +++ b/src/addons/script/functions_math.c @@ -0,0 +1,180 @@ +/** + * @file addons/script/functions_math.c + * @brief Math functions for flecs script. + */ + +#include "flecs.h" + +#ifdef FLECS_SCRIPT_MATH +#include "script.h" +#include + +#define FLECS_MATH_FUNC_F64(name, ...)\ + static\ + void flecs_math_##name(\ + const ecs_function_ctx_t *ctx,\ + int32_t argc,\ + const ecs_value_t *argv,\ + ecs_value_t *result)\ + {\ + (void)ctx;\ + (void)argc;\ + ecs_assert(argc == 1, ECS_INTERNAL_ERROR, NULL);\ + double x = *(double*)argv[0].ptr;\ + *(double*)result->ptr = __VA_ARGS__;\ + } + +#define FLECS_MATH_FUNC_F64_F64(name, ...)\ + static\ + void flecs_math_##name(\ + const ecs_function_ctx_t *ctx,\ + int32_t argc,\ + const ecs_value_t *argv,\ + ecs_value_t *result)\ + {\ + (void)ctx;\ + (void)argc;\ + ecs_assert(argc == 2, ECS_INTERNAL_ERROR, NULL);\ + double x = *(double*)argv[0].ptr;\ + double y = *(double*)argv[1].ptr;\ + *(double*)result->ptr = __VA_ARGS__;\ + } + +#define FLECS_MATH_FUNC_F64_I32(name, ...)\ + static\ + void flecs_math_##name(\ + const ecs_function_ctx_t *ctx,\ + int32_t argc,\ + const ecs_value_t *argv,\ + ecs_value_t *result)\ + {\ + (void)ctx;\ + (void)argc;\ + ecs_assert(argc == 2, ECS_INTERNAL_ERROR, NULL);\ + double x = *(double*)argv[0].ptr;\ + ecs_i32_t y = *(ecs_i32_t*)argv[1].ptr;\ + *(double*)result->ptr = __VA_ARGS__;\ + } + +#define FLECS_MATH_FUNC_DEF_F64(_name, brief)\ + {\ + ecs_entity_t f = ecs_script_function(world, {\ + .name = #_name,\ + .parent = ecs_id(FlecsScriptMath),\ + .return_type = ecs_id(ecs_f64_t),\ + .params = {{ .name = "x", .type = ecs_id(ecs_f64_t) }},\ + .callback = flecs_math_##_name\ + });\ + ecs_doc_set_brief(world, f, brief);\ + } + +#define FLECS_MATH_FUNC_DEF_F64_F64(_name, brief)\ + {\ + ecs_entity_t f = ecs_script_function(world, {\ + .name = #_name,\ + .parent = ecs_id(FlecsScriptMath),\ + .return_type = ecs_id(ecs_f64_t),\ + .params = {\ + { .name = "x", .type = ecs_id(ecs_f64_t) },\ + { .name = "y", .type = ecs_id(ecs_f64_t) }\ + },\ + .callback = flecs_math_##_name\ + });\ + ecs_doc_set_brief(world, f, brief);\ + } + +#define FLECS_MATH_FUNC_DEF_F64_F32(_name, brief)\ + {\ + ecs_entity_t f = ecs_script_function(world, {\ + .name = #_name,\ + .parent = ecs_id(FlecsScriptMath),\ + .return_type = ecs_id(ecs_f64_t),\ + .params = {\ + { .name = "x", .type = ecs_id(ecs_f64_t) },\ + { .name = "y", .type = ecs_id(ecs_i32_t) }\ + },\ + .callback = flecs_math_##_name\ + });\ + ecs_doc_set_brief(world, f, brief);\ + } + +/* Trigonometric functions */ +FLECS_MATH_FUNC_F64(cos, cos(x)) +FLECS_MATH_FUNC_F64(sin, sin(x)) +FLECS_MATH_FUNC_F64(tan, tan(x)) +FLECS_MATH_FUNC_F64(acos, acos(x)) +FLECS_MATH_FUNC_F64(asin, asin(x)) +FLECS_MATH_FUNC_F64(atan, atan(x)) +FLECS_MATH_FUNC_F64_F64(atan2, atan2(x, y)) + +/* Hyperbolic functions */ +FLECS_MATH_FUNC_F64(cosh, cosh(x)) +FLECS_MATH_FUNC_F64(sinh, sinh(x)) +FLECS_MATH_FUNC_F64(tanh, tanh(x)) +FLECS_MATH_FUNC_F64(acosh, acosh(x)) +FLECS_MATH_FUNC_F64(asinh, asinh(x)) +FLECS_MATH_FUNC_F64(atanh, atanh(x)) + +/* Exponential and logarithmic functions */ +FLECS_MATH_FUNC_F64(exp, exp(x)) +FLECS_MATH_FUNC_F64_I32(ldexp, ldexp(x, y)) +FLECS_MATH_FUNC_F64(log, log(x)) +FLECS_MATH_FUNC_F64(log10, log10(x)) +FLECS_MATH_FUNC_F64(exp2, exp2(x)) +FLECS_MATH_FUNC_F64(log2, log2(x)) + +/* Power functions */ +FLECS_MATH_FUNC_F64_F64(pow, pow(x, y)) +FLECS_MATH_FUNC_F64(sqrt, sqrt(x)) +FLECS_MATH_FUNC_F64(sqr, x * x) + +/* Rounding functions */ +FLECS_MATH_FUNC_F64(ceil, ceil(x)) +FLECS_MATH_FUNC_F64(floor, floor(x)) +FLECS_MATH_FUNC_F64(round, round(x)) + +FLECS_API +void FlecsScriptMathImport( + ecs_world_t *world) +{ + ECS_MODULE(world, FlecsScriptMath); + + ECS_IMPORT(world, FlecsScript); + + /* Trigonometric functions */ + FLECS_MATH_FUNC_DEF_F64(cos, "Compute cosine"); + FLECS_MATH_FUNC_DEF_F64(sin, "Compute sine"); + FLECS_MATH_FUNC_DEF_F64(tan, "Compute tangent"); + FLECS_MATH_FUNC_DEF_F64(acos, "Compute arc cosine"); + FLECS_MATH_FUNC_DEF_F64(asin, "Compute arc sine"); + FLECS_MATH_FUNC_DEF_F64(atan, "Compute arc tangent"); + FLECS_MATH_FUNC_DEF_F64_F64(atan2, "Compute arc tangent with two parameters"); + + /* Hyperbolic functions */ + FLECS_MATH_FUNC_DEF_F64(cosh, "Compute hyperbolic cosine"); + FLECS_MATH_FUNC_DEF_F64(sinh, "Compute hyperbolic sine"); + FLECS_MATH_FUNC_DEF_F64(tanh, "Compute hyperbolic tangent"); + FLECS_MATH_FUNC_DEF_F64(acosh, "Compute area hyperbolic cosine"); + FLECS_MATH_FUNC_DEF_F64(asinh, "Compute area hyperbolic sine"); + FLECS_MATH_FUNC_DEF_F64(atanh, "Compute area hyperbolic tangent"); + + /* Exponential and logarithmic functions */ + FLECS_MATH_FUNC_DEF_F64(exp, "Compute exponential function"); + FLECS_MATH_FUNC_DEF_F64_F32(ldexp, "Generate value from significand and exponent"); + FLECS_MATH_FUNC_DEF_F64(log, "Compute natural logarithm"); + FLECS_MATH_FUNC_DEF_F64(log10, "Compute common logarithm"); + FLECS_MATH_FUNC_DEF_F64(exp2, "Compute binary exponential function"); + FLECS_MATH_FUNC_DEF_F64(log2, "Compute binary logarithm"); + + /* Power functions */ + FLECS_MATH_FUNC_DEF_F64_F64(pow, "Raise to power"); + FLECS_MATH_FUNC_DEF_F64(sqrt, "Compute square root"); + FLECS_MATH_FUNC_DEF_F64(sqr, "Compute square"); + + /* Rounding functions */ + FLECS_MATH_FUNC_DEF_F64(ceil, "Round up value"); + FLECS_MATH_FUNC_DEF_F64(floor, "Round down value"); + FLECS_MATH_FUNC_DEF_F64(round, "Round to nearest"); +} + +#endif diff --git a/test/custom_builds/c/script/include/script.h b/test/custom_builds/c/script/include/script.h new file mode 100644 index 000000000..41a9a0352 --- /dev/null +++ b/test/custom_builds/c/script/include/script.h @@ -0,0 +1,16 @@ +#ifndef SCRIPT_H +#define SCRIPT_H + +/* This generated file contains includes for project dependencies */ +#include "script/bake_config.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/test/custom_builds/c/script/include/script/bake_config.h b/test/custom_builds/c/script/include/script/bake_config.h new file mode 100644 index 000000000..3969c3821 --- /dev/null +++ b/test/custom_builds/c/script/include/script/bake_config.h @@ -0,0 +1,24 @@ +/* + ) + (.) + .|. + | | + _.--| |--._ + .-'; ;`-'& ; `&. + \ & ; & &_/ + |"""---...---"""| + \ | | | | | | | / + `---.|.|.|.---' + + * This file is generated by bake.lang.c for your convenience. Headers of + * dependencies will automatically show up in this file. Include bake_config.h + * in your main project file. Do not edit! */ + +#ifndef SCRIPT_BAKE_CONFIG_H +#define SCRIPT_BAKE_CONFIG_H + +/* Headers of public dependencies */ +#include "../../deps/flecs.h" + +#endif + diff --git a/test/custom_builds/c/script/project.json b/test/custom_builds/c/script/project.json new file mode 100644 index 000000000..16c8f4c4e --- /dev/null +++ b/test/custom_builds/c/script/project.json @@ -0,0 +1,14 @@ +{ + "id": "script", + "type": "application", + "value": { + "public": false, + "use": [ + "flecs" + ], + "standalone": true + }, + "lang.c": { + "defines": ["FLECS_CUSTOM_BUILD", "FLECS_SCRIPT"] + } +} \ No newline at end of file diff --git a/test/custom_builds/c/script/src/main.c b/test/custom_builds/c/script/src/main.c new file mode 100644 index 000000000..6a208e7be --- /dev/null +++ b/test/custom_builds/c/script/src/main.c @@ -0,0 +1,12 @@ +#include +#include + +int main(int argc, char *argv[]) { + ecs_world_t *world = ecs_mini(); + (void)argc; + (void)argv; + + ECS_IMPORT(world, FlecsScript); + + return ecs_fini(world); +} diff --git a/test/custom_builds/c/script_math/include/script_math.h b/test/custom_builds/c/script_math/include/script_math.h new file mode 100644 index 000000000..86cc5c01f --- /dev/null +++ b/test/custom_builds/c/script_math/include/script_math.h @@ -0,0 +1,16 @@ +#ifndef SCRIPT_MATH_H +#define SCRIPT_MATH_H + +/* This generated file contains includes for project dependencies */ +#include "script_math/bake_config.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/test/custom_builds/c/script_math/include/script_math/bake_config.h b/test/custom_builds/c/script_math/include/script_math/bake_config.h new file mode 100644 index 000000000..ff5e59042 --- /dev/null +++ b/test/custom_builds/c/script_math/include/script_math/bake_config.h @@ -0,0 +1,24 @@ +/* + ) + (.) + .|. + | | + _.--| |--._ + .-'; ;`-'& ; `&. + \ & ; & &_/ + |"""---...---"""| + \ | | | | | | | / + `---.|.|.|.---' + + * This file is generated by bake.lang.c for your convenience. Headers of + * dependencies will automatically show up in this file. Include bake_config.h + * in your main project file. Do not edit! */ + +#ifndef SCRIPT_MATH_BAKE_CONFIG_H +#define SCRIPT_MATH_BAKE_CONFIG_H + +/* Headers of public dependencies */ +#include "../../deps/flecs.h" + +#endif + diff --git a/test/custom_builds/c/script_math/project.json b/test/custom_builds/c/script_math/project.json new file mode 100644 index 000000000..3605da0d4 --- /dev/null +++ b/test/custom_builds/c/script_math/project.json @@ -0,0 +1,14 @@ +{ + "id": "script_math", + "type": "application", + "value": { + "public": false, + "use": [ + "flecs" + ], + "standalone": true + }, + "lang.c": { + "defines": ["FLECS_CUSTOM_BUILD", "FLECS_SCRIPT_MATH"] + } +} \ No newline at end of file diff --git a/test/custom_builds/c/script_math/src/main.c b/test/custom_builds/c/script_math/src/main.c new file mode 100644 index 000000000..61e49bd1b --- /dev/null +++ b/test/custom_builds/c/script_math/src/main.c @@ -0,0 +1,37 @@ +#include +#include + +int main(int argc, char *argv[]) { + ecs_world_t *world = ecs_init_w_args(argc, argv); + + ECS_IMPORT(world, FlecsScriptMath); + + int32_t v = 0; + (void)v; + + assert(ecs_script_expr_run(world, "flecs.script.math.sqr(10)", + &ecs_value_ptr(ecs_i32_t, &v), NULL) != NULL); + assert(v == 100); + + assert(ecs_script_expr_run(world, "flecs.script.math.sqrt(100)", + &ecs_value_ptr(ecs_i32_t, &v), NULL) != NULL); + assert(v == 10); + + assert(ecs_script_expr_run(world, "flecs.script.math.pow(5, 2)", + &ecs_value_ptr(ecs_i32_t, &v), NULL) != NULL); + assert(v == 25); + + assert(ecs_script_expr_run(world, "flecs.script.math.ceil(1.6)", + &ecs_value_ptr(ecs_i32_t, &v), NULL) != NULL); + assert(v == 2); + + assert(ecs_script_expr_run(world, "flecs.script.math.floor(1.6)", + &ecs_value_ptr(ecs_i32_t, &v), NULL) != NULL); + assert(v == 1); + + assert(ecs_script_expr_run(world, "flecs.script.math.round(1.6)", + &ecs_value_ptr(ecs_i32_t, &v), NULL) != NULL); + assert(v == 2); + + return ecs_fini(world); +} diff --git a/test/script/project.json b/test/script/project.json index f2ef2c15d..3cb5f0894 100644 --- a/test/script/project.json +++ b/test/script/project.json @@ -264,7 +264,10 @@ "partial_assign_with_large_array", "non_trivial_var_component", "non_trivial_var_with", - "update_template_w_tag" + "update_template_w_tag", + "assign_call_func", + "assign_call_scoped_func", + "assign_call_scoped_func_w_using" ] }, { "id": "Template", diff --git a/test/script/src/Eval.c b/test/script/src/Eval.c index 7b4504a86..7148aaf39 100644 --- a/test/script/src/Eval.c +++ b/test/script/src/Eval.c @@ -8228,3 +8228,132 @@ void Eval_update_template_w_tag(void) { ecs_fini(world); } + +static +void func_sqr( + const ecs_function_ctx_t *ctx, + int argc, + const ecs_value_t *argv, + ecs_value_t *result) +{ + int32_t v = *(int32_t*)argv[0].ptr; + *(int32_t*)result->ptr = v * v; +} + +void Eval_assign_call_func(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_script_function(world, { + .name = "sqr", + .return_type = ecs_id(ecs_i32_t), + .params = {{ "x", ecs_id(ecs_i32_t) }}, + .callback = func_sqr, + }); + + const char *expr = + HEAD "Foo = Position: {sqr(2), sqr(3)}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + ecs_entity_t foo = ecs_lookup(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, 4); + test_int(ptr->y, 9); + + ecs_fini(world); +} + +void Eval_assign_call_scoped_func(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 parent = ecs_entity(world, { .name = "parent" }); + + ecs_script_function(world, { + .name = "sqr", + .parent = parent, + .return_type = ecs_id(ecs_i32_t), + .params = {{ "x", ecs_id(ecs_i32_t) }}, + .callback = func_sqr, + }); + + const char *expr = + HEAD "Foo = Position: {parent.sqr(2), parent.sqr(3)}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + ecs_entity_t foo = ecs_lookup(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, 4); + test_int(ptr->y, 9); + + ecs_fini(world); +} + +void Eval_assign_call_scoped_func_w_using(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 parent = ecs_entity(world, { .name = "parent" }); + + ecs_script_function(world, { + .name = "sqr", + .parent = parent, + .return_type = ecs_id(ecs_i32_t), + .params = {{ "x", ecs_id(ecs_i32_t) }}, + .callback = func_sqr, + }); + + const char *expr = + HEAD "using parent" + LINE "Foo = Position: {sqr(2), sqr(3)}"; + + test_assert(ecs_script_run(world, NULL, expr) == 0); + ecs_entity_t foo = ecs_lookup(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, 4); + test_int(ptr->y, 9); + + ecs_fini(world); +} diff --git a/test/script/src/main.c b/test/script/src/main.c index ad776e436..51d71ad0d 100644 --- a/test/script/src/main.c +++ b/test/script/src/main.c @@ -261,6 +261,9 @@ void Eval_partial_assign_with_large_array(void); void Eval_non_trivial_var_component(void); void Eval_non_trivial_var_with(void); void Eval_update_template_w_tag(void); +void Eval_assign_call_func(void); +void Eval_assign_call_scoped_func(void); +void Eval_assign_call_scoped_func_w_using(void); // Testsuite 'Template' void Template_template_no_scope(void); @@ -1724,6 +1727,18 @@ bake_test_case Eval_testcases[] = { { "update_template_w_tag", Eval_update_template_w_tag + }, + { + "assign_call_func", + Eval_assign_call_func + }, + { + "assign_call_scoped_func", + Eval_assign_call_scoped_func + }, + { + "assign_call_scoped_func_w_using", + Eval_assign_call_scoped_func_w_using } }; @@ -3519,7 +3534,7 @@ static bake_test_suite suites[] = { "Eval", NULL, NULL, - 252, + 255, Eval_testcases }, {