Files
PixelDefense/engine/libs/flecs/src/addons/rules/api.c

410 lines
12 KiB
C

/**
* @file addons/rules/api.c
* @brief User facing API for rules.
*/
#include "rules.h"
#include <ctype.h>
#ifdef FLECS_RULES
static ecs_mixins_t ecs_rule_t_mixins = {
.type_name = "ecs_rule_t",
.elems = {
[EcsMixinWorld] = offsetof(ecs_rule_t, filter.world),
[EcsMixinEntity] = offsetof(ecs_rule_t, filter.entity),
[EcsMixinIterable] = offsetof(ecs_rule_t, iterable),
[EcsMixinDtor] = offsetof(ecs_rule_t, dtor)
}
};
static
const char* flecs_rule_op_str(
uint16_t kind)
{
switch(kind) {
case EcsRuleAnd: return "and ";
case EcsRuleAndId: return "and_id ";
case EcsRuleAndAny: return "andany ";
case EcsRuleWith: return "with ";
case EcsRuleTrav: return "trav ";
case EcsRuleIdsRight: return "idsr ";
case EcsRuleIdsLeft: return "idsl ";
case EcsRuleEach: return "each ";
case EcsRuleStore: return "store ";
case EcsRuleReset: return "reset ";
case EcsRuleUnion: return "union ";
case EcsRuleEnd: return "end ";
case EcsRuleNot: return "not ";
case EcsRulePredEq: return "eq ";
case EcsRulePredNeq: return "neq ";
case EcsRulePredEqName: return "eq_nm ";
case EcsRulePredNeqName: return "neq_nm ";
case EcsRulePredEqMatch: return "eq_m ";
case EcsRulePredNeqMatch: return "neq_m ";
case EcsRuleLookup: return "lookup ";
case EcsRuleSetVars: return "setvars ";
case EcsRuleSetThis: return "setthis ";
case EcsRuleSetFixed: return "setfix ";
case EcsRuleSetIds: return "setids ";
case EcsRuleContain: return "contain ";
case EcsRulePairEq: return "pair_eq ";
case EcsRuleSetCond: return "setcond ";
case EcsRuleJmpCondFalse: return "jfalse ";
case EcsRuleJmpNotSet: return "jnotset ";
case EcsRuleYield: return "yield ";
case EcsRuleNothing: return "nothing ";
default: return "!invalid";
}
}
/* Implementation for iterable mixin */
static
void flecs_rule_iter_mixin_init(
const ecs_world_t *world,
const ecs_poly_t *poly,
ecs_iter_t *iter,
ecs_term_t *filter)
{
ecs_poly_assert(poly, ecs_rule_t);
if (filter) {
iter[1] = ecs_rule_iter(world, ECS_CONST_CAST(ecs_rule_t*, poly));
iter[0] = ecs_term_chain_iter(&iter[1], filter);
} else {
iter[0] = ecs_rule_iter(world, ECS_CONST_CAST(ecs_rule_t*, poly));
}
}
static
void flecs_rule_fini(
ecs_rule_t *rule)
{
if (rule->vars != &rule->vars_cache.var) {
ecs_os_free(rule->vars);
}
ecs_os_free(rule->ops);
ecs_os_free(rule->src_vars);
flecs_name_index_fini(&rule->tvar_index);
flecs_name_index_fini(&rule->evar_index);
ecs_filter_fini(&rule->filter);
ecs_poly_free(rule, ecs_rule_t);
}
void ecs_rule_fini(
ecs_rule_t *rule)
{
if (rule->filter.entity) {
/* If filter is associated with entity, use poly dtor path */
ecs_delete(rule->filter.world, rule->filter.entity);
} else {
flecs_rule_fini(rule);
}
}
ecs_rule_t* ecs_rule_init(
ecs_world_t *world,
const ecs_filter_desc_t *const_desc)
{
ecs_rule_t *result = ecs_poly_new(ecs_rule_t);
ecs_stage_t *stage = flecs_stage_from_world(&world);
/* Initialize the query */
ecs_filter_desc_t desc = *const_desc;
desc.storage = &result->filter; /* Use storage of rule */
result->filter = ECS_FILTER_INIT;
if (ecs_filter_init(world, &desc) == NULL) {
goto error;
}
result->iterable.init = flecs_rule_iter_mixin_init;
/* Compile filter to operations */
if (flecs_rule_compile(world, stage, result)) {
goto error;
}
ecs_entity_t entity = const_desc->entity;
result->dtor = (ecs_poly_dtor_t)flecs_rule_fini;
if (entity) {
EcsPoly *poly = ecs_poly_bind(world, entity, ecs_rule_t);
poly->poly = result;
ecs_poly_modified(world, entity, ecs_rule_t);
}
return result;
error:
ecs_rule_fini(result);
return NULL;
}
static
int32_t flecs_rule_op_ref_str(
const ecs_rule_t *rule,
ecs_rule_ref_t *ref,
ecs_flags16_t flags,
ecs_strbuf_t *buf)
{
int32_t color_chars = 0;
if (flags & EcsRuleIsVar) {
ecs_assert(ref->var < rule->var_count, ECS_INTERNAL_ERROR, NULL);
ecs_rule_var_t *var = &rule->vars[ref->var];
ecs_strbuf_appendlit(buf, "#[green]$#[reset]");
if (var->kind == EcsVarTable) {
ecs_strbuf_appendch(buf, '[');
}
ecs_strbuf_appendlit(buf, "#[green]");
if (var->name) {
ecs_strbuf_appendstr(buf, var->name);
} else {
if (var->id) {
#ifdef FLECS_DEBUG
if (var->label) {
ecs_strbuf_appendstr(buf, var->label);
ecs_strbuf_appendch(buf, '\'');
}
#endif
ecs_strbuf_append(buf, "%d", var->id);
} else {
ecs_strbuf_appendlit(buf, "this");
}
}
ecs_strbuf_appendlit(buf, "#[reset]");
if (var->kind == EcsVarTable) {
ecs_strbuf_appendch(buf, ']');
}
color_chars = ecs_os_strlen("#[green]#[reset]#[green]#[reset]");
} else if (flags & EcsRuleIsEntity) {
char *path = ecs_get_fullpath(rule->filter.world, ref->entity);
ecs_strbuf_appendlit(buf, "#[blue]");
ecs_strbuf_appendstr(buf, path);
ecs_strbuf_appendlit(buf, "#[reset]");
ecs_os_free(path);
color_chars = ecs_os_strlen("#[blue]#[reset]");
}
return color_chars;
}
char* ecs_rule_str_w_profile(
const ecs_rule_t *rule,
const ecs_iter_t *it)
{
ecs_poly_assert(rule, ecs_rule_t);
ecs_strbuf_t buf = ECS_STRBUF_INIT;
ecs_rule_op_t *ops = rule->ops;
int32_t i, count = rule->op_count, indent = 0;
for (i = 0; i < count; i ++) {
ecs_rule_op_t *op = &ops[i];
ecs_flags16_t flags = op->flags;
ecs_flags16_t src_flags = flecs_rule_ref_flags(flags, EcsRuleSrc);
ecs_flags16_t first_flags = flecs_rule_ref_flags(flags, EcsRuleFirst);
ecs_flags16_t second_flags = flecs_rule_ref_flags(flags, EcsRuleSecond);
if (it) {
#ifdef FLECS_DEBUG
const ecs_rule_iter_t *rit = &it->priv.iter.rule;
ecs_strbuf_append(&buf,
"#[green]%4d -> #[red]%4d <- #[grey] | ",
rit->profile[i].count[0],
rit->profile[i].count[1]);
#endif
}
ecs_strbuf_append(&buf,
"#[normal]%2d. [#[grey]%2d#[reset], #[green]%2d#[reset]] ",
i, op->prev, op->next);
int32_t hidden_chars, start = ecs_strbuf_written(&buf);
if (op->kind == EcsRuleEnd) {
indent --;
}
ecs_strbuf_append(&buf, "%*s", indent, "");
ecs_strbuf_appendstr(&buf, flecs_rule_op_str(op->kind));
ecs_strbuf_appendstr(&buf, " ");
int32_t written = ecs_strbuf_written(&buf);
for (int32_t j = 0; j < (10 - (written - start)); j ++) {
ecs_strbuf_appendch(&buf, ' ');
}
if (op->kind == EcsRuleJmpCondFalse || op->kind == EcsRuleSetCond ||
op->kind == EcsRuleJmpNotSet)
{
ecs_strbuf_appendint(&buf, op->other);
ecs_strbuf_appendch(&buf, ' ');
}
hidden_chars = flecs_rule_op_ref_str(rule, &op->src, src_flags, &buf);
if (op->kind == EcsRuleUnion) {
indent ++;
}
if (!first_flags && !second_flags) {
ecs_strbuf_appendstr(&buf, "\n");
continue;
}
written = ecs_strbuf_written(&buf) - hidden_chars;
for (int32_t j = 0; j < (30 - (written - start)); j ++) {
ecs_strbuf_appendch(&buf, ' ');
}
ecs_strbuf_appendstr(&buf, "(");
flecs_rule_op_ref_str(rule, &op->first, first_flags, &buf);
if (second_flags) {
ecs_strbuf_appendstr(&buf, ", ");
flecs_rule_op_ref_str(rule, &op->second, second_flags, &buf);
} else {
switch (op->kind) {
case EcsRulePredEqName:
case EcsRulePredNeqName:
case EcsRulePredEqMatch:
case EcsRulePredNeqMatch: {
int8_t term_index = op->term_index;
ecs_strbuf_appendstr(&buf, ", #[yellow]\"");
ecs_strbuf_appendstr(&buf, rule->filter.terms[term_index].second.name);
ecs_strbuf_appendstr(&buf, "\"#[reset]");
break;
}
case EcsRuleLookup: {
ecs_var_id_t src_id = op->src.var;
ecs_strbuf_appendstr(&buf, ", #[yellow]\"");
ecs_strbuf_appendstr(&buf, rule->vars[src_id].lookup);
ecs_strbuf_appendstr(&buf, "\"#[reset]");
break;
}
default:
break;
}
}
ecs_strbuf_appendch(&buf, ')');
ecs_strbuf_appendch(&buf, '\n');
}
#ifdef FLECS_LOG
char *str = ecs_strbuf_get(&buf);
flecs_colorize_buf(str, true, &buf);
ecs_os_free(str);
#endif
return ecs_strbuf_get(&buf);
}
char* ecs_rule_str(
const ecs_rule_t *rule)
{
return ecs_rule_str_w_profile(rule, NULL);
}
const ecs_filter_t* ecs_rule_get_filter(
const ecs_rule_t *rule)
{
return &rule->filter;
}
const char* ecs_rule_parse_vars(
ecs_rule_t *rule,
ecs_iter_t *it,
const char *expr)
{
ecs_poly_assert(rule, ecs_rule_t);
ecs_check(it != NULL, ECS_INVALID_PARAMETER, NULL);
ecs_check(expr != NULL, ECS_INVALID_PARAMETER, NULL)
char token[ECS_MAX_TOKEN_SIZE];
const char *ptr = expr;
bool paren = false;
const char *name = NULL;
if (rule->filter.entity) {
name = ecs_get_name(rule->filter.world, rule->filter.entity);
}
ptr = ecs_parse_ws_eol(ptr);
if (!ptr[0]) {
return ptr;
}
if (ptr[0] == '(') {
paren = true;
ptr = ecs_parse_ws_eol(ptr + 1);
if (ptr[0] == ')') {
return ptr + 1;
}
}
do {
ptr = ecs_parse_ws_eol(ptr);
ptr = ecs_parse_identifier(name, expr, ptr, token);
if (!ptr) {
return NULL;
}
int var = ecs_rule_find_var(rule, token);
if (var == -1) {
ecs_parser_error(name, expr, (ptr - expr),
"unknown variable '%s'", token);
return NULL;
}
ptr = ecs_parse_ws_eol(ptr);
if (ptr[0] != ':') {
ecs_parser_error(name, expr, (ptr - expr),
"missing ':'");
return NULL;
}
ptr = ecs_parse_ws_eol(ptr + 1);
ptr = ecs_parse_identifier(name, expr, ptr, token);
if (!ptr) {
return NULL;
}
ecs_entity_t val = ecs_lookup_fullpath(rule->filter.world, token);
if (!val) {
ecs_parser_error(name, expr, (ptr - expr),
"unresolved entity '%s'", token);
return NULL;
}
ecs_iter_set_var(it, var, val);
ptr = ecs_parse_ws_eol(ptr);
if (ptr[0] == ')') {
if (!paren) {
ecs_parser_error(name, expr, (ptr - expr),
"unexpected closing parenthesis");
return NULL;
}
ptr ++;
break;
} else if (ptr[0] == ',') {
ptr ++;
} else if (!ptr[0]) {
if (paren) {
ecs_parser_error(name, expr, (ptr - expr),
"missing closing parenthesis");
return NULL;
}
break;
} else {
ecs_parser_error(name, expr, (ptr - expr),
"expected , or end of string");
return NULL;
}
} while (true);
return ptr;
error:
return NULL;
}
#endif