Skip to content

Commit

Permalink
Reimplement string interpolation for expression parser
Browse files Browse the repository at this point in the history
  • Loading branch information
SanderMertens committed Dec 13, 2024
1 parent ad1d496 commit 4388be9
Show file tree
Hide file tree
Showing 28 changed files with 1,664 additions and 419 deletions.
781 changes: 577 additions & 204 deletions distr/flecs.c

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion distr/flecs.h
Original file line number Diff line number Diff line change
Expand Up @@ -14776,7 +14776,7 @@ typedef struct ecs_expr_eval_desc_t {
const char *value,
void *ctx);
void *lookup_ctx; /**< Context passed to lookup function */
ecs_script_vars_t *vars; /**< Variables accessible in expression */
const ecs_script_vars_t *vars; /**< Variables accessible in expression */
ecs_entity_t type; /**< Type of parsed value (optional) */
bool disable_folding; /**< Disable constant folding (slower evaluation, faster parsing) */
ecs_script_runtime_t *runtime; /**< Reusable runtime (optional) */
Expand Down
2 changes: 1 addition & 1 deletion include/flecs/addons/script.h
Original file line number Diff line number Diff line change
Expand Up @@ -502,7 +502,7 @@ typedef struct ecs_expr_eval_desc_t {
const char *value,
void *ctx);
void *lookup_ctx; /**< Context passed to lookup function */
ecs_script_vars_t *vars; /**< Variables accessible in expression */
const ecs_script_vars_t *vars; /**< Variables accessible in expression */
ecs_entity_t type; /**< Type of parsed value (optional) */
bool disable_folding; /**< Disable constant folding (slower evaluation, faster parsing) */
ecs_script_runtime_t *runtime; /**< Reusable runtime (optional) */
Expand Down
1 change: 0 additions & 1 deletion meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@ flecs_src = files(
'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',
'src/addons/script/script.c',
Expand Down
50 changes: 42 additions & 8 deletions src/addons/meta/cursor.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*/

#include "meta.h"
#include <inttypes.h>
#include <ctype.h>

#ifdef FLECS_META
Expand Down Expand Up @@ -890,6 +891,16 @@ int ecs_meta_set_bool(
cases_T_bool(ptr, value);
cases_T_signed(ptr, value, ecs_meta_bounds_signed);
cases_T_unsigned(ptr, value, ecs_meta_bounds_unsigned);
case EcsOpString: {
char *result;
if (value) {
result = ecs_os_strdup("true");
} else {
result = ecs_os_strdup("false");
}
set_T(ecs_string_t, ptr, result);
break;
}
case EcsOpOpaque: {
const EcsOpaque *ot = ecs_get(cursor->world, op->type, EcsOpaque);
if (ot && ot->assign_bool) {
Expand All @@ -906,7 +917,6 @@ int ecs_meta_set_bool(
case EcsOpPrimitive:
case EcsOpF32:
case EcsOpF64:
case EcsOpString:
flecs_meta_conversion_error(cursor, op, "bool");
return -1;
default:
Expand All @@ -929,6 +939,11 @@ int ecs_meta_set_char(
switch(op->kind) {
cases_T_bool(ptr, value);
cases_T_signed(ptr, value, ecs_meta_bounds_signed);
case EcsOpString: {
char *result = flecs_asprintf("%c", value);
set_T(ecs_string_t, ptr, result);
break;
}
case EcsOpOpaque: {
const EcsOpaque *opaque = ecs_get(cursor->world, op->type, EcsOpaque);
ecs_assert(opaque != NULL, ECS_INVALID_OPERATION,
Expand Down Expand Up @@ -961,7 +976,6 @@ int ecs_meta_set_char(
case EcsOpF32:
case EcsOpF64:
case EcsOpUPtr:
case EcsOpString:
case EcsOpEntity:
case EcsOpId:
flecs_meta_conversion_error(cursor, op, "char");
Expand All @@ -988,6 +1002,11 @@ int ecs_meta_set_int(
cases_T_signed(ptr, value, ecs_meta_bounds_signed);
cases_T_unsigned(ptr, value, ecs_meta_bounds_signed);
cases_T_float(ptr, value);
case EcsOpString: {
char *result = flecs_asprintf("%"PRId64, value);
set_T(ecs_string_t, ptr, result);
break;
}
case EcsOpOpaque: {
const EcsOpaque *opaque = ecs_get(cursor->world, op->type, EcsOpaque);
ecs_assert(opaque != NULL, ECS_INVALID_OPERATION,
Expand All @@ -1013,8 +1032,7 @@ int ecs_meta_set_int(
case EcsOpPush:
case EcsOpPop:
case EcsOpScope:
case EcsOpPrimitive:
case EcsOpString: {
case EcsOpPrimitive: {
if(!value) return ecs_meta_set_null(cursor);
flecs_meta_conversion_error(cursor, op, "int");
return -1;
Expand All @@ -1041,6 +1059,11 @@ int ecs_meta_set_uint(
cases_T_signed(ptr, value, ecs_meta_bounds_unsigned);
cases_T_unsigned(ptr, value, ecs_meta_bounds_unsigned);
cases_T_float(ptr, value);
case EcsOpString: {
char *result = flecs_asprintf("%"PRIu64, value);
set_T(ecs_string_t, ptr, result);
break;
}
case EcsOpOpaque: {
const EcsOpaque *opaque = ecs_get(cursor->world, op->type, EcsOpaque);
ecs_assert(opaque != NULL, ECS_INVALID_OPERATION,
Expand All @@ -1067,7 +1090,6 @@ int ecs_meta_set_uint(
case EcsOpPop:
case EcsOpScope:
case EcsOpPrimitive:
case EcsOpString:
if(!value) return ecs_meta_set_null(cursor);
flecs_meta_conversion_error(cursor, op, "uint");
return -1;
Expand Down Expand Up @@ -1099,6 +1121,11 @@ int ecs_meta_set_float(
cases_T_signed(ptr, value, ecs_meta_bounds_float);
cases_T_unsigned(ptr, value, ecs_meta_bounds_float);
cases_T_float(ptr, value);
case EcsOpString: {
char *result = flecs_asprintf("%f", value);
set_T(ecs_string_t, ptr, result);
break;
}
case EcsOpOpaque: {
const EcsOpaque *opaque = ecs_get(cursor->world, op->type, EcsOpaque);
ecs_assert(opaque != NULL, ECS_INVALID_OPERATION,
Expand Down Expand Up @@ -1129,7 +1156,6 @@ int ecs_meta_set_float(
case EcsOpPop:
case EcsOpScope:
case EcsOpPrimitive:
case EcsOpString:
flecs_meta_conversion_error(cursor, op, "float");
return -1;
default:
Expand Down Expand Up @@ -1567,6 +1593,11 @@ int ecs_meta_set_entity(
case EcsOpId:
set_T(ecs_id_t, ptr, value); /* entities are valid ids */
break;
case EcsOpString: {
char *result = ecs_get_path(cursor->world, value);
set_T(ecs_string_t, ptr, result);
break;
}
case EcsOpOpaque: {
const EcsOpaque *opaque = ecs_get(cursor->world, op->type, EcsOpaque);
if (opaque && opaque->assign_entity) {
Expand Down Expand Up @@ -1599,7 +1630,6 @@ int ecs_meta_set_entity(
case EcsOpF64:
case EcsOpUPtr:
case EcsOpIPtr:
case EcsOpString:
flecs_meta_conversion_error(cursor, op, "entity");
goto error;
default:
Expand All @@ -1624,6 +1654,11 @@ int ecs_meta_set_id(
case EcsOpId:
set_T(ecs_id_t, ptr, value);
break;
case EcsOpString: {
char *result = ecs_id_str(cursor->world, value);
set_T(ecs_string_t, ptr, result);
break;
}
case EcsOpOpaque: {
const EcsOpaque *opaque = ecs_get(cursor->world, op->type, EcsOpaque);
if (opaque && opaque->assign_id) {
Expand Down Expand Up @@ -1656,7 +1691,6 @@ int ecs_meta_set_id(
case EcsOpF64:
case EcsOpUPtr:
case EcsOpIPtr:
case EcsOpString:
case EcsOpEntity:
flecs_meta_conversion_error(cursor, op, "id");
goto error;
Expand Down
2 changes: 1 addition & 1 deletion src/addons/script/ast.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ typedef enum ecs_script_node_kind_t {
EcsAstConst,
EcsAstEntity,
EcsAstPairScope,
EcsAstIf,
EcsAstIf
} ecs_script_node_kind_t;

typedef struct ecs_script_node_t {
Expand Down
38 changes: 37 additions & 1 deletion src/addons/script/expr/ast.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,19 @@ ecs_expr_value_node_t* flecs_expr_value_from(
return result;
}

ecs_expr_variable_t* flecs_expr_variable_from(
ecs_script_t *script,
ecs_expr_node_t *node,
const char *name)
{
ecs_expr_variable_t *result = flecs_calloc_t(
&((ecs_script_impl_t*)script)->allocator, ecs_expr_variable_t);
result->name = name;
result->node.kind = EcsExprVariable;
result->node.pos = node ? node->pos : NULL;
return result;
}

ecs_expr_value_node_t* flecs_expr_bool(
ecs_script_parser_t *parser,
bool value)
Expand Down Expand Up @@ -93,11 +106,34 @@ ecs_expr_value_node_t* flecs_expr_string(
ecs_script_parser_t *parser,
const char *value)
{
char *str = ECS_CONST_CAST(char*, value);
ecs_expr_value_node_t *result = flecs_expr_ast_new(
parser, ecs_expr_value_node_t, EcsExprValue);
result->storage.string = ECS_CONST_CAST(char*, value);
result->storage.string = str;
result->ptr = &result->storage.string;
result->node.type = ecs_id(ecs_string_t);

if (!flecs_string_escape(str)) {
return NULL;
}

return result;
}

ecs_expr_interpolated_string_t* flecs_expr_interpolated_string(
ecs_script_parser_t *parser,
const char *value)
{
ecs_expr_interpolated_string_t *result = flecs_expr_ast_new(
parser, ecs_expr_interpolated_string_t, EcsExprInterpolatedString);
result->value = ECS_CONST_CAST(char*, value);
result->buffer = flecs_strdup(&parser->script->allocator, value);
result->buffer_size = ecs_os_strlen(result->buffer) + 1;
result->node.type = ecs_id(ecs_string_t);
ecs_vec_init_t(&parser->script->allocator, &result->fragments, char*, 0);
ecs_vec_init_t(&parser->script->allocator, &result->expressions,
ecs_expr_node_t*, 0);

return result;
}

Expand Down
19 changes: 19 additions & 0 deletions src/addons/script/expr/ast.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

typedef enum ecs_expr_node_kind_t {
EcsExprValue,
EcsExprInterpolatedString,
EcsExprInitializer,
EcsExprEmptyInitializer,
EcsExprUnary,
Expand Down Expand Up @@ -38,6 +39,15 @@ typedef struct ecs_expr_value_node_t {
ecs_expr_small_value_t storage;
} ecs_expr_value_node_t;

typedef struct ecs_expr_interpolated_string_t {
ecs_expr_node_t node;
char *value; /* modified by parser */
char *buffer; /* for storing expr tokens */
ecs_size_t buffer_size;
ecs_vec_t fragments; /* vec<char*> */
ecs_vec_t expressions; /* vec<ecs_expr_node_t*> */
} ecs_expr_interpolated_string_t;

typedef struct ecs_expr_initializer_element_t {
const char *member;
ecs_expr_node_t *value;
Expand Down Expand Up @@ -115,6 +125,11 @@ ecs_expr_value_node_t* flecs_expr_value_from(
ecs_expr_node_t *node,
ecs_entity_t type);

ecs_expr_variable_t* flecs_expr_variable_from(
ecs_script_t *script,
ecs_expr_node_t *node,
const char *name);

ecs_expr_value_node_t* flecs_expr_bool(
ecs_script_parser_t *parser,
bool value);
Expand All @@ -135,6 +150,10 @@ ecs_expr_value_node_t* flecs_expr_string(
ecs_script_parser_t *parser,
const char *value);

ecs_expr_interpolated_string_t* flecs_expr_interpolated_string(
ecs_script_parser_t *parser,
const char *value);

ecs_expr_value_node_t* flecs_expr_entity(
ecs_script_parser_t *parser,
ecs_entity_t value);
Expand Down
6 changes: 6 additions & 0 deletions src/addons/script/expr/expr.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,12 @@ void flecs_expr_to_str_buf(
ecs_strbuf_t *buf,
bool colors);

bool flecs_string_is_interpolated(
const char *str);

char* flecs_string_escape(
char *str);

#define ECS_VALUE_GET(value, T) (*(T*)(value)->ptr)

#define ECS_BOP(left, right, result, op, R, T)\
Expand Down
41 changes: 38 additions & 3 deletions src/addons/script/expr/parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,12 @@ const char* flecs_script_parse_lhs(
}

case EcsTokString: {
*out = (ecs_expr_node_t*)flecs_expr_string(parser, Token(0));
if (flecs_string_is_interpolated(Token(0))) {
*out = (ecs_expr_node_t*)flecs_expr_interpolated_string(
parser, Token(0));
} else {
*out = (ecs_expr_node_t*)flecs_expr_string(parser, Token(0));
}
break;
}

Expand All @@ -375,7 +380,7 @@ const char* flecs_script_parse_lhs(
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_memmove(&last_elem[1], &last_elem[2],
ecs_os_strlen(&last_elem[2]) + 1);
v->node.kind = EcsExprGlobalVariable;
*out = (ecs_expr_node_t*)v;
Expand Down Expand Up @@ -531,6 +536,7 @@ ecs_script_t* ecs_expr_parse(
}

impl->next_token = ptr;
impl->token_remaining = parser.token_cur;

if (flecs_expr_visit_type(script, impl->expr, &priv_desc)) {
goto error;
Expand All @@ -544,7 +550,7 @@ ecs_script_t* ecs_expr_parse(
}
}

// printf("%s\n", ecs_script_ast_to_str(script));
// printf("%s\n", ecs_script_ast_to_str(script, true));

return script;
error:
Expand Down Expand Up @@ -619,4 +625,33 @@ const char* ecs_expr_run(
return NULL;
}

FLECS_API
char* ecs_script_string_interpolate(
ecs_world_t *world,
const char *str,
const ecs_script_vars_t *vars)
{
if (!flecs_string_is_interpolated(str)) {
char *result = ecs_os_strdup(str);
if (!flecs_string_escape(result)) {
ecs_os_free(result);
return NULL;
}
return result;
}

char *expr = flecs_asprintf("\"%s\"", str);

ecs_expr_eval_desc_t desc = { .vars = vars };
char *r = NULL;
if (!ecs_expr_run(world, expr, &ecs_value_ptr(ecs_string_t, &r), &desc)) {
ecs_os_free(expr);
return NULL;
}

ecs_os_free(expr);

return r;
}

#endif
Loading

0 comments on commit 4388be9

Please sign in to comment.