#include <ecoli_malloc.h>
#include <ecoli_string.h>
#include <ecoli_strvec.h>
-#include <ecoli_keyval.h>
+#include <ecoli_dict.h>
#include <ecoli_log.h>
#include <ecoli_config.h>
#include <ecoli_test.h>
EC_LOG_TYPE_REGISTER(node);
+/* These states are used to mark the grammar graph when freeing, to
+ * detect loop. */
+enum ec_node_free_state {
+ EC_NODE_FREE_STATE_NONE,
+ EC_NODE_FREE_STATE_TRAVERSED,
+ EC_NODE_FREE_STATE_FREEABLE,
+ EC_NODE_FREE_STATE_NOT_FREEABLE,
+ EC_NODE_FREE_STATE_FREEING,
+};
+
+/**
+ * The grammar node structure.
+ */
+struct ec_node {
+ const struct ec_node_type *type; /**< The node type. */
+ struct ec_config *config; /**< Node configuration. */
+ char *id; /**< Node identifier (EC_NO_ID if none). */
+ struct ec_dict *attrs; /**< Attributes of the node. */
+ unsigned int refcnt; /**< Reference counter. */
+ struct {
+ enum ec_node_free_state state; /**< State of loop detection. */
+ unsigned int refcnt; /**< Number of reachable references
+ * starting from node beeing freed. */
+ } free; /**< Freeing state: used for loop detection */
+};
+
static struct ec_node_type_list node_type_list =
TAILQ_HEAD_INITIALIZER(node_type_list);
return NULL;
}
-int ec_node_type_register(struct ec_node_type *type)
+int ec_node_type_register(struct ec_node_type *type, bool override)
{
- EC_CHECK_ARG(type->size >= sizeof(struct ec_node), -1, EINVAL);
-
- if (ec_node_type_lookup(type->name) != NULL) {
+ if (!override && ec_node_type_lookup(type->name) != NULL) {
errno = EEXIST;
return -1;
}
- TAILQ_INSERT_TAIL(&node_type_list, type, next);
+ TAILQ_INSERT_HEAD(&node_type_list, type, next);
return 0;
}
goto fail;
}
- node = ec_calloc(1, type->size);
+ node = ec_calloc(1, sizeof(*node) + type->size);
if (node == NULL)
goto fail;
if (node->id == NULL)
goto fail;
- if (ec_asprintf(&node->desc, "<%s>", type->name) < 0)
- goto fail;
-
- node->attrs = ec_keyval();
+ node->attrs = ec_dict();
if (node->attrs == NULL)
goto fail;
fail:
if (node != NULL) {
- ec_keyval_free(node->attrs);
- ec_free(node->desc);
+ ec_dict_free(node->attrs);
ec_free(node->id);
}
ec_free(node);
if (node->type->free_priv != NULL)
node->type->free_priv(node);
ec_free(node->id);
- ec_free(node->desc);
- ec_keyval_free(node->attrs);
+ ec_dict_free(node->attrs);
}
node->refcnt--;
return node->type;
}
-struct ec_keyval *ec_node_attrs(const struct ec_node *node)
+struct ec_dict *ec_node_attrs(const struct ec_node *node)
{
return node->attrs;
}
}
static void __ec_node_dump(FILE *out,
- const struct ec_node *node, size_t indent, struct ec_keyval *dict)
+ const struct ec_node *node, size_t indent, struct ec_dict *dict)
{
const char *id, *typename;
struct ec_node *child;
typename = node->type->name;
snprintf(buf, sizeof(buf), "%p", node);
- if (ec_keyval_has_key(dict, buf)) {
+ if (ec_dict_has_key(dict, buf)) {
fprintf(out, "%*s" "type=%s id=%s %p... (loop)\n",
(int)indent * 4, "", typename, id, node);
return;
}
- ec_keyval_set(dict, buf, NULL, NULL);
+ ec_dict_set(dict, buf, NULL, NULL);
fprintf(out, "%*s" "type=%s id=%s %p refs=%u free_state=%d free_refs=%d\n",
(int)indent * 4, "", typename, id, node, node->refcnt,
node->free.state, node->free.refcnt);
/* XXX this is too much debug-oriented, we should have a parameter or 2 funcs */
void ec_node_dump(FILE *out, const struct ec_node *node)
{
- struct ec_keyval *dict = NULL;
+ struct ec_dict *dict = NULL;
fprintf(out, "------------------- node dump:\n");
return;
}
- dict = ec_keyval();
+ dict = ec_dict();
if (dict == NULL)
goto fail;
__ec_node_dump(out, node, 0, dict);
- ec_keyval_free(dict);
+ ec_dict_free(dict);
return;
fail:
- ec_keyval_free(dict);
+ ec_dict_free(dict);
EC_LOG(EC_LOG_ERR, "failed to dump node\n");
}
-const char *ec_node_desc(const struct ec_node *node)
+char *ec_node_desc(const struct ec_node *node)
{
+ char *desc = NULL;
+
if (node->type->desc != NULL)
return node->type->desc(node);
- return node->desc;
+ if (ec_asprintf(&desc, "<%s>", node->type->name) < 0)
+ return NULL;
+
+ return desc;
}
int ec_node_check_type(const struct ec_node *node,
return 0;
}
+const char *ec_node_get_type_name(const struct ec_node *node)
+{
+ return node->type->name;
+}
+
+void *ec_node_priv(const struct ec_node *node)
+{
+ if (node == NULL)
+ return NULL;
+ return (void *)(node + 1);
+}
+
/* LCOV_EXCL_START */
static int ec_node_testcase(void)
{
unsigned int refs;
FILE *f = NULL;
char *buf = NULL;
+ char *desc = NULL;
size_t buflen = 0;
int testres = 0;
int ret;
free(buf);
buf = NULL;
+ desc = ec_node_desc(node);
testres |= EC_TEST_CHECK(
!strcmp(ec_node_type(node)->name, "seq") &&
!strcmp(ec_node_id(node), EC_NO_ID) &&
- !strcmp(ec_node_desc(node), "<seq>"),
+ !strcmp(desc, "<seq>"),
"bad child 0");
+ ec_free(desc);
+ desc = NULL;
testres |= EC_TEST_CHECK(
ec_node_get_children_count(node) == 2,
"child 2 should be NULL");
child = ec_node_find(node, "id_x");
+ desc = ec_node_desc(child);
testres |= EC_TEST_CHECK(child != NULL &&
!strcmp(ec_node_type(child)->name, "str") &&
!strcmp(ec_node_id(child), "id_x") &&
- !strcmp(ec_node_desc(child), "x"),
+ !strcmp(desc, "x"),
"bad child id_x");
+ ec_free(desc);
+ desc = NULL;
child = ec_node_find(node, "id_dezdex");
testres |= EC_TEST_CHECK(child == NULL,
"child with wrong id should be NULL");
- ret = ec_keyval_set(ec_node_attrs(node), "key", "val", NULL);
+ ret = ec_dict_set(ec_node_attrs(node), "key", "val", NULL);
testres |= EC_TEST_CHECK(ret == 0,
"cannot set node attribute\n");