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_dict();
if (node->attrs == NULL)
goto fail;
fail:
if (node != NULL) {
ec_dict_free(node->attrs);
- ec_free(node->desc);
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_dict_free(node->attrs);
}
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");