X-Git-Url: http://git.droids-corp.org/?a=blobdiff_plain;f=lib%2Fecoli_node.h;h=9bf459dd3a82f77abf84177a3a2987a82f99ba1e;hb=1d655de6043b607f39888c1bb88f72d071f2d49a;hp=0933a20da6be49031ed3f65d0c1f1660cff775d4;hpb=c1eb3099a1a7f4c9b9f8c6238336ee5b69147fd6;p=protos%2Flibecoli.git diff --git a/lib/ecoli_node.h b/lib/ecoli_node.h index 0933a20..9bf459d 100644 --- a/lib/ecoli_node.h +++ b/lib/ecoli_node.h @@ -1,28 +1,42 @@ -/* - * Copyright (c) 2016, Olivier MATZ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright 2016, Olivier MATZ + */ + +/** + * Interface to manage the ecoli nodes. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: + * A node is a main structure of the ecoli library, used to define how + * to match and complete the input tokens. A node is a generic object + * that implements: + * - a parse(node, input) method: check if an input matches + * - a complete(node, input) method: return possible completions for + * a given input + * - some other methods to initialize, free, ... * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the University of California, Berkeley nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. + * One basic example is the string node (ec_node_str). A node + * ec_node_str("foo") will match any token list starting with "foo", + * for example: + * - ["foo"] + * - ["foo", "bar", ...] + * But will not match: + * - [] + * - ["bar", ...] * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * A node ec_node_str("foo") will complete with "foo" if the input + * contains one token, with the same beginning than "foo": + * - [""] + * - ["f"] + * - ["fo"] + * - ["foo"] + * But it will not complete: + * - [] + * - ["bar"] + * - ["f", ""] + * - ["", "f"] + * + * A node can have child nodes. For instance, a sequence node + * ec_node_seq(ec_node_str("foo"), ec_node_str("bar")) will match + * ["foo", "bar"]. */ #ifndef ECOLI_NODE_ @@ -32,48 +46,64 @@ #include #include +#define EC_NO_ID "no-id" + #define EC_NODE_ENDLIST ((void *)1) struct ec_node; -struct ec_parsed; +struct ec_parse; +struct ec_comp; struct ec_strvec; struct ec_keyval; +struct ec_config; +struct ec_config_schema; -/* return 0 on success, else -errno. */ -typedef int (*ec_node_build_t)(struct ec_node *node); - -typedef struct ec_parsed *(*ec_node_parse_t)(const struct ec_node *node, - const struct ec_strvec *strvec); -typedef struct ec_completed *(*ec_node_complete_t)(const struct ec_node *node, - const struct ec_strvec *strvec); -typedef const char * (*ec_node_desc_t)(const struct ec_node *); -typedef void (*ec_node_init_priv_t)(struct ec_node *); -typedef void (*ec_node_free_priv_t)(struct ec_node *); - -#define EC_NODE_TYPE_REGISTER(t) \ +#define EC_NODE_TYPE_REGISTER(t) \ static void ec_node_init_##t(void); \ static void __attribute__((constructor, used)) \ ec_node_init_##t(void) \ { \ if (ec_node_type_register(&t) < 0) \ - fprintf(stderr, "cannot register %s\n", t.name); \ + fprintf(stderr, \ + "cannot register node type %s\n", \ + t.name); \ } TAILQ_HEAD(ec_node_type_list, ec_node_type); +typedef int (*ec_node_set_config_t)(struct ec_node *node, + const struct ec_config *config); +typedef int (*ec_node_parse_t)(const struct ec_node *node, + struct ec_parse *state, + const struct ec_strvec *strvec); +typedef int (*ec_node_complete_t)(const struct ec_node *node, + struct ec_comp *comp_state, + const struct ec_strvec *strvec); +typedef const char * (*ec_node_desc_t)(const struct ec_node *); +typedef int (*ec_node_init_priv_t)(struct ec_node *); +typedef void (*ec_node_free_priv_t)(struct ec_node *); +typedef size_t (*ec_node_get_children_count_t)(const struct ec_node *); +typedef struct ec_node * (*ec_node_get_child_t)(const struct ec_node *, + size_t i); + /** * A structure describing a node type. */ struct ec_node_type { TAILQ_ENTRY(ec_node_type) next; /**< Next in list. */ - const char *name; /**< Node type name. */ - ec_node_build_t build; /* (re)build the node, called by generic parse */ + const char *name; /**< Node type name. */ + /** Generic configuration schema. */ + const struct ec_config_schema *schema; + size_t schema_len; /**< Number of elts in schema array. */ + ec_node_set_config_t set_config; /* validate/ack a config change */ ec_node_parse_t parse; ec_node_complete_t complete; ec_node_desc_t desc; size_t size; ec_node_init_priv_t init_priv; ec_node_free_priv_t free_priv; + ec_node_get_children_count_t get_children_count; + ec_node_get_child_t get_child; }; /** @@ -95,165 +125,71 @@ int ec_node_type_register(struct ec_node_type *type); * @return * The node type if found, or NULL on error. */ -struct ec_node_type *ec_node_type_lookup(const char *name); +const struct ec_node_type *ec_node_type_lookup(const char *name); /** * Dump registered log types */ void ec_node_type_dump(FILE *out); -TAILQ_HEAD(ec_node_list, ec_node); +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, +}; struct ec_node { const struct ec_node_type *type; + struct ec_config *config; /**< Generic configuration. */ char *id; char *desc; struct ec_keyval *attrs; - /* XXX ensure parent and child are properly set in all nodes */ - struct ec_node *parent; unsigned int refcnt; -#define EC_NODE_F_BUILT 0x0001 /** set if configuration is built */ - unsigned int flags; - - TAILQ_ENTRY(ec_node) next; - struct ec_node_list children; + 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 */ }; /* create a new node when the type is known, typically called from the node * code */ struct ec_node *__ec_node(const struct ec_node_type *type, const char *id); -/* create a_new node node */ +/* create a new node */ struct ec_node *ec_node(const char *typename, const char *id); +struct ec_node *ec_node_clone(struct ec_node *node); void ec_node_free(struct ec_node *node); +/* set configuration of a node + * after a call to this function, the config is + * owned by the node and must not be used by the caller + * on error, the config is freed. */ +int ec_node_set_config(struct ec_node *node, struct ec_config *config); + +/* get the current node configuration. Return NULL if no configuration. */ +const struct ec_config *ec_node_get_config(struct ec_node *node); + +size_t ec_node_get_children_count(const struct ec_node *node); +struct ec_node * +ec_node_get_child(const struct ec_node *node, size_t i); +int ec_node_add_child(struct ec_node *node, struct ec_node *child); +int ec_node_del_child(struct ec_node *node, struct ec_node *child); + /* XXX add more accessors */ +const struct ec_node_type *ec_node_type(const struct ec_node *node); struct ec_keyval *ec_node_attrs(const struct ec_node *node); -struct ec_node *ec_node_parent(const struct ec_node *node); const char *ec_node_id(const struct ec_node *node); const char *ec_node_desc(const struct ec_node *node); void ec_node_dump(FILE *out, const struct ec_node *node); struct ec_node *ec_node_find(struct ec_node *node, const char *id); -/* XXX split this file ? */ - -TAILQ_HEAD(ec_parsed_list, ec_parsed); - -/* - node == NULL + empty children list means "no match" -*/ -struct ec_parsed { - TAILQ_ENTRY(ec_parsed) next; - struct ec_parsed_list children; - const struct ec_node *node; - struct ec_strvec *strvec; -}; - -struct ec_parsed *ec_parsed(void); -void ec_parsed_free(struct ec_parsed *parsed); -struct ec_node *ec_node_clone(struct ec_node *node); -void ec_parsed_free_children(struct ec_parsed *parsed); - -const struct ec_strvec *ec_parsed_strvec( - const struct ec_parsed *parsed); - -void ec_parsed_set_match(struct ec_parsed *parsed, - const struct ec_node *node, struct ec_strvec *strvec); - -/* XXX we could use a cache to store possible completions or match: the - * cache would be per-node, and would be reset for each call to parse() - * or complete() ? */ -/* a NULL return value is an error, with errno set - ENOTSUP: no ->parse() operation -*/ -struct ec_parsed *ec_node_parse(struct ec_node *node, const char *str); - -/* mostly internal to nodes */ -/* XXX it should not reset cache - * ... not sure... it is used by tests */ -struct ec_parsed *ec_node_parse_strvec(struct ec_node *node, - const struct ec_strvec *strvec); - -void ec_parsed_add_child(struct ec_parsed *parsed, - struct ec_parsed *child); -void ec_parsed_del_child(struct ec_parsed *parsed, - struct ec_parsed *child); -void ec_parsed_dump(FILE *out, const struct ec_parsed *parsed); - -struct ec_parsed *ec_parsed_find_first(struct ec_parsed *parsed, - const char *id); - -const char *ec_parsed_to_string(const struct ec_parsed *parsed); -size_t ec_parsed_len(const struct ec_parsed *parsed); -size_t ec_parsed_matches(const struct ec_parsed *parsed); - -struct ec_completed_elt { - TAILQ_ENTRY(ec_completed_elt) next; - const struct ec_node *node; - char *add; -}; - -TAILQ_HEAD(ec_completed_elt_list, ec_completed_elt); - - -struct ec_completed { - struct ec_completed_elt_list elts; - unsigned count; - unsigned count_match; - char *smallest_start; -}; - -/* - * return a completed object filled with elts - * return NULL on error (nomem?) - */ -struct ec_completed *ec_node_complete(struct ec_node *node, - const char *str); -struct ec_completed *ec_node_complete_strvec(struct ec_node *node, - const struct ec_strvec *strvec); -struct ec_completed *ec_completed(void); -struct ec_completed_elt *ec_completed_elt(const struct ec_node *node, - const char *add); -void ec_completed_add_elt(struct ec_completed *completed, - struct ec_completed_elt *elt); -void ec_completed_elt_free(struct ec_completed_elt *elt); -void ec_completed_merge(struct ec_completed *completed1, - struct ec_completed *completed2); -void ec_completed_free(struct ec_completed *completed); -void ec_completed_dump(FILE *out, - const struct ec_completed *completed); -struct ec_completed *ec_node_default_complete(const struct ec_node *gen_node, - const struct ec_strvec *strvec); - -/* cannot return NULL */ -const char *ec_completed_smallest_start( - const struct ec_completed *completed); - -enum ec_completed_filter_flags { - EC_MATCH = 1, - EC_NO_MATCH = 2, -}; - -unsigned int ec_completed_count( - const struct ec_completed *completed, - enum ec_completed_filter_flags flags); - -struct ec_completed_iter { - enum ec_completed_filter_flags flags; - const struct ec_completed *completed; - const struct ec_completed_elt *cur; -}; - -struct ec_completed_iter * -ec_completed_iter(struct ec_completed *completed, - enum ec_completed_filter_flags flags); - -const struct ec_completed_elt *ec_completed_iter_next( - struct ec_completed_iter *iter); - -void ec_completed_iter_free(struct ec_completed_iter *iter); - +/* check the type of a node */ +int ec_node_check_type(const struct ec_node *node, + const struct ec_node_type *type); #endif