diff --git a/flecs.c b/flecs.c index 860ff2c0f2..2efe0e2c30 100644 --- a/flecs.c +++ b/flecs.c @@ -25981,6 +25981,7 @@ void FlecsMetaImport( #endif +#include #ifdef FLECS_META @@ -26672,7 +26673,7 @@ int ecs_meta_set_value( case EcsI16: return ecs_meta_set_int(cursor, *(int16_t*)value->ptr); case EcsI32: return ecs_meta_set_int(cursor, *(int32_t*)value->ptr); case EcsI64: return ecs_meta_set_int(cursor, *(int64_t*)value->ptr); - case EcsF32: return ecs_meta_set_float(cursor, *(float*)value->ptr); + case EcsF32: return ecs_meta_set_float(cursor, (double)*(float*)value->ptr); case EcsF64: return ecs_meta_set_float(cursor, *(double*)value->ptr); case EcsUPtr: return ecs_meta_set_uint(cursor, *(uintptr_t*)value->ptr); case EcsIPtr: return ecs_meta_set_int(cursor, *(intptr_t*)value->ptr); @@ -26784,6 +26785,12 @@ int ecs_meta_set_string( set_T(ecs_bool_t, ptr, true); } else if (!ecs_os_strcmp(value, "false")) { set_T(ecs_bool_t, ptr, false); + } else if (isdigit(value[0])) { + if (!ecs_os_strcmp(value, "0")) { + set_T(ecs_bool_t, ptr, false); + } else { + set_T(ecs_bool_t, ptr, true); + } } else { ecs_err("invalid value for boolean '%s'", value); return -1; @@ -27726,8 +27733,8 @@ ecs_expr_var_t* ecs_vars_declare( var->value.type = type; var->name = flecs_strdup(&vars->world->allocator, name); - flecs_name_index_ensure(var_index, ecs_vec_count(&scope->vars), - var->name, 0, 0); + flecs_name_index_ensure(var_index, + flecs_ito(uint64_t, ecs_vec_count(&scope->vars)), var->name, 0, 0); return var; error: return NULL; @@ -27755,8 +27762,8 @@ ecs_expr_var_t* ecs_vars_declare_w_value( var->name = flecs_strdup(&vars->world->allocator, name); value->ptr = NULL; /* Take ownership, prevent double free */ - flecs_name_index_ensure(var_index, ecs_vec_count(&scope->vars), - var->name, 0, 0); + flecs_name_index_ensure(var_index, + flecs_ito(uint64_t, ecs_vec_count(&scope->vars)), var->name, 0, 0); return var; error: return NULL; @@ -27961,11 +27968,85 @@ char* ecs_astresc( #endif - #include #ifdef FLECS_EXPR +/* String deserializer for values & simple expressions */ + +/* Order in enumeration is important, as it is used for precedence */ +typedef enum ecs_expr_oper_t { + EcsExprOperUnknown, + EcsLeftParen, + EcsCondAnd, + EcsCondOr, + EcsCondEq, + EcsCondNeq, + EcsCondGt, + EcsCondGtEq, + EcsCondLt, + EcsCondLtEq, + EcsAdd, + EcsSub, + EcsMul, + EcsDiv, +} ecs_expr_oper_t; + +static +const char* flecs_parse_expr( + ecs_world_t *world, + const char *ptr, + ecs_value_t *value, + ecs_expr_oper_t op, + const ecs_parse_expr_desc_t *desc); + +static +const char* flecs_str_to_expr_oper( + const char *str, + ecs_expr_oper_t *op) +{ + if (!ecs_os_strncmp(str, "+", 1)) { + *op = EcsAdd; + return str + 1; + } else if (!ecs_os_strncmp(str, "-", 1)) { + *op = EcsSub; + return str + 1; + } else if (!ecs_os_strncmp(str, "*", 1)) { + *op = EcsMul; + return str + 1; + } else if (!ecs_os_strncmp(str, "/", 1)) { + *op = EcsDiv; + return str + 1; + } else if (!ecs_os_strncmp(str, "&&", 2)) { + *op = EcsCondAnd; + return str + 2; + } else if (!ecs_os_strncmp(str, "||", 2)) { + *op = EcsCondOr; + return str + 2; + } else if (!ecs_os_strncmp(str, "==", 2)) { + *op = EcsCondEq; + return str + 2; + } else if (!ecs_os_strncmp(str, "!=", 2)) { + *op = EcsCondNeq; + return str + 2; + } else if (!ecs_os_strncmp(str, ">=", 2)) { + *op = EcsCondGtEq; + return str + 2; + } else if (!ecs_os_strncmp(str, "<=", 2)) { + *op = EcsCondLtEq; + return str + 2; + } else if (!ecs_os_strncmp(str, ">", 1)) { + *op = EcsCondGt; + return str + 1; + } else if (!ecs_os_strncmp(str, "<", 1)) { + *op = EcsCondLt; + return str + 1; + } + + *op = EcsExprOperUnknown; + return NULL; +} + const char *ecs_parse_expr_token( const char *name, const char *expr, @@ -27975,8 +28056,21 @@ const char *ecs_parse_expr_token( const char *start = ptr; char *token_ptr = token; + ecs_expr_oper_t op; + if (ptr[0] == '(') { + token[0] = '('; + token[1] = 0; + return ptr + 1; + } else if (ptr[0] != '-') { + const char *tptr = flecs_str_to_expr_oper(ptr, &op); + if (tptr) { + ecs_os_strncpy(token, ptr, tptr - ptr); + return tptr; + } + } + while ((ptr = ecs_parse_token(name, expr, ptr, token_ptr))) { - if (ptr[0] == '|') { + if (ptr[0] == '|' && ptr[1] != '|') { token_ptr = &token_ptr[ptr - start]; token_ptr[0] = '|'; token_ptr[1] = '\0'; @@ -28043,150 +28137,595 @@ bool flecs_parse_is_float( return false; } +/* Determine the type of an expression from the first character(s). This allows + * us to initialize a storage for a type if none was provided. */ static -int flecs_parse_discover_type( - ecs_world_t *world, +ecs_entity_t flecs_parse_discover_type( const char *name, const char *expr, const char *ptr, - ecs_value_t *value, + ecs_entity_t input_type, const ecs_parse_expr_desc_t *desc) { /* String literal */ if (ptr[0] == '"' || ptr[0] == '`') { - value->type = ecs_id(ecs_string_t); - value->ptr = ecs_value_new_t(world, ecs_string_t); - return 0; + if (input_type == ecs_id(ecs_char_t)) { + return input_type; + } + return ecs_id(ecs_string_t); } - + /* Negative number literal */ if (ptr[0] == '-') { if (!isdigit(ptr[1])) { ecs_parser_error(name, expr, ptr - expr, "invalid literal"); - return -1; + return 0; } if (flecs_parse_is_float(ptr + 1)) { - value->type = ecs_id(ecs_f64_t); - value->ptr = ecs_value_new_t(world, ecs_f64_t); - return 0; + return ecs_id(ecs_f64_t); } else { - value->type = ecs_id(ecs_i64_t); - value->ptr = ecs_value_new_t(world, ecs_i64_t); - return 0; + return ecs_id(ecs_i64_t); } } /* Positive number literal */ if (isdigit(ptr[0])) { if (flecs_parse_is_float(ptr)) { - value->type = ecs_id(ecs_f64_t); - value->ptr = ecs_value_new_t(world, ecs_f64_t); - return 0; + return ecs_id(ecs_f64_t); } else { - value->type = ecs_id(ecs_u64_t); - value->ptr = ecs_value_new_t(world, ecs_u64_t); - return 0; + return ecs_id(ecs_u64_t); } } /* Variable */ if (ptr[0] == '$') { - const ecs_expr_var_t *var = ecs_vars_lookup(desc->vars, &ptr[1]); + if (!desc || !desc->vars) { + ecs_parser_error(name, expr, ptr - expr, + "unresolved variable (no variable scope)"); + return 0; + } + char token[ECS_MAX_TOKEN_SIZE]; + if (ecs_parse_expr_token(name, expr, &ptr[1], token) == NULL) { + return 0; + } + const ecs_expr_var_t *var = ecs_vars_lookup(desc->vars, token); if (!var) { - ecs_parser_error(name, expr, ptr - expr, "unresolved variable"); - return -1; + ecs_parser_error(name, expr, ptr - expr, + "unresolved variable '%s'", token); + return 0; } - value->type = var->value.type; - value->ptr = ecs_value_new(world, value->type); - return 0; + return var->value.type; } /* Boolean */ if (ptr[0] == 't' && !ecs_os_strncmp(ptr, "true", 4)) { if (!isalpha(ptr[4]) && ptr[4] != '_') { - value->type = ecs_id(ecs_bool_t); - value->ptr = ecs_value_new_t(world, ecs_bool_t); - return 0; + return ecs_id(ecs_bool_t); } } if (ptr[0] == 'f' && !ecs_os_strncmp(ptr, "false", 5)) { if (!isalpha(ptr[5]) && ptr[5] != '_') { - value->type = ecs_id(ecs_bool_t); - value->ptr = ecs_value_new_t(world, ecs_bool_t); - return 0; + return ecs_id(ecs_bool_t); } } /* Entity identifier */ if (isalpha(ptr[0])) { - value->type = ecs_id(ecs_entity_t); - value->ptr = ecs_value_new_t(world, ecs_entity_t); + if (!input_type) { /* Identifier could also be enum/bitmask constant */ + return ecs_id(ecs_entity_t); + } + } + + /* If no default type was provided we can't automatically deduce the type of + * composite/collection expressions. */ + if (!input_type) { + if (ptr[0] == '{') { + ecs_parser_error(name, expr, ptr - expr, + "unknown type for composite literal"); + return 0; + } + + if (ptr[0] == '[') { + ecs_parser_error(name, expr, ptr - expr, + "unknown type for collection literal"); + return 0; + } + + ecs_parser_error(name, expr, ptr - expr, "invalid expression"); + } + + return input_type; +} + +/* Normalize types to their largest representation. + * Rather than taking the original type of a value, use the largest + * representation of the type so we don't have to worry about overflowing the + * original type in the operation. */ +static +ecs_entity_t flecs_largest_type( + const EcsPrimitive *type) +{ + switch(type->kind) { + case EcsBool: return ecs_id(ecs_bool_t); + case EcsChar: return ecs_id(ecs_char_t); + case EcsByte: return ecs_id(ecs_u8_t); + case EcsU8: return ecs_id(ecs_u64_t); + case EcsU16: return ecs_id(ecs_u64_t); + case EcsU32: return ecs_id(ecs_u64_t); + case EcsU64: return ecs_id(ecs_u64_t); + case EcsI8: return ecs_id(ecs_i64_t); + case EcsI16: return ecs_id(ecs_i64_t); + case EcsI32: return ecs_id(ecs_i64_t); + case EcsI64: return ecs_id(ecs_i64_t); + case EcsF32: return ecs_id(ecs_f64_t); + case EcsF64: return ecs_id(ecs_f64_t); + case EcsUPtr: return ecs_id(ecs_u64_t); + case EcsIPtr: return ecs_id(ecs_i64_t); + case EcsString: return ecs_id(ecs_string_t); + case EcsEntity: return ecs_id(ecs_entity_t); + default: ecs_abort(ECS_INTERNAL_ERROR, NULL); + } +} + +/** Test if a normalized type can promote to another type in an expression */ +static +bool flecs_is_number( + ecs_entity_t type) +{ + if (type == ecs_id(ecs_bool_t)) return false; + else if (type == ecs_id(ecs_char_t)) return false; + else if (type == ecs_id(ecs_u8_t)) return false; + else if (type == ecs_id(ecs_u64_t)) return true; + else if (type == ecs_id(ecs_i64_t)) return true; + else if (type == ecs_id(ecs_f64_t)) return true; + else if (type == ecs_id(ecs_string_t)) return false; + else if (type == ecs_id(ecs_entity_t)) return false; + else return false; +} + +static +bool flecs_oper_valid_for_type( + ecs_entity_t type, + ecs_expr_oper_t op) +{ + switch(op) { + case EcsAdd: + case EcsSub: + case EcsMul: + case EcsDiv: + return flecs_is_number(type); + case EcsCondEq: + case EcsCondNeq: + case EcsCondAnd: + case EcsCondOr: + case EcsCondGt: + case EcsCondGtEq: + case EcsCondLt: + case EcsCondLtEq: + return flecs_is_number(type) || + (type == ecs_id(ecs_bool_t)) || + (type == ecs_id(ecs_char_t)) || + (type == ecs_id(ecs_entity_t)); + default: + return false; + } +} + +/** Promote type to most expressive (f64 > i64 > u64) */ +static +ecs_entity_t flecs_promote_type( + ecs_entity_t type, + ecs_entity_t promote_to) +{ + if (type == ecs_id(ecs_u64_t)) { + return promote_to; + } + if (promote_to == ecs_id(ecs_u64_t)) { + return type; + } + if (type == ecs_id(ecs_f64_t)) { + return type; + } + if (promote_to == ecs_id(ecs_f64_t)) { + return promote_to; + } + return ecs_id(ecs_i64_t); +} + +static +int flecs_oper_precedence( + ecs_expr_oper_t left, + ecs_expr_oper_t right) +{ + return (left > right) - (left < right); +} + +static +void flecs_value_cast( + ecs_world_t *world, + ecs_value_t *value, + ecs_entity_t type) +{ + if (value->type == type) { + return; + } + + ecs_value_t result; + result.type = type; + result.ptr = ecs_value_new(world, type); + + if (value->ptr) { + ecs_meta_cursor_t cur = ecs_meta_cursor(world, type, result.ptr); + ecs_meta_set_value(&cur, value); + ecs_value_free(world, value->type, value->ptr); + } + + *value = result; +} + +static +bool flecs_expr_op_is_equality( + ecs_expr_oper_t op) +{ + switch(op) { + case EcsCondEq: + case EcsCondNeq: + case EcsCondGt: + case EcsCondGtEq: + case EcsCondLt: + case EcsCondLtEq: + return true; + default: + return false; + } +} + +static +ecs_entity_t flecs_binary_expr_type( + ecs_world_t *world, + const char *name, + const char *expr, + const char *ptr, + ecs_value_t *lvalue, + ecs_value_t *rvalue, + ecs_expr_oper_t op, + ecs_entity_t *operand_type_out) +{ + ecs_entity_t result_type = 0, operand_type = 0; + + switch(op) { + case EcsDiv: + /* Result type of a division is always a float */ + *operand_type_out = ecs_id(ecs_f64_t); + return ecs_id(ecs_f64_t); + case EcsCondAnd: + case EcsCondOr: + /* Result type of a condition operator is always a bool */ + *operand_type_out = ecs_id(ecs_bool_t); + return ecs_id(ecs_bool_t); + case EcsCondEq: + case EcsCondNeq: + case EcsCondGt: + case EcsCondGtEq: + case EcsCondLt: + case EcsCondLtEq: + /* Result type of equality operator is always bool, but operand types + * should not be casted to bool */ + result_type = ecs_id(ecs_bool_t); + break; + default: + break; + } + + /* Result type for arithmetic operators is determined by operands */ + const EcsPrimitive *ltype_ptr = ecs_get(world, lvalue->type, EcsPrimitive); + const EcsPrimitive *rtype_ptr = ecs_get(world, rvalue->type, EcsPrimitive); + if (!ltype_ptr || !rtype_ptr) { + ecs_parser_error(name, expr, ptr - expr, + "invalid non-primitive type in binary expression"); return 0; } - if (ptr[0] == '{') { - ecs_parser_error(name, expr, ptr - expr, - "unknown type for composite literal"); + ecs_entity_t ltype = flecs_largest_type(ltype_ptr); + ecs_entity_t rtype = flecs_largest_type(rtype_ptr); + if (ltype == rtype) { + operand_type = ltype; + goto done; + } + + if (flecs_expr_op_is_equality(op)) { + ecs_parser_error(name, expr, ptr - expr, + "mismatching types in equality expression"); + return 0; + } + + if (!flecs_is_number(ltype) || !flecs_is_number(rtype)) { + ecs_parser_error(name, expr, ptr - expr, + "incompatible types in binary expression"); + return 0; + } + + operand_type = flecs_promote_type(ltype, rtype); + +done: + if (op == EcsSub && operand_type == ecs_id(ecs_u64_t)) { + /* Result of subtracting two unsigned ints can be negative */ + operand_type = ecs_id(ecs_i64_t); + } + + if (!result_type) { + result_type = operand_type; + } + + *operand_type_out = operand_type; + return result_type; +} + +/* Macro's to let the compiler do the operations & conversion work for us */ + +#define ECS_VALUE_GET(value, T) (*(T*)value->ptr) + +#define ECS_BINARY_OP_T(left, right, result, op, R, T)\ + ECS_VALUE_GET(result, R) = ECS_VALUE_GET(left, T) op ECS_VALUE_GET(right, T) + +#define ECS_BINARY_OP(left, right, result, op)\ + if (left->type == ecs_id(ecs_u64_t)) { \ + ECS_BINARY_OP_T(left, right, result, op, ecs_u64_t, ecs_u64_t);\ + } else if (left->type == ecs_id(ecs_i64_t)) { \ + ECS_BINARY_OP_T(left, right, result, op, ecs_i64_t, ecs_i64_t);\ + } else if (left->type == ecs_id(ecs_f64_t)) { \ + ECS_BINARY_OP_T(left, right, result, op, ecs_f64_t, ecs_f64_t);\ + } else if (left->type == ecs_id(ecs_u8_t)) { \ + ECS_BINARY_OP_T(left, right, result, op, ecs_u8_t, ecs_u8_t);\ + } else if (left->type == ecs_id(ecs_char_t)) { \ + ECS_BINARY_OP_T(left, right, result, op, ecs_char_t, ecs_char_t);\ + } else if (left->type == ecs_id(ecs_bool_t)) { \ + ECS_BINARY_OP_T(left, right, result, op, ecs_bool_t, ecs_bool_t);\ + } else {\ + ecs_abort(ECS_INTERNAL_ERROR, "unexpected type in binary expression");\ + } + +#define ECS_BINARY_COND_OP(left, right, result, op)\ + if (left->type == ecs_id(ecs_u64_t)) { \ + ECS_BINARY_OP_T(left, right, result, op, ecs_bool_t, ecs_u64_t);\ + } else if (left->type == ecs_id(ecs_i64_t)) { \ + ECS_BINARY_OP_T(left, right, result, op, ecs_bool_t, ecs_i64_t);\ + } else if (left->type == ecs_id(ecs_f64_t)) { \ + ECS_BINARY_OP_T(left, right, result, op, ecs_bool_t, ecs_f64_t);\ + } else if (left->type == ecs_id(ecs_u8_t)) { \ + ECS_BINARY_OP_T(left, right, result, op, ecs_bool_t, ecs_u8_t);\ + } else if (left->type == ecs_id(ecs_char_t)) { \ + ECS_BINARY_OP_T(left, right, result, op, ecs_bool_t, ecs_char_t);\ + } else if (left->type == ecs_id(ecs_bool_t)) { \ + ECS_BINARY_OP_T(left, right, result, op, ecs_bool_t, ecs_bool_t);\ + } else {\ + ecs_abort(ECS_INTERNAL_ERROR, "unexpected type in binary expression");\ + } + +#define ECS_BINARY_BOOL_OP(left, right, result, op)\ + if (left->type == ecs_id(ecs_bool_t)) { \ + ECS_BINARY_OP_T(left, right, result, op, ecs_bool_t, ecs_bool_t);\ + } else {\ + ecs_abort(ECS_INTERNAL_ERROR, "unexpected type in binary expression");\ + } + +static +int flecs_binary_expr_do( + ecs_world_t *world, + const char *name, + const char *expr, + const char *ptr, + ecs_value_t *lvalue, + ecs_value_t *rvalue, + ecs_value_t *result, + ecs_expr_oper_t op) +{ + /* Find expression type */ + ecs_entity_t operand_type, type = flecs_binary_expr_type( + world, name, expr, ptr, lvalue, rvalue, op, &operand_type); + if (!type) { return -1; } - if (ptr[0] == '[') { - ecs_parser_error(name, expr, ptr - expr, - "unknown type for collection literal"); + if (!flecs_oper_valid_for_type(type, op)) { + ecs_parser_error(name, expr, ptr - expr, "invalid operator for type"); return -1; } - ecs_parser_error(name, expr, ptr - expr, "invalid literal"); - return -1; + flecs_value_cast(world, lvalue, operand_type); + flecs_value_cast(world, rvalue, operand_type); + + ecs_value_t *storage = result; + ecs_value_t tmp_storage = {0}; + if (result->type && (result->type != type)) { + storage = &tmp_storage; + } + if (storage->type != type) { + storage->type = type; + storage->ptr = ecs_value_new(world, type); + } + + switch(op) { + case EcsAdd: + ECS_BINARY_OP(lvalue, rvalue, storage, +); + break; + case EcsSub: + ECS_BINARY_OP(lvalue, rvalue, storage, -); + break; + case EcsMul: + ECS_BINARY_OP(lvalue, rvalue, storage, *); + break; + case EcsDiv: + ECS_BINARY_OP(lvalue, rvalue, storage, /); + break; + case EcsCondEq: + ECS_BINARY_COND_OP(lvalue, rvalue, storage, ==); + break; + case EcsCondNeq: + ECS_BINARY_COND_OP(lvalue, rvalue, storage, !=); + break; + case EcsCondGt: + ECS_BINARY_COND_OP(lvalue, rvalue, storage, >); + break; + case EcsCondGtEq: + ECS_BINARY_COND_OP(lvalue, rvalue, storage, >=); + break; + case EcsCondLt: + ECS_BINARY_COND_OP(lvalue, rvalue, storage, <); + break; + case EcsCondLtEq: + ECS_BINARY_COND_OP(lvalue, rvalue, storage, <=); + break; + case EcsCondAnd: + ECS_BINARY_BOOL_OP(lvalue, rvalue, storage, &&); + break; + case EcsCondOr: + ECS_BINARY_BOOL_OP(lvalue, rvalue, storage, ||); + break; + default: + ecs_parser_error(name, expr, ptr - expr, "unsupported operator"); + return -1; + } + + if (storage != result) { + ecs_meta_cursor_t cur = ecs_meta_cursor(world, + result->type, result->ptr); + ecs_meta_set_value(&cur, storage); + ecs_value_free(world, storage->type, storage->ptr); + } + + return 0; } -const char* ecs_parse_expr( +static +const char* flecs_binary_expr_parse( + ecs_world_t *world, + const char *name, + const char *expr, + const char *ptr, + ecs_value_t *lvalue, + ecs_value_t *result, + ecs_expr_oper_t left_op, + const ecs_parse_expr_desc_t *desc) +{ + ecs_entity_t result_type = result->type; + do { + ecs_expr_oper_t op; + ptr = flecs_str_to_expr_oper(ptr, &op); + if (!ptr) { + ecs_parser_error(name, expr, ptr - expr, "invalid operator"); + return NULL; + } + + ptr = ecs_parse_fluff(ptr, NULL); + + ecs_value_t rvalue = {0}; + const char *rptr = flecs_parse_expr(world, ptr, &rvalue, op, desc); + if (!rptr) { + return NULL; + } + + if (flecs_binary_expr_do(world, name, expr, ptr, + lvalue, &rvalue, result, op)) + { + return NULL; + } + + ecs_value_free(world, lvalue->type, lvalue->ptr); + ecs_value_free(world, rvalue.type, rvalue.ptr); + + ptr = rptr; + + ecs_expr_oper_t right_op; + flecs_str_to_expr_oper(rptr, &right_op); + if (right_op > left_op) { + if (result_type) { + /* If result was initialized, preserve its value */ + lvalue->type = result->type; + lvalue->ptr = ecs_value_new(world, lvalue->type); + ecs_value_copy(world, lvalue->type, lvalue->ptr, result->ptr); + continue; + } else { + /* Otherwise move result to lvalue */ + *lvalue = *result; + ecs_os_zeromem(result); + continue; + } + } + + break; + } while (true); + + return ptr; +} + +static +const char* flecs_parse_expr( ecs_world_t *world, const char *ptr, ecs_value_t *value, + ecs_expr_oper_t left_op, const ecs_parse_expr_desc_t *desc) { ecs_assert(value != NULL, ECS_INTERNAL_ERROR, NULL); char token[ECS_MAX_TOKEN_SIZE]; int depth = 0; - - const char *name = NULL; - const char *expr = NULL; - - if (desc) { - name = desc->name; - expr = desc->expr; - } - if (!expr) { - expr = ptr; - } + ecs_value_t result = {0}; + ecs_meta_cursor_t cur = {0}; + const char *name = desc ? desc->name : NULL; + const char *expr = desc ? desc->expr : NULL; + expr = expr ? expr : ptr; ptr = ecs_parse_fluff(ptr, NULL); - ecs_entity_t type = value->type; - if (!type) { - ecs_assert(value->ptr == NULL, ECS_INVALID_PARAMETER, NULL); - if (flecs_parse_discover_type(world, name, expr, ptr, value, desc)) { + /* Initialize storage and cursor. If expression starts with a '(' storage + * will be initialized by a nested expression */ + if (ptr[0] != '(') { + ecs_entity_t type = flecs_parse_discover_type( + name, expr, ptr, value->type, desc); + if (!type) { return NULL; } - type = value->type; - } - void *data_out = value->ptr; - ecs_meta_cursor_t cur = ecs_meta_cursor(world, type, data_out); - if (cur.valid == false) { - return NULL; - } + result.type = type; + if (type != value->type) { + result.ptr = ecs_value_new(world, type); + } else { + result.ptr = value->ptr; + } - if (desc) { - cur.lookup_action = desc->lookup_action; - cur.lookup_ctx = desc->lookup_ctx; + cur = ecs_meta_cursor(world, result.type, result.ptr); + if (!cur.valid) { + ecs_value_free(world, result.type, result.ptr); + return NULL; + } + + cur.lookup_action = desc ? desc->lookup_action : NULL; + cur.lookup_ctx = desc ? desc->lookup_ctx : NULL; } + /* Loop that parses all values in a value scope */ while ((ptr = ecs_parse_expr_token(name, expr, ptr, token))) { - if (!ecs_os_strcmp(token, "{")) { + /* Used to track of the result of the parsed token can be used as the + * lvalue for a binary expression */ + bool is_lvalue = false; + + if (!ecs_os_strcmp(token, "(")) { + /* Parenthesis, parse nested expression */ + ptr = flecs_parse_expr(world, ptr, &result, EcsLeftParen, desc); + if (ptr[0] != ')') { + ecs_parser_error(name, expr, ptr - expr, + "missing closing parenthesis"); + return NULL; + } + ptr = ecs_parse_fluff(ptr + 1, NULL); + is_lvalue = true; + + } else if (!ecs_os_strcmp(token, "{")) { + /* Parse nested value scope */ ecs_entity_t scope_type = ecs_meta_get_type(&cur); - depth ++; + + depth ++; /* Keep track of depth so we know when parsing is done */ if (ecs_meta_push(&cur) != 0) { goto error; } @@ -28214,6 +28753,7 @@ const char* ecs_parse_expr( } else if (!ecs_os_strcmp(token, "[")) { + /* Open collection value scope */ depth ++; if (ecs_meta_push(&cur) != 0) { goto error; @@ -28239,6 +28779,7 @@ const char* ecs_parse_expr( } else if (!ecs_os_strcmp(token, ",")) { + /* Move to next field */ if (ecs_meta_next(&cur) != 0) { goto error; } @@ -28248,18 +28789,32 @@ const char* ecs_parse_expr( if (ecs_meta_set_null(&cur) != 0) { goto error; } + + is_lvalue = true; } else if (token[0] == '\"') { + /* Regular string */ if (ecs_meta_set_string_literal(&cur, token) != 0) { goto error; } + + is_lvalue = true; } - else if (token[0] == '$') { + else if (!ecs_os_strcmp(token, "`")) { + /* Multiline string */ + if (!(ptr = flecs_parse_multiline_string(&cur, name, expr, ptr))) { + goto error; + } + + is_lvalue = true; + + } else if (token[0] == '$') { + /* Variable */ if (!desc || !desc->vars) { ecs_parser_error(name, expr, ptr - expr, - "unresolved variable '%s' (no variables provided)", token); + "unresolved variable '%s' (no variable scope)", token); return NULL; } @@ -28271,15 +28826,9 @@ const char* ecs_parse_expr( } ecs_meta_set_value(&cur, &var->value); - } + is_lvalue = true; - else if (!ecs_os_strcmp(token, "`")) { - if (!(ptr = flecs_parse_multiline_string(&cur, name, expr, ptr))) { - goto error; - } - } - - else { + } else { ptr = ecs_parse_fluff(ptr, NULL); if (ptr[0] == ':') { @@ -28293,20 +28842,84 @@ const char* ecs_parse_expr( goto error; } } + is_lvalue = true; + } + + /* If lvalue was parsed, test if this is a binary expression */ + if (is_lvalue) { + ecs_expr_oper_t right_op; + flecs_str_to_expr_oper(ptr, &right_op); + if (right_op) { + /* This is a binary expression, test precedence to determine if + * it should be evaluated here */ + if (flecs_oper_precedence(left_op, right_op) < 0) { + ecs_value_t lvalue; + ecs_value_t *op_result = &result; + ecs_value_t temp_storage; + if (!depth) { + /* Root level value, move result to lvalue storage */ + lvalue = result; + ecs_os_zeromem(&result); + } else { + /* Not a root level value. Move the parsed lvalue to a + * temporary storage, and initialize the result value + * for the binary operation with the current cursor */ + ecs_entity_t cur_type = ecs_meta_get_type(&cur); + void *cur_ptr = ecs_meta_get_ptr(&cur); + lvalue.type = cur_type; + lvalue.ptr = ecs_value_new(world, cur_type); + ecs_value_copy(world, cur_type, lvalue.ptr, cur_ptr); + temp_storage.type = cur_type; + temp_storage.ptr = cur_ptr; + op_result = &temp_storage; + } + + /* Do the binary expression */ + ptr = flecs_binary_expr_parse(world, name, expr, ptr, + &lvalue, op_result, left_op, desc); + if (!ptr) { + return NULL; + } + } + } } if (!depth) { + /* Reached the end of the root scope */ break; } ptr = ecs_parse_fluff(ptr, NULL); } + if (!value->ptr) { + /* Move result to output */ + *value = result; + } else if (result.ptr != value->ptr) { + /* Result and value differ, copy value to result. Use cursor API so + * value is casted if necessary */ + cur = ecs_meta_cursor(world, value->type, value->ptr); + ecs_meta_set_value(&cur, &result); + ecs_value_free(world, result.type, result.ptr); + } + + ecs_assert(value->type != 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(value->ptr != NULL, ECS_INTERNAL_ERROR, NULL); + return ptr; error: return NULL; } +const char* ecs_parse_expr( + ecs_world_t *world, + const char *ptr, + ecs_value_t *value, + const ecs_parse_expr_desc_t *desc) +{ + return flecs_parse_expr(world, ptr, value, EcsExprOperUnknown, desc); +} + #endif diff --git a/flecs.h b/flecs.h index 866037830a..2cce136293 100644 --- a/flecs.h +++ b/flecs.h @@ -12175,10 +12175,13 @@ typedef struct ecs_parse_expr_desc_t { * This operation parses a flecs expression into the provided pointer. The * memory pointed to must be large enough to contain a value of the used type. * + * If no type and pointer are provided for the value argument, the operation + * will discover the type from the expression and allocate storage for the + * value. The allocated value must be freed with ecs_value_free. + * * @param world The world. * @param ptr The pointer to the expression to parse. - * @param type The type of the expression to parse. - * @param data_out Pointer to the memory to write to. + * @param value The value containing type & pointer to write to. * @param desc Configuration parameters for deserializer. * @return Pointer to the character after the last one read, or NULL if failed. */ diff --git a/include/flecs/addons/expr.h b/include/flecs/addons/expr.h index 4782d17521..f203593d38 100644 --- a/include/flecs/addons/expr.h +++ b/include/flecs/addons/expr.h @@ -181,10 +181,13 @@ typedef struct ecs_parse_expr_desc_t { * This operation parses a flecs expression into the provided pointer. The * memory pointed to must be large enough to contain a value of the used type. * + * If no type and pointer are provided for the value argument, the operation + * will discover the type from the expression and allocate storage for the + * value. The allocated value must be freed with ecs_value_free. + * * @param world The world. * @param ptr The pointer to the expression to parse. - * @param type The type of the expression to parse. - * @param data_out Pointer to the memory to write to. + * @param value The value containing type & pointer to write to. * @param desc Configuration parameters for deserializer. * @return Pointer to the character after the last one read, or NULL if failed. */ diff --git a/src/addons/expr/deserialize.c b/src/addons/expr/deserialize.c index 0dd0a98552..526e238e23 100644 --- a/src/addons/expr/deserialize.c +++ b/src/addons/expr/deserialize.c @@ -1,9 +1,83 @@ - #include "../../private_api.h" #include #ifdef FLECS_EXPR +/* String deserializer for values & simple expressions */ + +/* Order in enumeration is important, as it is used for precedence */ +typedef enum ecs_expr_oper_t { + EcsExprOperUnknown, + EcsLeftParen, + EcsCondAnd, + EcsCondOr, + EcsCondEq, + EcsCondNeq, + EcsCondGt, + EcsCondGtEq, + EcsCondLt, + EcsCondLtEq, + EcsAdd, + EcsSub, + EcsMul, + EcsDiv, +} ecs_expr_oper_t; + +static +const char* flecs_parse_expr( + ecs_world_t *world, + const char *ptr, + ecs_value_t *value, + ecs_expr_oper_t op, + const ecs_parse_expr_desc_t *desc); + +static +const char* flecs_str_to_expr_oper( + const char *str, + ecs_expr_oper_t *op) +{ + if (!ecs_os_strncmp(str, "+", 1)) { + *op = EcsAdd; + return str + 1; + } else if (!ecs_os_strncmp(str, "-", 1)) { + *op = EcsSub; + return str + 1; + } else if (!ecs_os_strncmp(str, "*", 1)) { + *op = EcsMul; + return str + 1; + } else if (!ecs_os_strncmp(str, "/", 1)) { + *op = EcsDiv; + return str + 1; + } else if (!ecs_os_strncmp(str, "&&", 2)) { + *op = EcsCondAnd; + return str + 2; + } else if (!ecs_os_strncmp(str, "||", 2)) { + *op = EcsCondOr; + return str + 2; + } else if (!ecs_os_strncmp(str, "==", 2)) { + *op = EcsCondEq; + return str + 2; + } else if (!ecs_os_strncmp(str, "!=", 2)) { + *op = EcsCondNeq; + return str + 2; + } else if (!ecs_os_strncmp(str, ">=", 2)) { + *op = EcsCondGtEq; + return str + 2; + } else if (!ecs_os_strncmp(str, "<=", 2)) { + *op = EcsCondLtEq; + return str + 2; + } else if (!ecs_os_strncmp(str, ">", 1)) { + *op = EcsCondGt; + return str + 1; + } else if (!ecs_os_strncmp(str, "<", 1)) { + *op = EcsCondLt; + return str + 1; + } + + *op = EcsExprOperUnknown; + return NULL; +} + const char *ecs_parse_expr_token( const char *name, const char *expr, @@ -13,8 +87,21 @@ const char *ecs_parse_expr_token( const char *start = ptr; char *token_ptr = token; + ecs_expr_oper_t op; + if (ptr[0] == '(') { + token[0] = '('; + token[1] = 0; + return ptr + 1; + } else if (ptr[0] != '-') { + const char *tptr = flecs_str_to_expr_oper(ptr, &op); + if (tptr) { + ecs_os_strncpy(token, ptr, tptr - ptr); + return tptr; + } + } + while ((ptr = ecs_parse_token(name, expr, ptr, token_ptr))) { - if (ptr[0] == '|') { + if (ptr[0] == '|' && ptr[1] != '|') { token_ptr = &token_ptr[ptr - start]; token_ptr[0] = '|'; token_ptr[1] = '\0'; @@ -81,150 +168,595 @@ bool flecs_parse_is_float( return false; } +/* Determine the type of an expression from the first character(s). This allows + * us to initialize a storage for a type if none was provided. */ static -int flecs_parse_discover_type( - ecs_world_t *world, +ecs_entity_t flecs_parse_discover_type( const char *name, const char *expr, const char *ptr, - ecs_value_t *value, + ecs_entity_t input_type, const ecs_parse_expr_desc_t *desc) { /* String literal */ if (ptr[0] == '"' || ptr[0] == '`') { - value->type = ecs_id(ecs_string_t); - value->ptr = ecs_value_new_t(world, ecs_string_t); - return 0; + if (input_type == ecs_id(ecs_char_t)) { + return input_type; + } + return ecs_id(ecs_string_t); } - + /* Negative number literal */ if (ptr[0] == '-') { if (!isdigit(ptr[1])) { ecs_parser_error(name, expr, ptr - expr, "invalid literal"); - return -1; + return 0; } if (flecs_parse_is_float(ptr + 1)) { - value->type = ecs_id(ecs_f64_t); - value->ptr = ecs_value_new_t(world, ecs_f64_t); - return 0; + return ecs_id(ecs_f64_t); } else { - value->type = ecs_id(ecs_i64_t); - value->ptr = ecs_value_new_t(world, ecs_i64_t); - return 0; + return ecs_id(ecs_i64_t); } } /* Positive number literal */ if (isdigit(ptr[0])) { if (flecs_parse_is_float(ptr)) { - value->type = ecs_id(ecs_f64_t); - value->ptr = ecs_value_new_t(world, ecs_f64_t); - return 0; + return ecs_id(ecs_f64_t); } else { - value->type = ecs_id(ecs_u64_t); - value->ptr = ecs_value_new_t(world, ecs_u64_t); - return 0; + return ecs_id(ecs_u64_t); } } /* Variable */ if (ptr[0] == '$') { - const ecs_expr_var_t *var = ecs_vars_lookup(desc->vars, &ptr[1]); + if (!desc || !desc->vars) { + ecs_parser_error(name, expr, ptr - expr, + "unresolved variable (no variable scope)"); + return 0; + } + char token[ECS_MAX_TOKEN_SIZE]; + if (ecs_parse_expr_token(name, expr, &ptr[1], token) == NULL) { + return 0; + } + const ecs_expr_var_t *var = ecs_vars_lookup(desc->vars, token); if (!var) { - ecs_parser_error(name, expr, ptr - expr, "unresolved variable"); - return -1; + ecs_parser_error(name, expr, ptr - expr, + "unresolved variable '%s'", token); + return 0; } - value->type = var->value.type; - value->ptr = ecs_value_new(world, value->type); - return 0; + return var->value.type; } /* Boolean */ if (ptr[0] == 't' && !ecs_os_strncmp(ptr, "true", 4)) { if (!isalpha(ptr[4]) && ptr[4] != '_') { - value->type = ecs_id(ecs_bool_t); - value->ptr = ecs_value_new_t(world, ecs_bool_t); - return 0; + return ecs_id(ecs_bool_t); } } if (ptr[0] == 'f' && !ecs_os_strncmp(ptr, "false", 5)) { if (!isalpha(ptr[5]) && ptr[5] != '_') { - value->type = ecs_id(ecs_bool_t); - value->ptr = ecs_value_new_t(world, ecs_bool_t); - return 0; + return ecs_id(ecs_bool_t); } } /* Entity identifier */ if (isalpha(ptr[0])) { - value->type = ecs_id(ecs_entity_t); - value->ptr = ecs_value_new_t(world, ecs_entity_t); + if (!input_type) { /* Identifier could also be enum/bitmask constant */ + return ecs_id(ecs_entity_t); + } + } + + /* If no default type was provided we can't automatically deduce the type of + * composite/collection expressions. */ + if (!input_type) { + if (ptr[0] == '{') { + ecs_parser_error(name, expr, ptr - expr, + "unknown type for composite literal"); + return 0; + } + + if (ptr[0] == '[') { + ecs_parser_error(name, expr, ptr - expr, + "unknown type for collection literal"); + return 0; + } + + ecs_parser_error(name, expr, ptr - expr, "invalid expression"); + } + + return input_type; +} + +/* Normalize types to their largest representation. + * Rather than taking the original type of a value, use the largest + * representation of the type so we don't have to worry about overflowing the + * original type in the operation. */ +static +ecs_entity_t flecs_largest_type( + const EcsPrimitive *type) +{ + switch(type->kind) { + case EcsBool: return ecs_id(ecs_bool_t); + case EcsChar: return ecs_id(ecs_char_t); + case EcsByte: return ecs_id(ecs_u8_t); + case EcsU8: return ecs_id(ecs_u64_t); + case EcsU16: return ecs_id(ecs_u64_t); + case EcsU32: return ecs_id(ecs_u64_t); + case EcsU64: return ecs_id(ecs_u64_t); + case EcsI8: return ecs_id(ecs_i64_t); + case EcsI16: return ecs_id(ecs_i64_t); + case EcsI32: return ecs_id(ecs_i64_t); + case EcsI64: return ecs_id(ecs_i64_t); + case EcsF32: return ecs_id(ecs_f64_t); + case EcsF64: return ecs_id(ecs_f64_t); + case EcsUPtr: return ecs_id(ecs_u64_t); + case EcsIPtr: return ecs_id(ecs_i64_t); + case EcsString: return ecs_id(ecs_string_t); + case EcsEntity: return ecs_id(ecs_entity_t); + default: ecs_abort(ECS_INTERNAL_ERROR, NULL); + } +} + +/** Test if a normalized type can promote to another type in an expression */ +static +bool flecs_is_number( + ecs_entity_t type) +{ + if (type == ecs_id(ecs_bool_t)) return false; + else if (type == ecs_id(ecs_char_t)) return false; + else if (type == ecs_id(ecs_u8_t)) return false; + else if (type == ecs_id(ecs_u64_t)) return true; + else if (type == ecs_id(ecs_i64_t)) return true; + else if (type == ecs_id(ecs_f64_t)) return true; + else if (type == ecs_id(ecs_string_t)) return false; + else if (type == ecs_id(ecs_entity_t)) return false; + else return false; +} + +static +bool flecs_oper_valid_for_type( + ecs_entity_t type, + ecs_expr_oper_t op) +{ + switch(op) { + case EcsAdd: + case EcsSub: + case EcsMul: + case EcsDiv: + return flecs_is_number(type); + case EcsCondEq: + case EcsCondNeq: + case EcsCondAnd: + case EcsCondOr: + case EcsCondGt: + case EcsCondGtEq: + case EcsCondLt: + case EcsCondLtEq: + return flecs_is_number(type) || + (type == ecs_id(ecs_bool_t)) || + (type == ecs_id(ecs_char_t)) || + (type == ecs_id(ecs_entity_t)); + default: + return false; + } +} + +/** Promote type to most expressive (f64 > i64 > u64) */ +static +ecs_entity_t flecs_promote_type( + ecs_entity_t type, + ecs_entity_t promote_to) +{ + if (type == ecs_id(ecs_u64_t)) { + return promote_to; + } + if (promote_to == ecs_id(ecs_u64_t)) { + return type; + } + if (type == ecs_id(ecs_f64_t)) { + return type; + } + if (promote_to == ecs_id(ecs_f64_t)) { + return promote_to; + } + return ecs_id(ecs_i64_t); +} + +static +int flecs_oper_precedence( + ecs_expr_oper_t left, + ecs_expr_oper_t right) +{ + return (left > right) - (left < right); +} + +static +void flecs_value_cast( + ecs_world_t *world, + ecs_value_t *value, + ecs_entity_t type) +{ + if (value->type == type) { + return; + } + + ecs_value_t result; + result.type = type; + result.ptr = ecs_value_new(world, type); + + if (value->ptr) { + ecs_meta_cursor_t cur = ecs_meta_cursor(world, type, result.ptr); + ecs_meta_set_value(&cur, value); + ecs_value_free(world, value->type, value->ptr); + } + + *value = result; +} + +static +bool flecs_expr_op_is_equality( + ecs_expr_oper_t op) +{ + switch(op) { + case EcsCondEq: + case EcsCondNeq: + case EcsCondGt: + case EcsCondGtEq: + case EcsCondLt: + case EcsCondLtEq: + return true; + default: + return false; + } +} + +static +ecs_entity_t flecs_binary_expr_type( + ecs_world_t *world, + const char *name, + const char *expr, + const char *ptr, + ecs_value_t *lvalue, + ecs_value_t *rvalue, + ecs_expr_oper_t op, + ecs_entity_t *operand_type_out) +{ + ecs_entity_t result_type = 0, operand_type = 0; + + switch(op) { + case EcsDiv: + /* Result type of a division is always a float */ + *operand_type_out = ecs_id(ecs_f64_t); + return ecs_id(ecs_f64_t); + case EcsCondAnd: + case EcsCondOr: + /* Result type of a condition operator is always a bool */ + *operand_type_out = ecs_id(ecs_bool_t); + return ecs_id(ecs_bool_t); + case EcsCondEq: + case EcsCondNeq: + case EcsCondGt: + case EcsCondGtEq: + case EcsCondLt: + case EcsCondLtEq: + /* Result type of equality operator is always bool, but operand types + * should not be casted to bool */ + result_type = ecs_id(ecs_bool_t); + break; + default: + break; + } + + /* Result type for arithmetic operators is determined by operands */ + const EcsPrimitive *ltype_ptr = ecs_get(world, lvalue->type, EcsPrimitive); + const EcsPrimitive *rtype_ptr = ecs_get(world, rvalue->type, EcsPrimitive); + if (!ltype_ptr || !rtype_ptr) { + ecs_parser_error(name, expr, ptr - expr, + "invalid non-primitive type in binary expression"); + return 0; + } + + ecs_entity_t ltype = flecs_largest_type(ltype_ptr); + ecs_entity_t rtype = flecs_largest_type(rtype_ptr); + if (ltype == rtype) { + operand_type = ltype; + goto done; + } + + if (flecs_expr_op_is_equality(op)) { + ecs_parser_error(name, expr, ptr - expr, + "mismatching types in equality expression"); + return 0; + } + + if (!flecs_is_number(ltype) || !flecs_is_number(rtype)) { + ecs_parser_error(name, expr, ptr - expr, + "incompatible types in binary expression"); return 0; } - if (ptr[0] == '{') { - ecs_parser_error(name, expr, ptr - expr, - "unknown type for composite literal"); + operand_type = flecs_promote_type(ltype, rtype); + +done: + if (op == EcsSub && operand_type == ecs_id(ecs_u64_t)) { + /* Result of subtracting two unsigned ints can be negative */ + operand_type = ecs_id(ecs_i64_t); + } + + if (!result_type) { + result_type = operand_type; + } + + *operand_type_out = operand_type; + return result_type; +} + +/* Macro's to let the compiler do the operations & conversion work for us */ + +#define ECS_VALUE_GET(value, T) (*(T*)value->ptr) + +#define ECS_BINARY_OP_T(left, right, result, op, R, T)\ + ECS_VALUE_GET(result, R) = ECS_VALUE_GET(left, T) op ECS_VALUE_GET(right, T) + +#define ECS_BINARY_OP(left, right, result, op)\ + if (left->type == ecs_id(ecs_u64_t)) { \ + ECS_BINARY_OP_T(left, right, result, op, ecs_u64_t, ecs_u64_t);\ + } else if (left->type == ecs_id(ecs_i64_t)) { \ + ECS_BINARY_OP_T(left, right, result, op, ecs_i64_t, ecs_i64_t);\ + } else if (left->type == ecs_id(ecs_f64_t)) { \ + ECS_BINARY_OP_T(left, right, result, op, ecs_f64_t, ecs_f64_t);\ + } else if (left->type == ecs_id(ecs_u8_t)) { \ + ECS_BINARY_OP_T(left, right, result, op, ecs_u8_t, ecs_u8_t);\ + } else if (left->type == ecs_id(ecs_char_t)) { \ + ECS_BINARY_OP_T(left, right, result, op, ecs_char_t, ecs_char_t);\ + } else if (left->type == ecs_id(ecs_bool_t)) { \ + ECS_BINARY_OP_T(left, right, result, op, ecs_bool_t, ecs_bool_t);\ + } else {\ + ecs_abort(ECS_INTERNAL_ERROR, "unexpected type in binary expression");\ + } + +#define ECS_BINARY_COND_OP(left, right, result, op)\ + if (left->type == ecs_id(ecs_u64_t)) { \ + ECS_BINARY_OP_T(left, right, result, op, ecs_bool_t, ecs_u64_t);\ + } else if (left->type == ecs_id(ecs_i64_t)) { \ + ECS_BINARY_OP_T(left, right, result, op, ecs_bool_t, ecs_i64_t);\ + } else if (left->type == ecs_id(ecs_f64_t)) { \ + ECS_BINARY_OP_T(left, right, result, op, ecs_bool_t, ecs_f64_t);\ + } else if (left->type == ecs_id(ecs_u8_t)) { \ + ECS_BINARY_OP_T(left, right, result, op, ecs_bool_t, ecs_u8_t);\ + } else if (left->type == ecs_id(ecs_char_t)) { \ + ECS_BINARY_OP_T(left, right, result, op, ecs_bool_t, ecs_char_t);\ + } else if (left->type == ecs_id(ecs_bool_t)) { \ + ECS_BINARY_OP_T(left, right, result, op, ecs_bool_t, ecs_bool_t);\ + } else {\ + ecs_abort(ECS_INTERNAL_ERROR, "unexpected type in binary expression");\ + } + +#define ECS_BINARY_BOOL_OP(left, right, result, op)\ + if (left->type == ecs_id(ecs_bool_t)) { \ + ECS_BINARY_OP_T(left, right, result, op, ecs_bool_t, ecs_bool_t);\ + } else {\ + ecs_abort(ECS_INTERNAL_ERROR, "unexpected type in binary expression");\ + } + +static +int flecs_binary_expr_do( + ecs_world_t *world, + const char *name, + const char *expr, + const char *ptr, + ecs_value_t *lvalue, + ecs_value_t *rvalue, + ecs_value_t *result, + ecs_expr_oper_t op) +{ + /* Find expression type */ + ecs_entity_t operand_type, type = flecs_binary_expr_type( + world, name, expr, ptr, lvalue, rvalue, op, &operand_type); + if (!type) { return -1; } - if (ptr[0] == '[') { - ecs_parser_error(name, expr, ptr - expr, - "unknown type for collection literal"); + if (!flecs_oper_valid_for_type(type, op)) { + ecs_parser_error(name, expr, ptr - expr, "invalid operator for type"); + return -1; + } + + flecs_value_cast(world, lvalue, operand_type); + flecs_value_cast(world, rvalue, operand_type); + + ecs_value_t *storage = result; + ecs_value_t tmp_storage = {0}; + if (result->type && (result->type != type)) { + storage = &tmp_storage; + } + if (storage->type != type) { + storage->type = type; + storage->ptr = ecs_value_new(world, type); + } + + switch(op) { + case EcsAdd: + ECS_BINARY_OP(lvalue, rvalue, storage, +); + break; + case EcsSub: + ECS_BINARY_OP(lvalue, rvalue, storage, -); + break; + case EcsMul: + ECS_BINARY_OP(lvalue, rvalue, storage, *); + break; + case EcsDiv: + ECS_BINARY_OP(lvalue, rvalue, storage, /); + break; + case EcsCondEq: + ECS_BINARY_COND_OP(lvalue, rvalue, storage, ==); + break; + case EcsCondNeq: + ECS_BINARY_COND_OP(lvalue, rvalue, storage, !=); + break; + case EcsCondGt: + ECS_BINARY_COND_OP(lvalue, rvalue, storage, >); + break; + case EcsCondGtEq: + ECS_BINARY_COND_OP(lvalue, rvalue, storage, >=); + break; + case EcsCondLt: + ECS_BINARY_COND_OP(lvalue, rvalue, storage, <); + break; + case EcsCondLtEq: + ECS_BINARY_COND_OP(lvalue, rvalue, storage, <=); + break; + case EcsCondAnd: + ECS_BINARY_BOOL_OP(lvalue, rvalue, storage, &&); + break; + case EcsCondOr: + ECS_BINARY_BOOL_OP(lvalue, rvalue, storage, ||); + break; + default: + ecs_parser_error(name, expr, ptr - expr, "unsupported operator"); return -1; } - ecs_parser_error(name, expr, ptr - expr, "invalid literal"); - return -1; + if (storage != result) { + ecs_meta_cursor_t cur = ecs_meta_cursor(world, + result->type, result->ptr); + ecs_meta_set_value(&cur, storage); + ecs_value_free(world, storage->type, storage->ptr); + } + + return 0; } -const char* ecs_parse_expr( +static +const char* flecs_binary_expr_parse( + ecs_world_t *world, + const char *name, + const char *expr, + const char *ptr, + ecs_value_t *lvalue, + ecs_value_t *result, + ecs_expr_oper_t left_op, + const ecs_parse_expr_desc_t *desc) +{ + ecs_entity_t result_type = result->type; + do { + ecs_expr_oper_t op; + ptr = flecs_str_to_expr_oper(ptr, &op); + if (!ptr) { + ecs_parser_error(name, expr, ptr - expr, "invalid operator"); + return NULL; + } + + ptr = ecs_parse_fluff(ptr, NULL); + + ecs_value_t rvalue = {0}; + const char *rptr = flecs_parse_expr(world, ptr, &rvalue, op, desc); + if (!rptr) { + return NULL; + } + + if (flecs_binary_expr_do(world, name, expr, ptr, + lvalue, &rvalue, result, op)) + { + return NULL; + } + + ecs_value_free(world, lvalue->type, lvalue->ptr); + ecs_value_free(world, rvalue.type, rvalue.ptr); + + ptr = rptr; + + ecs_expr_oper_t right_op; + flecs_str_to_expr_oper(rptr, &right_op); + if (right_op > left_op) { + if (result_type) { + /* If result was initialized, preserve its value */ + lvalue->type = result->type; + lvalue->ptr = ecs_value_new(world, lvalue->type); + ecs_value_copy(world, lvalue->type, lvalue->ptr, result->ptr); + continue; + } else { + /* Otherwise move result to lvalue */ + *lvalue = *result; + ecs_os_zeromem(result); + continue; + } + } + + break; + } while (true); + + return ptr; +} + +static +const char* flecs_parse_expr( ecs_world_t *world, const char *ptr, ecs_value_t *value, + ecs_expr_oper_t left_op, const ecs_parse_expr_desc_t *desc) { ecs_assert(value != NULL, ECS_INTERNAL_ERROR, NULL); char token[ECS_MAX_TOKEN_SIZE]; int depth = 0; - - const char *name = NULL; - const char *expr = NULL; - - if (desc) { - name = desc->name; - expr = desc->expr; - } - if (!expr) { - expr = ptr; - } + ecs_value_t result = {0}; + ecs_meta_cursor_t cur = {0}; + const char *name = desc ? desc->name : NULL; + const char *expr = desc ? desc->expr : NULL; + expr = expr ? expr : ptr; ptr = ecs_parse_fluff(ptr, NULL); - ecs_entity_t type = value->type; - if (!type) { - ecs_assert(value->ptr == NULL, ECS_INVALID_PARAMETER, NULL); - if (flecs_parse_discover_type(world, name, expr, ptr, value, desc)) { + /* Initialize storage and cursor. If expression starts with a '(' storage + * will be initialized by a nested expression */ + if (ptr[0] != '(') { + ecs_entity_t type = flecs_parse_discover_type( + name, expr, ptr, value->type, desc); + if (!type) { return NULL; } - type = value->type; - } - void *data_out = value->ptr; - ecs_meta_cursor_t cur = ecs_meta_cursor(world, type, data_out); - if (cur.valid == false) { - return NULL; - } + result.type = type; + if (type != value->type) { + result.ptr = ecs_value_new(world, type); + } else { + result.ptr = value->ptr; + } - if (desc) { - cur.lookup_action = desc->lookup_action; - cur.lookup_ctx = desc->lookup_ctx; + cur = ecs_meta_cursor(world, result.type, result.ptr); + if (!cur.valid) { + ecs_value_free(world, result.type, result.ptr); + return NULL; + } + + cur.lookup_action = desc ? desc->lookup_action : NULL; + cur.lookup_ctx = desc ? desc->lookup_ctx : NULL; } + /* Loop that parses all values in a value scope */ while ((ptr = ecs_parse_expr_token(name, expr, ptr, token))) { - if (!ecs_os_strcmp(token, "{")) { + /* Used to track of the result of the parsed token can be used as the + * lvalue for a binary expression */ + bool is_lvalue = false; + + if (!ecs_os_strcmp(token, "(")) { + /* Parenthesis, parse nested expression */ + ptr = flecs_parse_expr(world, ptr, &result, EcsLeftParen, desc); + if (ptr[0] != ')') { + ecs_parser_error(name, expr, ptr - expr, + "missing closing parenthesis"); + return NULL; + } + ptr = ecs_parse_fluff(ptr + 1, NULL); + is_lvalue = true; + + } else if (!ecs_os_strcmp(token, "{")) { + /* Parse nested value scope */ ecs_entity_t scope_type = ecs_meta_get_type(&cur); - depth ++; + + depth ++; /* Keep track of depth so we know when parsing is done */ if (ecs_meta_push(&cur) != 0) { goto error; } @@ -252,6 +784,7 @@ const char* ecs_parse_expr( } else if (!ecs_os_strcmp(token, "[")) { + /* Open collection value scope */ depth ++; if (ecs_meta_push(&cur) != 0) { goto error; @@ -277,6 +810,7 @@ const char* ecs_parse_expr( } else if (!ecs_os_strcmp(token, ",")) { + /* Move to next field */ if (ecs_meta_next(&cur) != 0) { goto error; } @@ -286,18 +820,32 @@ const char* ecs_parse_expr( if (ecs_meta_set_null(&cur) != 0) { goto error; } + + is_lvalue = true; } else if (token[0] == '\"') { + /* Regular string */ if (ecs_meta_set_string_literal(&cur, token) != 0) { goto error; } + + is_lvalue = true; } - else if (token[0] == '$') { + else if (!ecs_os_strcmp(token, "`")) { + /* Multiline string */ + if (!(ptr = flecs_parse_multiline_string(&cur, name, expr, ptr))) { + goto error; + } + + is_lvalue = true; + + } else if (token[0] == '$') { + /* Variable */ if (!desc || !desc->vars) { ecs_parser_error(name, expr, ptr - expr, - "unresolved variable '%s' (no variables provided)", token); + "unresolved variable '%s' (no variable scope)", token); return NULL; } @@ -309,15 +857,9 @@ const char* ecs_parse_expr( } ecs_meta_set_value(&cur, &var->value); - } - - else if (!ecs_os_strcmp(token, "`")) { - if (!(ptr = flecs_parse_multiline_string(&cur, name, expr, ptr))) { - goto error; - } - } + is_lvalue = true; - else { + } else { ptr = ecs_parse_fluff(ptr, NULL); if (ptr[0] == ':') { @@ -331,18 +873,82 @@ const char* ecs_parse_expr( goto error; } } + is_lvalue = true; + } + + /* If lvalue was parsed, test if this is a binary expression */ + if (is_lvalue) { + ecs_expr_oper_t right_op; + flecs_str_to_expr_oper(ptr, &right_op); + if (right_op) { + /* This is a binary expression, test precedence to determine if + * it should be evaluated here */ + if (flecs_oper_precedence(left_op, right_op) < 0) { + ecs_value_t lvalue; + ecs_value_t *op_result = &result; + ecs_value_t temp_storage; + if (!depth) { + /* Root level value, move result to lvalue storage */ + lvalue = result; + ecs_os_zeromem(&result); + } else { + /* Not a root level value. Move the parsed lvalue to a + * temporary storage, and initialize the result value + * for the binary operation with the current cursor */ + ecs_entity_t cur_type = ecs_meta_get_type(&cur); + void *cur_ptr = ecs_meta_get_ptr(&cur); + lvalue.type = cur_type; + lvalue.ptr = ecs_value_new(world, cur_type); + ecs_value_copy(world, cur_type, lvalue.ptr, cur_ptr); + temp_storage.type = cur_type; + temp_storage.ptr = cur_ptr; + op_result = &temp_storage; + } + + /* Do the binary expression */ + ptr = flecs_binary_expr_parse(world, name, expr, ptr, + &lvalue, op_result, left_op, desc); + if (!ptr) { + return NULL; + } + } + } } if (!depth) { + /* Reached the end of the root scope */ break; } ptr = ecs_parse_fluff(ptr, NULL); } + if (!value->ptr) { + /* Move result to output */ + *value = result; + } else if (result.ptr != value->ptr) { + /* Result and value differ, copy value to result. Use cursor API so + * value is casted if necessary */ + cur = ecs_meta_cursor(world, value->type, value->ptr); + ecs_meta_set_value(&cur, &result); + ecs_value_free(world, result.type, result.ptr); + } + + ecs_assert(value->type != 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(value->ptr != NULL, ECS_INTERNAL_ERROR, NULL); + return ptr; error: return NULL; } +const char* ecs_parse_expr( + ecs_world_t *world, + const char *ptr, + ecs_value_t *value, + const ecs_parse_expr_desc_t *desc) +{ + return flecs_parse_expr(world, ptr, value, EcsExprOperUnknown, desc); +} + #endif diff --git a/src/addons/expr/vars.c b/src/addons/expr/vars.c index 2c14cb15f7..634512a9d7 100644 --- a/src/addons/expr/vars.c +++ b/src/addons/expr/vars.c @@ -100,8 +100,8 @@ ecs_expr_var_t* ecs_vars_declare( var->value.type = type; var->name = flecs_strdup(&vars->world->allocator, name); - flecs_name_index_ensure(var_index, ecs_vec_count(&scope->vars), - var->name, 0, 0); + flecs_name_index_ensure(var_index, + flecs_ito(uint64_t, ecs_vec_count(&scope->vars)), var->name, 0, 0); return var; error: return NULL; @@ -129,8 +129,8 @@ ecs_expr_var_t* ecs_vars_declare_w_value( var->name = flecs_strdup(&vars->world->allocator, name); value->ptr = NULL; /* Take ownership, prevent double free */ - flecs_name_index_ensure(var_index, ecs_vec_count(&scope->vars), - var->name, 0, 0); + flecs_name_index_ensure(var_index, + flecs_ito(uint64_t, ecs_vec_count(&scope->vars)), var->name, 0, 0); return var; error: return NULL; diff --git a/src/addons/meta/cursor.c b/src/addons/meta/cursor.c index 0eafc69451..abf8f90bdf 100644 --- a/src/addons/meta/cursor.c +++ b/src/addons/meta/cursor.c @@ -1,4 +1,5 @@ #include "meta.h" +#include #ifdef FLECS_META @@ -690,7 +691,7 @@ int ecs_meta_set_value( case EcsI16: return ecs_meta_set_int(cursor, *(int16_t*)value->ptr); case EcsI32: return ecs_meta_set_int(cursor, *(int32_t*)value->ptr); case EcsI64: return ecs_meta_set_int(cursor, *(int64_t*)value->ptr); - case EcsF32: return ecs_meta_set_float(cursor, *(float*)value->ptr); + case EcsF32: return ecs_meta_set_float(cursor, (double)*(float*)value->ptr); case EcsF64: return ecs_meta_set_float(cursor, *(double*)value->ptr); case EcsUPtr: return ecs_meta_set_uint(cursor, *(uintptr_t*)value->ptr); case EcsIPtr: return ecs_meta_set_int(cursor, *(intptr_t*)value->ptr); @@ -802,6 +803,12 @@ int ecs_meta_set_string( set_T(ecs_bool_t, ptr, true); } else if (!ecs_os_strcmp(value, "false")) { set_T(ecs_bool_t, ptr, false); + } else if (isdigit(value[0])) { + if (!ecs_os_strcmp(value, "0")) { + set_T(ecs_bool_t, ptr, false); + } else { + set_T(ecs_bool_t, ptr, true); + } } else { ecs_err("invalid value for boolean '%s'", value); return -1; diff --git a/test/meta/include/meta.h b/test/meta/include/meta.h index 47cf4fd2ec..908ce378bc 100644 --- a/test/meta/include/meta.h +++ b/test/meta/include/meta.h @@ -3,6 +3,7 @@ /* This generated file contains includes for project dependencies */ #include +#include #ifdef __cplusplus extern "C" { diff --git a/test/meta/project.json b/test/meta/project.json index 76c3dcb2e1..ad04ba0e1f 100644 --- a/test/meta/project.json +++ b/test/meta/project.json @@ -373,12 +373,14 @@ "struct_enum", "struct_bitmask", "struct_i32", + "struct_i32_neg", "struct_i32_i32", "struct_entity", "struct_nested_i32", "struct_nested_i32_i32", "struct_2_nested_i32_i32", "struct_member_i32", + "struct_member_i32_neg", "struct_member_i32_i32", "struct_member_nested_i32", "struct_member_nested_i32_i32", @@ -672,6 +674,84 @@ "declare_w_value", "redeclare_in_scope" ] + }, { + "id": "DeserExprOperators", + "testcases": [ + "add_2_int_literals", + "add_2_int_literals_twice", + "sub_2_int_literals", + "mul_2_int_literals", + "div_2_int_literals", + "add_3_int_literals", + "add_3_int_literals_twice", + "sub_3_int_literals", + "mul_3_int_literals", + "div_3_int_literals", + "int_to_bool", + "add_mul_3_int_literals", + "sub_mul_3_int_literals", + "div_mul_3_int_literals", + "add_div_3_int_literals", + "sub_div_3_int_literals", + "mul_div_3_int_literals", + "mul_add_mul_add_int_literals", + "mul_sub_mul_sub_int_literals", + "mul_div_mul_div_int_literals", + "div_add_div_add_int_literals", + "div_sub_div_sub_int_literals", + "div_sub_div_mul_int_literals", + "div_mul_div_mul_int_literals", + "add_2_flt_literals", + "sub_2_flt_literals", + "mul_2_flt_literals", + "div_2_flt_literals", + "add_2_int_neg_literals", + "sub_2_int_neg_literals", + "mul_2_int_neg_literals", + "div_2_int_neg_literals", + "mul_lparen_add_add_rparen_int_literals", + "mul_lparen_add_add_add_rparen_int_literals", + "mul_lparen_add_add_rparen_add_int_literals", + "lparen_add_add_rparen_mul_int_literals", + "lparen_add_add_add_rparen_mul_int_literals", + "double_paren_add_add", + "double_paren_literal", + "lparen_add_add_rparen_mul_lparen_add_add_rparen", + "float_result_add_2_int_literals", + "struct_result_add_2_int_literals", + "struct_result_add_2_2_fields_int_literals", + "struct_result_add_3_int_literals", + "add_to_var", + "add_var_to", + "bool_cond_and_bool", + "bool_cond_or_bool", + "int_cond_and_int", + "int_cond_or_int", + "bool_cond_and_int", + "int_cond_and_bool", + "bool_cond_or_int", + "int_cond_or_bool", + "cond_eq_bool", + "cond_eq_int", + "cond_neq_bool", + "cond_neq_int", + "cond_eq_bool_int", + "cond_eq_int_flt", + "cond_eq_cond_and", + "cond_eq_cond_or", + "cond_gt_bool", + "cond_gt_int", + "cond_gt_flt", + "cond_gteq_bool", + "cond_gteq_int", + "cond_gteq_flt", + "cond_lt_bool", + "cond_lt_int", + "cond_lt_flt", + "cond_lteq_bool", + "cond_lteq_int", + "cond_lteq_flt" + ] }] } } diff --git a/test/meta/src/DeserExprOperators.c b/test/meta/src/DeserExprOperators.c new file mode 100644 index 0000000000..53b9f7e4d6 --- /dev/null +++ b/test/meta/src/DeserExprOperators.c @@ -0,0 +1,1354 @@ +#include + +void DeserExprOperators_add_2_int_literals() { + ecs_world_t *world = ecs_init(); + + ecs_value_t v = {0}; + test_assert(ecs_parse_expr(world, "10 + 20", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_u64_t)); + test_assert(v.ptr != NULL); + test_uint(*(uint64_t*)v.ptr, 10 + 20); + + ecs_fini(world); +} + +void DeserExprOperators_add_2_int_literals_twice() { + ecs_world_t *world = ecs_init(); + + ecs_value_t v = {0}; + test_assert(ecs_parse_expr(world, "10 + 20", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_u64_t)); + test_assert(v.ptr != NULL); + test_uint(*(uint64_t*)v.ptr, 10 + 20); + + test_assert(ecs_parse_expr(world, "10 + 20", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_u64_t)); + test_assert(v.ptr != NULL); + test_uint(*(uint64_t*)v.ptr, 10 + 20); + + ecs_fini(world); +} + +void DeserExprOperators_sub_2_int_literals() { + ecs_world_t *world = ecs_init(); + + ecs_value_t v = {0}; + test_assert(ecs_parse_expr(world, "20 - 10", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_i64_t)); + test_assert(v.ptr != NULL); + test_int(*(int64_t*)v.ptr, 20 - 10); + + ecs_fini(world); +} + +void DeserExprOperators_mul_2_int_literals() { + ecs_world_t *world = ecs_init(); + + ecs_value_t v = {0}; + test_assert(ecs_parse_expr(world, "20 * 10", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_u64_t)); + test_assert(v.ptr != NULL); + test_uint(*(ecs_u64_t*)v.ptr, 20 * 10); + + ecs_fini(world); +} + +void DeserExprOperators_div_2_int_literals() { + ecs_world_t *world = ecs_init(); + + ecs_value_t v = {0}; + test_assert(ecs_parse_expr(world, "10 / 2", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_f64_t)); + test_assert(v.ptr != NULL); + test_flt(*(ecs_f64_t*)v.ptr, 10 / 2); + + ecs_fini(world); +} + +void DeserExprOperators_add_3_int_literals() { + ecs_world_t *world = ecs_init(); + + ecs_value_t v = {0}; + test_assert(ecs_parse_expr(world, "10 + 20 + 30", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_u64_t)); + test_assert(v.ptr != NULL); + test_uint(*(uint64_t*)v.ptr, 10 + 20 + 30); + + ecs_fini(world); +} + +void DeserExprOperators_add_3_int_literals_twice() { + ecs_world_t *world = ecs_init(); + + ecs_value_t v = {0}; + test_assert(ecs_parse_expr(world, "10 + 20 + 30", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_u64_t)); + test_assert(v.ptr != NULL); + test_uint(*(uint64_t*)v.ptr, 10 + 20 + 30); + + test_assert(ecs_parse_expr(world, "10 + 20 + 30", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_u64_t)); + test_assert(v.ptr != NULL); + test_uint(*(uint64_t*)v.ptr, 10 + 20 + 30); + + ecs_fini(world); +} + +void DeserExprOperators_sub_3_int_literals() { + ecs_world_t *world = ecs_init(); + + ecs_value_t v = {0}; + test_assert(ecs_parse_expr(world, "30 - 10 - 5", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_i64_t)); + test_assert(v.ptr != NULL); + test_int(*(ecs_i64_t*)v.ptr, 30 - 10 - 5); + + ecs_fini(world); +} + +void DeserExprOperators_mul_3_int_literals() { + ecs_world_t *world = ecs_init(); + + ecs_value_t v = {0}; + test_assert(ecs_parse_expr(world, "2 * 5 * 10", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_u64_t)); + test_assert(v.ptr != NULL); + test_uint(*(ecs_u64_t*)v.ptr, 2 * 5 * 10); + + ecs_fini(world); +} + +void DeserExprOperators_div_3_int_literals() { + ecs_world_t *world = ecs_init(); + + ecs_value_t v = {0}; + test_assert(ecs_parse_expr(world, "40 / 5 / 2", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_f64_t)); + test_assert(v.ptr != NULL); + test_flt(*(ecs_f64_t*)v.ptr, 40 / 5 / 2); + + ecs_fini(world); +} + +void DeserExprOperators_int_to_bool() { + ecs_world_t *world = ecs_init(); + + bool b = false; + ecs_value_t v = { .type = ecs_id(ecs_bool_t), .ptr = &b }; + + test_assert(ecs_parse_expr(world, "10", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_flt(*(ecs_bool_t*)v.ptr, true); + + test_assert(ecs_parse_expr(world, "0", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_flt(*(ecs_bool_t*)v.ptr, false); + + test_assert(ecs_parse_expr(world, "256", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_flt(*(ecs_bool_t*)v.ptr, true); + + ecs_fini(world); +} + +void DeserExprOperators_add_mul_3_int_literals() { + ecs_world_t *world = ecs_init(); + + ecs_value_t v = {0}; + test_assert(ecs_parse_expr(world, "10 + 20 * 2", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_u64_t)); + test_assert(v.ptr != NULL); + test_uint(*(uint64_t*)v.ptr, 10 + 20 * 2); + + ecs_fini(world); +} + +void DeserExprOperators_sub_mul_3_int_literals() { + ecs_world_t *world = ecs_init(); + + ecs_value_t v = {0}; + test_assert(ecs_parse_expr(world, "50 - 10 * 2", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_i64_t)); + test_assert(v.ptr != NULL); + test_int(*(int64_t*)v.ptr, 50 - 10 * 2); + + ecs_fini(world); +} + +void DeserExprOperators_div_mul_3_int_literals() { + ecs_world_t *world = ecs_init(); + + ecs_value_t v = {0}; + test_assert(ecs_parse_expr(world, "10 / 5 * 2", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_f64_t)); + test_assert(v.ptr != NULL); + test_flt(*(ecs_f64_t*)v.ptr, 10 / 5 * 2); + + ecs_fini(world); +} + +void DeserExprOperators_add_div_3_int_literals() { + ecs_world_t *world = ecs_init(); + + ecs_value_t v = {0}; + test_assert(ecs_parse_expr(world, "10 + 30 / 2", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_f64_t)); + test_assert(v.ptr != NULL); + test_uint(*(ecs_f64_t*)v.ptr, 10 + 30 / 2); + + ecs_fini(world); +} + +void DeserExprOperators_sub_div_3_int_literals() { + ecs_world_t *world = ecs_init(); + + ecs_value_t v = {0}; + test_assert(ecs_parse_expr(world, "30 - 10 / 2", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_f64_t)); + test_assert(v.ptr != NULL); + test_uint(*(ecs_f64_t*)v.ptr, 30 - 10 / 2); + + ecs_fini(world); +} + +void DeserExprOperators_mul_div_3_int_literals() { + ecs_world_t *world = ecs_init(); + + ecs_value_t v = {0}; + test_assert(ecs_parse_expr(world, "20 * 10 / 2", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_f64_t)); + test_assert(v.ptr != NULL); + test_uint(*(ecs_f64_t*)v.ptr, 20 * 10 / 2); + + ecs_fini(world); +} + +void DeserExprOperators_mul_add_mul_add_int_literals() { + ecs_world_t *world = ecs_init(); + + ecs_value_t v = {0}; + test_assert(ecs_parse_expr(world, "2 * 4 + 6 * 8 + 10", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_u64_t)); + test_assert(v.ptr != NULL); + test_uint(*(ecs_u64_t*)v.ptr, 2 * 4 + 6 * 8 + 10); + + ecs_fini(world); +} + +void DeserExprOperators_mul_sub_mul_sub_int_literals() { + ecs_world_t *world = ecs_init(); + + ecs_value_t v = {0}; + test_assert(ecs_parse_expr(world, "2 * 4 - 6 * 8 - 10", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_i64_t)); + test_assert(v.ptr != NULL); + test_int(*(ecs_i64_t*)v.ptr, 2 * 4 - 6 * 8 - 10); + + ecs_fini(world); +} + +void DeserExprOperators_mul_div_mul_div_int_literals() { + ecs_world_t *world = ecs_init(); + + ecs_value_t v = {0}; + test_assert(ecs_parse_expr(world, "2 * 4 / 6 * 8 / 10", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_f64_t)); + test_assert(v.ptr != NULL); + test_flt(*(ecs_f64_t*)v.ptr, 2.0 * 4.0 / 6.0 * 8.0 / 10.0); + + ecs_fini(world); +} + +void DeserExprOperators_div_add_div_add_int_literals() { + ecs_world_t *world = ecs_init(); + + ecs_value_t v = {0}; + test_assert(ecs_parse_expr(world, "2 / 4 + 6 / 8 + 10", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_f64_t)); + test_assert(v.ptr != NULL); + test_flt(*(ecs_f64_t*)v.ptr, 2.0 / 4.0 + 6.0 / 8.0 + 10.0); + + ecs_fini(world); +} + +void DeserExprOperators_div_sub_div_sub_int_literals() { + ecs_world_t *world = ecs_init(); + + ecs_value_t v = {0}; + test_assert(ecs_parse_expr(world, "2 / 4 - 6 / 8 - 10", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_f64_t)); + test_assert(v.ptr != NULL); + test_flt(*(ecs_f64_t*)v.ptr, 2.0 / 4.0 - 6.0 / 8.0 - 10.0); + + ecs_fini(world); +} + +void DeserExprOperators_div_mul_div_mul_int_literals() { + ecs_world_t *world = ecs_init(); + + ecs_value_t v = {0}; + test_assert(ecs_parse_expr(world, "2 / 4 * 6 / 8 * 10", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_f64_t)); + test_assert(v.ptr != NULL); + test_flt(*(ecs_f64_t*)v.ptr, 2.0 / 4.0 * 6.0 / 8.0 * 10.0); + + ecs_fini(world); +} + +void DeserExprOperators_div_sub_div_mul_int_literals() { + ecs_world_t *world = ecs_init(); + + ecs_value_t v = {0}; + test_assert(ecs_parse_expr(world, "2 / 4 - 6 / 8 * 10", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_f64_t)); + test_assert(v.ptr != NULL); + test_flt(*(ecs_f64_t*)v.ptr, 2.0 / 4.0 - 6.0 / 8.0 * 10.0); + + ecs_fini(world); +} + +void DeserExprOperators_add_2_flt_literals() { + ecs_world_t *world = ecs_init(); + + ecs_value_t v = {0}; + test_assert(ecs_parse_expr(world, "10.5 + 20.0", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_f64_t)); + test_assert(v.ptr != NULL); + test_flt(*(ecs_f64_t*)v.ptr, 10.5 + 20.0); + + ecs_fini(world); +} + +void DeserExprOperators_sub_2_flt_literals() { + ecs_world_t *world = ecs_init(); + + ecs_value_t v = {0}; + test_assert(ecs_parse_expr(world, "20.5 - 10.0", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_f64_t)); + test_assert(v.ptr != NULL); + test_flt(*(ecs_f64_t*)v.ptr, 20.5 - 10.0); + + ecs_fini(world); +} + +void DeserExprOperators_mul_2_flt_literals() { + ecs_world_t *world = ecs_init(); + + ecs_value_t v = {0}; + test_assert(ecs_parse_expr(world, "20.5 * 10.0", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_f64_t)); + test_assert(v.ptr != NULL); + test_flt(*(ecs_f64_t*)v.ptr, 20.5 * 10.0); + + ecs_fini(world); +} + +void DeserExprOperators_div_2_flt_literals() { + ecs_world_t *world = ecs_init(); + + ecs_value_t v = {0}; + test_assert(ecs_parse_expr(world, "10.5 / 2.0", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_f64_t)); + test_assert(v.ptr != NULL); + test_flt(*(ecs_f64_t*)v.ptr, 10.5 / 2.0); + + ecs_fini(world); +} + +void DeserExprOperators_add_2_int_neg_literals() { + ecs_world_t *world = ecs_init(); + + ecs_value_t v = {0}; + test_assert(ecs_parse_expr(world, "-10 + -20", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_i64_t)); + test_assert(v.ptr != NULL); + test_int(*(ecs_i64_t*)v.ptr, -10 + -20); + + ecs_fini(world); +} + +void DeserExprOperators_sub_2_int_neg_literals() { + ecs_world_t *world = ecs_init(); + + ecs_value_t v = {0}; + test_assert(ecs_parse_expr(world, "-10 - -20", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_i64_t)); + test_assert(v.ptr != NULL); + test_int(*(ecs_i64_t*)v.ptr, -10 - -20); + + ecs_fini(world); +} + +void DeserExprOperators_mul_2_int_neg_literals() { + ecs_world_t *world = ecs_init(); + + ecs_value_t v = {0}; + test_assert(ecs_parse_expr(world, "-10 * -20", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_i64_t)); + test_assert(v.ptr != NULL); + test_int(*(ecs_i64_t*)v.ptr, -10 * -20); + + ecs_fini(world); +} + +void DeserExprOperators_div_2_int_neg_literals() { + ecs_world_t *world = ecs_init(); + + ecs_value_t v = {0}; + test_assert(ecs_parse_expr(world, "-10 / -20", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_f64_t)); + test_assert(v.ptr != NULL); + test_flt(*(ecs_f64_t*)v.ptr, -10.0 / -20.0); + + ecs_fini(world); +} + +void DeserExprOperators_mul_lparen_add_add_rparen_int_literals() { + ecs_world_t *world = ecs_init(); + + ecs_value_t v = {0}; + test_assert(ecs_parse_expr(world, "10 * (20 + 30)", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_u64_t)); + test_assert(v.ptr != NULL); + test_uint(*(ecs_u64_t*)v.ptr, 10 * (20 + 30)); + + ecs_fini(world); +} + +void DeserExprOperators_mul_lparen_add_add_add_rparen_int_literals() { + ecs_world_t *world = ecs_init(); + + ecs_value_t v = {0}; + test_assert(ecs_parse_expr(world, "10 * (20 + 30 + 40)", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_u64_t)); + test_assert(v.ptr != NULL); + test_uint(*(ecs_u64_t*)v.ptr, 10 * (20 + 30 + 40)); + + ecs_fini(world); +} + +void DeserExprOperators_mul_lparen_add_add_rparen_add_int_literals() { + ecs_world_t *world = ecs_init(); + + ecs_value_t v = {0}; + test_assert(ecs_parse_expr(world, "10 * (20 + 30) + 40", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_u64_t)); + test_assert(v.ptr != NULL); + test_uint(*(ecs_u64_t*)v.ptr, 10 * (20 + 30) + 40); + + ecs_fini(world); +} + +void DeserExprOperators_lparen_add_add_rparen_mul_int_literals() { + ecs_world_t *world = ecs_init(); + + ecs_value_t v = {0}; + test_assert(ecs_parse_expr(world, "(20 + 30) * 10", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_u64_t)); + test_assert(v.ptr != NULL); + test_uint(*(ecs_u64_t*)v.ptr, (20 + 30) * 10); + + ecs_fini(world); +} + +void DeserExprOperators_lparen_add_add_add_rparen_mul_int_literals() { + ecs_world_t *world = ecs_init(); + + ecs_value_t v = {0}; + test_assert(ecs_parse_expr(world, "(20 + 30 + 40) * 10", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_u64_t)); + test_assert(v.ptr != NULL); + test_uint(*(ecs_u64_t*)v.ptr, (20 + 30 + 40) * 10); + + ecs_fini(world); +} + +void DeserExprOperators_double_paren_add_add() { + ecs_world_t *world = ecs_init(); + + ecs_value_t v = {0}; + test_assert(ecs_parse_expr(world, "((20 + 30))", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_u64_t)); + test_assert(v.ptr != NULL); + test_uint(*(ecs_u64_t*)v.ptr, ((20 + 30))); + + ecs_fini(world); +} + +void DeserExprOperators_double_paren_literal() { + ecs_world_t *world = ecs_init(); + + ecs_value_t v = {0}; + test_assert(ecs_parse_expr(world, "((20))", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_u64_t)); + test_assert(v.ptr != NULL); + test_uint(*(ecs_u64_t*)v.ptr, ((20))); + + ecs_fini(world); +} + +void DeserExprOperators_lparen_add_add_rparen_mul_lparen_add_add_rparen() { + ecs_world_t *world = ecs_init(); + + ecs_value_t v = {0}; + test_assert(ecs_parse_expr(world, "(10 + 20) * (20 + 30)", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_u64_t)); + test_assert(v.ptr != NULL); + test_uint(*(ecs_u64_t*)v.ptr, (10 + 20) * (20 + 30)); + + ecs_fini(world); +} + +void DeserExprOperators_float_result_add_2_int_literals() { + ecs_world_t *world = ecs_init(); + + float value = 0; + ecs_value_t v = { + .type = ecs_id(ecs_f32_t), + .ptr = &value + }; + test_assert(ecs_parse_expr(world, "10 + 20", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_f32_t)); + test_assert(v.ptr != NULL); + test_flt(value, 10 + 20); + + ecs_fini(world); +} + +void DeserExprOperators_struct_result_add_2_int_literals() { + ecs_world_t *world = ecs_init(); + + typedef struct { + int32_t value; + } Mass; + + ecs_entity_t t = ecs_struct_init(world, &(ecs_struct_desc_t){ + .members = { + {"value", ecs_id(ecs_i32_t)} + } + }); + + Mass v = {0}; + const char *ptr = ecs_parse_expr(world, "{10 + 20}", &(ecs_value_t){ + .type = t, .ptr = &v + }, NULL); + test_assert(ptr != NULL); + test_assert(!ptr[0]); + + test_uint(v.value, 10 + 20); + + ecs_fini(world); +} + +void DeserExprOperators_struct_result_add_2_2_fields_int_literals() { + ecs_world_t *world = ecs_init(); + + typedef struct { + int32_t x; + int32_t y; + } Mass; + + ecs_entity_t t = ecs_struct_init(world, &(ecs_struct_desc_t){ + .members = { + {"x", ecs_id(ecs_i32_t)}, + {"y", ecs_id(ecs_i32_t)} + } + }); + + Mass v = {0}; + const char *ptr = ecs_parse_expr(world, "{10 + 20, 20 + 30}", &(ecs_value_t){ + .type = t, .ptr = &v + }, NULL); + test_assert(ptr != NULL); + test_assert(!ptr[0]); + + test_uint(v.x, 10 + 20); + test_uint(v.y, 20 + 30); + + ecs_fini(world); +} + +void DeserExprOperators_struct_result_add_3_int_literals() { + ecs_world_t *world = ecs_init(); + + typedef struct { + int32_t value; + } Mass; + + ecs_entity_t t = ecs_struct_init(world, &(ecs_struct_desc_t){ + .members = { + {"value", ecs_id(ecs_i32_t)} + } + }); + + Mass v = {0}; + const char *ptr = ecs_parse_expr(world, "{10 + 20 + 30}", &(ecs_value_t){ + .type = t, .ptr = &v + }, NULL); + test_assert(ptr != NULL); + test_assert(!ptr[0]); + + test_uint(v.value, 10 + 20 + 30); + + ecs_fini(world); +} + +void DeserExprOperators_add_to_var() { + ecs_world_t *world = ecs_init(); + + ecs_vars_t vars = {0}; + ecs_vars_init(world, &vars); + + ecs_expr_var_t *var = ecs_vars_declare( + &vars, "foo", ecs_id(ecs_i32_t)); + *(int32_t*)var->value.ptr = 10; + + ecs_value_t v = {0}; + ecs_parse_expr_desc_t desc = { .vars = &vars }; + const char *ptr = ecs_parse_expr(world, "$foo + 20", &v, &desc); + test_assert(ptr != NULL); + test_assert(!ptr[0]); + test_int(*(int32_t*)v.ptr, 10 + 20); + + ecs_value_free(world, v.type, v.ptr); + ecs_vars_fini(&vars); + ecs_fini(world); +} + +void DeserExprOperators_add_var_to() { + ecs_world_t *world = ecs_init(); + + ecs_vars_t vars = {0}; + ecs_vars_init(world, &vars); + + ecs_expr_var_t *var = ecs_vars_declare( + &vars, "foo", ecs_id(ecs_i32_t)); + *(int32_t*)var->value.ptr = 10; + + ecs_value_t v = {0}; + ecs_parse_expr_desc_t desc = { .vars = &vars }; + const char *ptr = ecs_parse_expr(world, "20 + $foo", &v, &desc); + test_assert(ptr != NULL); + test_assert(!ptr[0]); + test_int(*(int32_t*)v.ptr, 20 + 10); + + ecs_value_free(world, v.type, v.ptr); + ecs_vars_fini(&vars); + ecs_fini(world); +} + +void DeserExprOperators_bool_cond_and_bool() { + ecs_world_t *world = ecs_init(); + + ecs_value_t v = {0}; + test_assert(ecs_parse_expr(world, "true && true", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, true); + + test_assert(ecs_parse_expr(world, "true && false", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, false); + + test_assert(ecs_parse_expr(world, "false && true", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, false); + + test_assert(ecs_parse_expr(world, "false && false", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, false); + + ecs_fini(world); +} + +void DeserExprOperators_bool_cond_or_bool() { + ecs_world_t *world = ecs_init(); + + ecs_value_t v = {0}; + test_assert(ecs_parse_expr(world, "true || true", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, true); + + test_assert(ecs_parse_expr(world, "true || false", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, true); + + test_assert(ecs_parse_expr(world, "false || true", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, true); + + test_assert(ecs_parse_expr(world, "false || false", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, false); + + ecs_fini(world); +} + +void DeserExprOperators_int_cond_and_int() { + ecs_world_t *world = ecs_init(); + + ecs_value_t v = {0}; + test_assert(ecs_parse_expr(world, "10 && 10", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, true); + + test_assert(ecs_parse_expr(world, "10 && 0", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, false); + + test_assert(ecs_parse_expr(world, "0 && 10", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, false); + + test_assert(ecs_parse_expr(world, "0 && 0", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, false); + + ecs_fini(world); +} + +void DeserExprOperators_int_cond_or_int() { + ecs_world_t *world = ecs_init(); + + ecs_value_t v = {0}; + test_assert(ecs_parse_expr(world, "10 || 10", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, true); + + test_assert(ecs_parse_expr(world, "10 || 0", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, true); + + test_assert(ecs_parse_expr(world, "0 || 10", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, true); + + test_assert(ecs_parse_expr(world, "0 || 0", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, false); + + ecs_fini(world); +} + +void DeserExprOperators_bool_cond_and_int() { + ecs_world_t *world = ecs_init(); + + ecs_value_t v = {0}; + test_assert(ecs_parse_expr(world, "true && 10", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, true); + + test_assert(ecs_parse_expr(world, "true && 0", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, false); + + test_assert(ecs_parse_expr(world, "false && 10", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, false); + + test_assert(ecs_parse_expr(world, "false && 0", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, false); + + ecs_fini(world); +} + +void DeserExprOperators_int_cond_and_bool() { + ecs_world_t *world = ecs_init(); + + ecs_value_t v = {0}; + test_assert(ecs_parse_expr(world, "10 && true", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, true); + + test_assert(ecs_parse_expr(world, "10 && false", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, false); + + test_assert(ecs_parse_expr(world, "0 && true", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, false); + + test_assert(ecs_parse_expr(world, "0 && false", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, false); + + ecs_fini(world); +} + +void DeserExprOperators_bool_cond_or_int() { + ecs_world_t *world = ecs_init(); + + ecs_value_t v = {0}; + test_assert(ecs_parse_expr(world, "true || 10", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, true); + + test_assert(ecs_parse_expr(world, "true || 0", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, true); + + test_assert(ecs_parse_expr(world, "false || 10", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, true); + + test_assert(ecs_parse_expr(world, "false || 0", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, false); + + ecs_fini(world); +} + +void DeserExprOperators_int_cond_or_bool() { + ecs_world_t *world = ecs_init(); + + ecs_value_t v = {0}; + test_assert(ecs_parse_expr(world, "10 || true", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, true); + + test_assert(ecs_parse_expr(world, "10 || false", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, true); + + test_assert(ecs_parse_expr(world, "0 || true", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, true); + + test_assert(ecs_parse_expr(world, "0 || false", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, false); + + ecs_fini(world); +} + +void DeserExprOperators_cond_eq_bool() { + ecs_world_t *world = ecs_init(); + + ecs_value_t v = {0}; + test_assert(ecs_parse_expr(world, "true == true", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, true); + + test_assert(ecs_parse_expr(world, "true == false", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, false); + + test_assert(ecs_parse_expr(world, "false == true", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, false); + + test_assert(ecs_parse_expr(world, "false == false", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, true); + + ecs_fini(world); +} + +void DeserExprOperators_cond_eq_int() { + ecs_world_t *world = ecs_init(); + + ecs_value_t v = {0}; + test_assert(ecs_parse_expr(world, "10 == 10", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, true); + + test_assert(ecs_parse_expr(world, "10 == 20", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, false); + + test_assert(ecs_parse_expr(world, "10 == 0", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, false); + + test_assert(ecs_parse_expr(world, "0 == 0", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, true); + + ecs_fini(world); +} + +void DeserExprOperators_cond_neq_bool() { + ecs_world_t *world = ecs_init(); + + ecs_value_t v = {0}; + test_assert(ecs_parse_expr(world, "true != true", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, false); + + test_assert(ecs_parse_expr(world, "true != false", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, true); + + test_assert(ecs_parse_expr(world, "false != true", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, true); + + test_assert(ecs_parse_expr(world, "false != false", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, false); + + ecs_fini(world); +} + +void DeserExprOperators_cond_neq_int() { + ecs_world_t *world = ecs_init(); + + ecs_value_t v = {0}; + test_assert(ecs_parse_expr(world, "10 != 10", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, false); + + test_assert(ecs_parse_expr(world, "10 != 20", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, true); + + test_assert(ecs_parse_expr(world, "10 != 0", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, true); + + test_assert(ecs_parse_expr(world, "0 != 0", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, false); + + ecs_fini(world); +} + +void DeserExprOperators_cond_eq_bool_int() { + ecs_world_t *world = ecs_init(); + + ecs_value_t v = {0}; + + ecs_log_set_level(-4); + test_assert(ecs_parse_expr(world, "true == 1", &v, NULL) == NULL); + test_assert(ecs_parse_expr(world, "true == 0", &v, NULL) == NULL); + test_assert(ecs_parse_expr(world, "false == 0", &v, NULL) == NULL); + + ecs_fini(world); +} + +void DeserExprOperators_cond_eq_int_flt() { + ecs_world_t *world = ecs_init(); + + ecs_value_t v = {0}; + + ecs_log_set_level(-4); + test_assert(ecs_parse_expr(world, "1 == 1.0", &v, NULL) == NULL); + test_assert(ecs_parse_expr(world, "1 == 0.0", &v, NULL) == NULL); + test_assert(ecs_parse_expr(world, "0 == 0.0", &v, NULL) == NULL); + + ecs_fini(world); +} + +void DeserExprOperators_cond_eq_cond_and() { + ecs_world_t *world = ecs_init(); + + ecs_value_t v = {0}; + test_assert(ecs_parse_expr(world, "true == true && false", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, true == true && false); + + test_assert(ecs_parse_expr(world, "true && false == false", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, true && false == false); + + test_assert(ecs_parse_expr(world, "true && true == true", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, true && true == true); + + ecs_fini(world); +} + +void DeserExprOperators_cond_eq_cond_or() { + ecs_world_t *world = ecs_init(); + + ecs_value_t v = {0}; + test_assert(ecs_parse_expr(world, "true == true || false", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, true == true || false); + + test_assert(ecs_parse_expr(world, "true || false == false", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, true || false == false); + + ecs_fini(world); +} + +void DeserExprOperators_cond_gt_bool() { + ecs_world_t *world = ecs_init(); + + ecs_value_t v = {0}; + test_assert(ecs_parse_expr(world, "true > false", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, true); + + test_assert(ecs_parse_expr(world, "true > true", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, false); + + test_assert(ecs_parse_expr(world, "false > true", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, false); + + test_assert(ecs_parse_expr(world, "false > false", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, false); + + ecs_fini(world); +} + +void DeserExprOperators_cond_gt_int() { + ecs_world_t *world = ecs_init(); + + ecs_value_t v = {0}; + test_assert(ecs_parse_expr(world, "10 > 5", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, true); + + test_assert(ecs_parse_expr(world, "10 > 10", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, false); + + test_assert(ecs_parse_expr(world, "5 > 10", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, false); + + test_assert(ecs_parse_expr(world, "5 > 5", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, false); + + ecs_fini(world); +} + +void DeserExprOperators_cond_gt_flt() { + ecs_world_t *world = ecs_init(); + + ecs_value_t v = {0}; + test_assert(ecs_parse_expr(world, "10.5 > 5.5", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, true); + + test_assert(ecs_parse_expr(world, "10.5 > 10.5", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, false); + + test_assert(ecs_parse_expr(world, "5.5 > 10.5", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, false); + + test_assert(ecs_parse_expr(world, "5.5 > 5.5", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, false); + + ecs_fini(world); +} + +void DeserExprOperators_cond_gteq_bool() { + ecs_world_t *world = ecs_init(); + + ecs_value_t v = {0}; + test_assert(ecs_parse_expr(world, "true >= false", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, true); + + test_assert(ecs_parse_expr(world, "true >= true", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, true); + + test_assert(ecs_parse_expr(world, "false >= true", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, false); + + test_assert(ecs_parse_expr(world, "false >= false", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, true); + + ecs_fini(world); +} + +void DeserExprOperators_cond_gteq_int() { + ecs_world_t *world = ecs_init(); + + ecs_value_t v = {0}; + test_assert(ecs_parse_expr(world, "10 >= 5", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, true); + + test_assert(ecs_parse_expr(world, "10 >= 10", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, true); + + test_assert(ecs_parse_expr(world, "5 >= 10", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, false); + + test_assert(ecs_parse_expr(world, "5 >= 5", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, true); + + ecs_fini(world); +} + +void DeserExprOperators_cond_gteq_flt() { + ecs_world_t *world = ecs_init(); + + ecs_value_t v = {0}; + test_assert(ecs_parse_expr(world, "10.5 >= 5.5", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, true); + + test_assert(ecs_parse_expr(world, "10.5 >= 10.5", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, true); + + test_assert(ecs_parse_expr(world, "5.5 >= 10.5", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, false); + + test_assert(ecs_parse_expr(world, "5.5 >= 5.5", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, true); + + ecs_fini(world); +} + +void DeserExprOperators_cond_lt_bool() { + ecs_world_t *world = ecs_init(); + + ecs_value_t v = {0}; + test_assert(ecs_parse_expr(world, "true < false", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, false); + + test_assert(ecs_parse_expr(world, "true < true", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, false); + + test_assert(ecs_parse_expr(world, "false < true", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, true); + + test_assert(ecs_parse_expr(world, "false < false", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, false); + + ecs_fini(world); +} + +void DeserExprOperators_cond_lt_int() { + ecs_world_t *world = ecs_init(); + + ecs_value_t v = {0}; + test_assert(ecs_parse_expr(world, "10 < 5", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, false); + + test_assert(ecs_parse_expr(world, "10 < 10", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, false); + + test_assert(ecs_parse_expr(world, "5 < 10", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, true); + + test_assert(ecs_parse_expr(world, "5 < 5", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, false); + + ecs_fini(world); +} + +void DeserExprOperators_cond_lt_flt() { + ecs_world_t *world = ecs_init(); + + ecs_value_t v = {0}; + test_assert(ecs_parse_expr(world, "10.5 < 5.5", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, false); + + test_assert(ecs_parse_expr(world, "10.5 < 10.5", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, false); + + test_assert(ecs_parse_expr(world, "5.5 < 10.5", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, true); + + test_assert(ecs_parse_expr(world, "5.5 < 5.5", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, false); + + ecs_fini(world); +} + +void DeserExprOperators_cond_lteq_bool() { + ecs_world_t *world = ecs_init(); + + ecs_value_t v = {0}; + test_assert(ecs_parse_expr(world, "true <= false", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, false); + + test_assert(ecs_parse_expr(world, "true <= true", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, true); + + test_assert(ecs_parse_expr(world, "false <= true", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, true); + + test_assert(ecs_parse_expr(world, "false <= false", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, true); + + ecs_fini(world); +} + +void DeserExprOperators_cond_lteq_int() { + ecs_world_t *world = ecs_init(); + + ecs_value_t v = {0}; + test_assert(ecs_parse_expr(world, "10 <= 5", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, false); + + test_assert(ecs_parse_expr(world, "10 <= 10", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, true); + + test_assert(ecs_parse_expr(world, "5 <= 10", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, true); + + test_assert(ecs_parse_expr(world, "5 <= 5", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, true); + + ecs_fini(world); +} + +void DeserExprOperators_cond_lteq_flt() { + ecs_world_t *world = ecs_init(); + + ecs_value_t v = {0}; + test_assert(ecs_parse_expr(world, "10.5 <= 5.5", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, false); + + test_assert(ecs_parse_expr(world, "10.5 <= 10.5", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, true); + + test_assert(ecs_parse_expr(world, "5.5 <= 10.5", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, true); + + test_assert(ecs_parse_expr(world, "5.5 <= 5.5", &v, NULL) != NULL); + test_assert(v.type == ecs_id(ecs_bool_t)); + test_assert(v.ptr != NULL); + test_bool(*(bool*)v.ptr, true); + + ecs_fini(world); +} diff --git a/test/meta/src/DeserializeFromExpr.c b/test/meta/src/DeserializeFromExpr.c index 09b18fad7c..07d4ac5aab 100644 --- a/test/meta/src/DeserializeFromExpr.c +++ b/test/meta/src/DeserializeFromExpr.c @@ -530,6 +530,31 @@ void DeserializeFromExpr_struct_i32() { ecs_fini(world); } +void DeserializeFromExpr_struct_i32_neg() { + typedef struct { + int32_t x; + } T; + + ecs_world_t *world = ecs_init(); + + ecs_entity_t t = ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_entity(world, {.name = "T"}), + .members = { + {"x", ecs_id(ecs_i32_t)} + } + }); + + T value = {0}; + + const char *ptr = ecs_parse_expr(world, "{-10}", &(ecs_value_t){t, &value}, NULL); + test_assert(ptr != NULL); + test_assert(ptr[0] == '\0'); + + test_int(value.x, -10); + + ecs_fini(world); +} + void DeserializeFromExpr_struct_i32_i32() { typedef struct { int32_t x; @@ -726,6 +751,31 @@ void DeserializeFromExpr_struct_member_i32() { ecs_fini(world); } +void DeserializeFromExpr_struct_member_i32_neg() { + typedef struct { + int32_t x; + } T; + + ecs_world_t *world = ecs_init(); + + ecs_entity_t t = ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_entity(world, {.name = "T"}), + .members = { + {"x", ecs_id(ecs_i32_t)} + } + }); + + T value = {0}; + + const char *ptr = ecs_parse_expr(world, "{x: -10}", &(ecs_value_t){t, &value}, NULL); + test_assert(ptr != NULL); + test_assert(ptr[0] == '\0'); + + test_int(value.x, -10); + + ecs_fini(world); +} + void DeserializeFromExpr_struct_member_i32_i32() { typedef struct { int32_t x; diff --git a/test/meta/src/main.c b/test/meta/src/main.c index a76dba7d04..1445fc28c2 100644 --- a/test/meta/src/main.c +++ b/test/meta/src/main.c @@ -348,12 +348,14 @@ void DeserializeFromExpr_bitmask(void); void DeserializeFromExpr_struct_enum(void); void DeserializeFromExpr_struct_bitmask(void); void DeserializeFromExpr_struct_i32(void); +void DeserializeFromExpr_struct_i32_neg(void); void DeserializeFromExpr_struct_i32_i32(void); void DeserializeFromExpr_struct_entity(void); void DeserializeFromExpr_struct_nested_i32(void); void DeserializeFromExpr_struct_nested_i32_i32(void); void DeserializeFromExpr_struct_2_nested_i32_i32(void); void DeserializeFromExpr_struct_member_i32(void); +void DeserializeFromExpr_struct_member_i32_neg(void); void DeserializeFromExpr_struct_member_i32_i32(void); void DeserializeFromExpr_struct_member_nested_i32(void); void DeserializeFromExpr_struct_member_nested_i32_i32(void); @@ -635,6 +637,82 @@ void Vars_nested_struct_expr_w_struct_var(void); void Vars_declare_w_value(void); void Vars_redeclare_in_scope(void); +// Testsuite 'DeserExprOperators' +void DeserExprOperators_add_2_int_literals(void); +void DeserExprOperators_add_2_int_literals_twice(void); +void DeserExprOperators_sub_2_int_literals(void); +void DeserExprOperators_mul_2_int_literals(void); +void DeserExprOperators_div_2_int_literals(void); +void DeserExprOperators_add_3_int_literals(void); +void DeserExprOperators_add_3_int_literals_twice(void); +void DeserExprOperators_sub_3_int_literals(void); +void DeserExprOperators_mul_3_int_literals(void); +void DeserExprOperators_div_3_int_literals(void); +void DeserExprOperators_int_to_bool(void); +void DeserExprOperators_add_mul_3_int_literals(void); +void DeserExprOperators_sub_mul_3_int_literals(void); +void DeserExprOperators_div_mul_3_int_literals(void); +void DeserExprOperators_add_div_3_int_literals(void); +void DeserExprOperators_sub_div_3_int_literals(void); +void DeserExprOperators_mul_div_3_int_literals(void); +void DeserExprOperators_mul_add_mul_add_int_literals(void); +void DeserExprOperators_mul_sub_mul_sub_int_literals(void); +void DeserExprOperators_mul_div_mul_div_int_literals(void); +void DeserExprOperators_div_add_div_add_int_literals(void); +void DeserExprOperators_div_sub_div_sub_int_literals(void); +void DeserExprOperators_div_sub_div_mul_int_literals(void); +void DeserExprOperators_div_mul_div_mul_int_literals(void); +void DeserExprOperators_add_2_flt_literals(void); +void DeserExprOperators_sub_2_flt_literals(void); +void DeserExprOperators_mul_2_flt_literals(void); +void DeserExprOperators_div_2_flt_literals(void); +void DeserExprOperators_add_2_int_neg_literals(void); +void DeserExprOperators_sub_2_int_neg_literals(void); +void DeserExprOperators_mul_2_int_neg_literals(void); +void DeserExprOperators_div_2_int_neg_literals(void); +void DeserExprOperators_mul_lparen_add_add_rparen_int_literals(void); +void DeserExprOperators_mul_lparen_add_add_add_rparen_int_literals(void); +void DeserExprOperators_mul_lparen_add_add_rparen_add_int_literals(void); +void DeserExprOperators_lparen_add_add_rparen_mul_int_literals(void); +void DeserExprOperators_lparen_add_add_add_rparen_mul_int_literals(void); +void DeserExprOperators_double_paren_add_add(void); +void DeserExprOperators_double_paren_literal(void); +void DeserExprOperators_lparen_add_add_rparen_mul_lparen_add_add_rparen(void); +void DeserExprOperators_float_result_add_2_int_literals(void); +void DeserExprOperators_struct_result_add_2_int_literals(void); +void DeserExprOperators_struct_result_add_2_2_fields_int_literals(void); +void DeserExprOperators_struct_result_add_3_int_literals(void); +void DeserExprOperators_add_to_var(void); +void DeserExprOperators_add_var_to(void); +void DeserExprOperators_bool_cond_and_bool(void); +void DeserExprOperators_bool_cond_or_bool(void); +void DeserExprOperators_int_cond_and_int(void); +void DeserExprOperators_int_cond_or_int(void); +void DeserExprOperators_bool_cond_and_int(void); +void DeserExprOperators_int_cond_and_bool(void); +void DeserExprOperators_bool_cond_or_int(void); +void DeserExprOperators_int_cond_or_bool(void); +void DeserExprOperators_cond_eq_bool(void); +void DeserExprOperators_cond_eq_int(void); +void DeserExprOperators_cond_neq_bool(void); +void DeserExprOperators_cond_neq_int(void); +void DeserExprOperators_cond_eq_bool_int(void); +void DeserExprOperators_cond_eq_int_flt(void); +void DeserExprOperators_cond_eq_cond_and(void); +void DeserExprOperators_cond_eq_cond_or(void); +void DeserExprOperators_cond_gt_bool(void); +void DeserExprOperators_cond_gt_int(void); +void DeserExprOperators_cond_gt_flt(void); +void DeserExprOperators_cond_gteq_bool(void); +void DeserExprOperators_cond_gteq_int(void); +void DeserExprOperators_cond_gteq_flt(void); +void DeserExprOperators_cond_lt_bool(void); +void DeserExprOperators_cond_lt_int(void); +void DeserExprOperators_cond_lt_flt(void); +void DeserExprOperators_cond_lteq_bool(void); +void DeserExprOperators_cond_lteq_int(void); +void DeserExprOperators_cond_lteq_flt(void); + bake_test_case PrimitiveTypes_testcases[] = { { "bool", @@ -1942,6 +2020,10 @@ bake_test_case DeserializeFromExpr_testcases[] = { "struct_i32", DeserializeFromExpr_struct_i32 }, + { + "struct_i32_neg", + DeserializeFromExpr_struct_i32_neg + }, { "struct_i32_i32", DeserializeFromExpr_struct_i32_i32 @@ -1966,6 +2048,10 @@ bake_test_case DeserializeFromExpr_testcases[] = { "struct_member_i32", DeserializeFromExpr_struct_member_i32 }, + { + "struct_member_i32_neg", + DeserializeFromExpr_struct_member_i32_neg + }, { "struct_member_i32_i32", DeserializeFromExpr_struct_member_i32_i32 @@ -3058,6 +3144,305 @@ bake_test_case Vars_testcases[] = { } }; +bake_test_case DeserExprOperators_testcases[] = { + { + "add_2_int_literals", + DeserExprOperators_add_2_int_literals + }, + { + "add_2_int_literals_twice", + DeserExprOperators_add_2_int_literals_twice + }, + { + "sub_2_int_literals", + DeserExprOperators_sub_2_int_literals + }, + { + "mul_2_int_literals", + DeserExprOperators_mul_2_int_literals + }, + { + "div_2_int_literals", + DeserExprOperators_div_2_int_literals + }, + { + "add_3_int_literals", + DeserExprOperators_add_3_int_literals + }, + { + "add_3_int_literals_twice", + DeserExprOperators_add_3_int_literals_twice + }, + { + "sub_3_int_literals", + DeserExprOperators_sub_3_int_literals + }, + { + "mul_3_int_literals", + DeserExprOperators_mul_3_int_literals + }, + { + "div_3_int_literals", + DeserExprOperators_div_3_int_literals + }, + { + "int_to_bool", + DeserExprOperators_int_to_bool + }, + { + "add_mul_3_int_literals", + DeserExprOperators_add_mul_3_int_literals + }, + { + "sub_mul_3_int_literals", + DeserExprOperators_sub_mul_3_int_literals + }, + { + "div_mul_3_int_literals", + DeserExprOperators_div_mul_3_int_literals + }, + { + "add_div_3_int_literals", + DeserExprOperators_add_div_3_int_literals + }, + { + "sub_div_3_int_literals", + DeserExprOperators_sub_div_3_int_literals + }, + { + "mul_div_3_int_literals", + DeserExprOperators_mul_div_3_int_literals + }, + { + "mul_add_mul_add_int_literals", + DeserExprOperators_mul_add_mul_add_int_literals + }, + { + "mul_sub_mul_sub_int_literals", + DeserExprOperators_mul_sub_mul_sub_int_literals + }, + { + "mul_div_mul_div_int_literals", + DeserExprOperators_mul_div_mul_div_int_literals + }, + { + "div_add_div_add_int_literals", + DeserExprOperators_div_add_div_add_int_literals + }, + { + "div_sub_div_sub_int_literals", + DeserExprOperators_div_sub_div_sub_int_literals + }, + { + "div_sub_div_mul_int_literals", + DeserExprOperators_div_sub_div_mul_int_literals + }, + { + "div_mul_div_mul_int_literals", + DeserExprOperators_div_mul_div_mul_int_literals + }, + { + "add_2_flt_literals", + DeserExprOperators_add_2_flt_literals + }, + { + "sub_2_flt_literals", + DeserExprOperators_sub_2_flt_literals + }, + { + "mul_2_flt_literals", + DeserExprOperators_mul_2_flt_literals + }, + { + "div_2_flt_literals", + DeserExprOperators_div_2_flt_literals + }, + { + "add_2_int_neg_literals", + DeserExprOperators_add_2_int_neg_literals + }, + { + "sub_2_int_neg_literals", + DeserExprOperators_sub_2_int_neg_literals + }, + { + "mul_2_int_neg_literals", + DeserExprOperators_mul_2_int_neg_literals + }, + { + "div_2_int_neg_literals", + DeserExprOperators_div_2_int_neg_literals + }, + { + "mul_lparen_add_add_rparen_int_literals", + DeserExprOperators_mul_lparen_add_add_rparen_int_literals + }, + { + "mul_lparen_add_add_add_rparen_int_literals", + DeserExprOperators_mul_lparen_add_add_add_rparen_int_literals + }, + { + "mul_lparen_add_add_rparen_add_int_literals", + DeserExprOperators_mul_lparen_add_add_rparen_add_int_literals + }, + { + "lparen_add_add_rparen_mul_int_literals", + DeserExprOperators_lparen_add_add_rparen_mul_int_literals + }, + { + "lparen_add_add_add_rparen_mul_int_literals", + DeserExprOperators_lparen_add_add_add_rparen_mul_int_literals + }, + { + "double_paren_add_add", + DeserExprOperators_double_paren_add_add + }, + { + "double_paren_literal", + DeserExprOperators_double_paren_literal + }, + { + "lparen_add_add_rparen_mul_lparen_add_add_rparen", + DeserExprOperators_lparen_add_add_rparen_mul_lparen_add_add_rparen + }, + { + "float_result_add_2_int_literals", + DeserExprOperators_float_result_add_2_int_literals + }, + { + "struct_result_add_2_int_literals", + DeserExprOperators_struct_result_add_2_int_literals + }, + { + "struct_result_add_2_2_fields_int_literals", + DeserExprOperators_struct_result_add_2_2_fields_int_literals + }, + { + "struct_result_add_3_int_literals", + DeserExprOperators_struct_result_add_3_int_literals + }, + { + "add_to_var", + DeserExprOperators_add_to_var + }, + { + "add_var_to", + DeserExprOperators_add_var_to + }, + { + "bool_cond_and_bool", + DeserExprOperators_bool_cond_and_bool + }, + { + "bool_cond_or_bool", + DeserExprOperators_bool_cond_or_bool + }, + { + "int_cond_and_int", + DeserExprOperators_int_cond_and_int + }, + { + "int_cond_or_int", + DeserExprOperators_int_cond_or_int + }, + { + "bool_cond_and_int", + DeserExprOperators_bool_cond_and_int + }, + { + "int_cond_and_bool", + DeserExprOperators_int_cond_and_bool + }, + { + "bool_cond_or_int", + DeserExprOperators_bool_cond_or_int + }, + { + "int_cond_or_bool", + DeserExprOperators_int_cond_or_bool + }, + { + "cond_eq_bool", + DeserExprOperators_cond_eq_bool + }, + { + "cond_eq_int", + DeserExprOperators_cond_eq_int + }, + { + "cond_neq_bool", + DeserExprOperators_cond_neq_bool + }, + { + "cond_neq_int", + DeserExprOperators_cond_neq_int + }, + { + "cond_eq_bool_int", + DeserExprOperators_cond_eq_bool_int + }, + { + "cond_eq_int_flt", + DeserExprOperators_cond_eq_int_flt + }, + { + "cond_eq_cond_and", + DeserExprOperators_cond_eq_cond_and + }, + { + "cond_eq_cond_or", + DeserExprOperators_cond_eq_cond_or + }, + { + "cond_gt_bool", + DeserExprOperators_cond_gt_bool + }, + { + "cond_gt_int", + DeserExprOperators_cond_gt_int + }, + { + "cond_gt_flt", + DeserExprOperators_cond_gt_flt + }, + { + "cond_gteq_bool", + DeserExprOperators_cond_gteq_bool + }, + { + "cond_gteq_int", + DeserExprOperators_cond_gteq_int + }, + { + "cond_gteq_flt", + DeserExprOperators_cond_gteq_flt + }, + { + "cond_lt_bool", + DeserExprOperators_cond_lt_bool + }, + { + "cond_lt_int", + DeserExprOperators_cond_lt_int + }, + { + "cond_lt_flt", + DeserExprOperators_cond_lt_flt + }, + { + "cond_lteq_bool", + DeserExprOperators_cond_lteq_bool + }, + { + "cond_lteq_int", + DeserExprOperators_cond_lteq_int + }, + { + "cond_lteq_flt", + DeserExprOperators_cond_lteq_flt + } +}; + static bake_test_suite suites[] = { { "PrimitiveTypes", @@ -3133,7 +3518,7 @@ static bake_test_suite suites[] = { "DeserializeFromExpr", NULL, NULL, - 53, + 55, DeserializeFromExpr_testcases }, { @@ -3177,9 +3562,16 @@ static bake_test_suite suites[] = { NULL, 14, Vars_testcases + }, + { + "DeserExprOperators", + NULL, + NULL, + 74, + DeserExprOperators_testcases } }; int main(int argc, char *argv[]) { - return bake_test_run("meta", argc, argv, suites, 17); + return bake_test_run("meta", argc, argv, suites, 18); }