From ff1f48e335ca77cc97e49ea86b292233731ecb78 Mon Sep 17 00:00:00 2001 From: Olivier Matz Date: Thu, 21 Dec 2017 18:16:58 +0100 Subject: [PATCH] save --- lib/ecoli_assert.h | 2 +- lib/ecoli_completed.c | 31 ++++++ lib/ecoli_completed.h | 128 ++++++++++++++++++++++--- lib/ecoli_init.h | 4 + lib/ecoli_node.h | 37 ++++++++ lib/ecoli_node_any.h | 2 + lib/ecoli_parsed.c | 2 +- lib/ecoli_parsed.h | 2 +- lib/ecoli_test.c | 3 +- lib/main-readline.c | 22 +++-- lib/todo.txt | 215 +++++++++++++++++++++++++++++++++++++++++- 11 files changed, 419 insertions(+), 29 deletions(-) diff --git a/lib/ecoli_assert.h b/lib/ecoli_assert.h index 61989e9..f0eb9be 100644 --- a/lib/ecoli_assert.h +++ b/lib/ecoli_assert.h @@ -29,7 +29,7 @@ * Assert API * * Helpers to check at runtime if a condition is true, and abort - * otherwise. + * (exit) otherwise. */ #ifndef ECOLI_ASSERT_ diff --git a/lib/ecoli_completed.c b/lib/ecoli_completed.c index 553eccc..4836803 100644 --- a/lib/ecoli_completed.c +++ b/lib/ecoli_completed.c @@ -39,6 +39,19 @@ #include #include +struct ec_completed_item { + TAILQ_ENTRY(ec_completed_item) next; + enum ec_completed_type type; + const struct ec_node *node; + char *str; + char *display; + struct ec_keyval *attrs; + + /* reverse order: [0] = last, [len-1] = root */ + const struct ec_node **path; + size_t pathlen; +}; + struct ec_completed *ec_completed(void) { struct ec_completed *completed = NULL; @@ -340,6 +353,24 @@ ec_completed_item_add(struct ec_completed *completed, return 0; } +const char * +ec_completed_item_get_str(const struct ec_completed_item *item) +{ + return item->str; +} + +const char * +ec_completed_item_get_display(const struct ec_completed_item *item) +{ + return item->display; +} + +enum ec_completed_type +ec_completed_item_get_type(const struct ec_completed_item *item) +{ + return item->type; +} + void ec_completed_item_free(struct ec_completed_item *item) { if (item == NULL) diff --git a/lib/ecoli_completed.h b/lib/ecoli_completed.h index 22016e5..e66cd71 100644 --- a/lib/ecoli_completed.h +++ b/lib/ecoli_completed.h @@ -25,6 +25,15 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +/** + * API for generating completions item on a node. + * + * This file provide helpers to list and manipulate the possible + * completions for a given input. + * + * XXX completed vs item + */ + #ifndef ECOLI_COMPLETED_ #define ECOLI_COMPLETED_ @@ -40,18 +49,7 @@ enum ec_completed_type { EC_PARTIAL_MATCH, }; -struct ec_completed_item { - TAILQ_ENTRY(ec_completed_item) next; - enum ec_completed_type type; - const struct ec_node *node; - char *str; - char *display; - struct ec_keyval *attrs; - - /* reverse order: [0] = last, [len-1] = root */ - const struct ec_node **path; - size_t pathlen; -}; +struct ec_completed_item; TAILQ_HEAD(ec_completed_item_list, ec_completed_item); @@ -85,32 +83,117 @@ int ec_node_complete_child(struct ec_node *node, struct ec_parsed *parsed_state, const struct ec_strvec *strvec); +/** + * Create a completion object (list of completion items). + * + * + */ struct ec_completed *ec_completed(void); +/** + * Free a completion object and all its items. + * + * + */ +void ec_completed_free(struct ec_completed *completed); + +/** + * + * + * + */ +void ec_completed_dump(FILE *out, + const struct ec_completed *completed); + + +/** + * Create a completion item. + * + * + */ struct ec_completed_item * ec_completed_item(struct ec_parsed *state, const struct ec_node *node); + +/** + * Set type and value of a completion item. + * + * + */ int ec_completed_item_set(struct ec_completed_item *item, enum ec_completed_type type, const char *str); + +/** + * Add a completion item to a completion list. + * + * + */ int ec_completed_item_add(struct ec_completed *completed, struct ec_completed_item *item); + +/** + * Get the string value of a completion item. + * + * + */ +const char * +ec_completed_item_get_str(const struct ec_completed_item *item); + +/** + * Get the display string value of a completion item. + * + * + */ +const char * +ec_completed_item_get_display(const struct ec_completed_item *item); + +/** + * Get the type of a completion item. + * + * + */ +enum ec_completed_type +ec_completed_item_get_type(const struct ec_completed_item *item); + +/** + * + * + * + */ void ec_completed_item_free(struct ec_completed_item *item); +/** + * Set the display value of an item. + * + * + */ int ec_completed_item_set_display(struct ec_completed_item *item, const char *display); -void ec_completed_free(struct ec_completed *completed); -void ec_completed_dump(FILE *out, - const struct ec_completed *completed); +/** + * + * + * + */ int ec_node_default_complete(const struct ec_node *gen_node, struct ec_completed *completed, struct ec_parsed *state, const struct ec_strvec *strvec); +/** + * + * + * + */ unsigned int ec_completed_count( const struct ec_completed *completed, enum ec_completed_type flags); +/** + * + * + * + */ struct ec_completed_iter { enum ec_completed_type type; const struct ec_completed *completed; @@ -118,13 +201,28 @@ struct ec_completed_iter { const struct ec_completed_item *cur_match; }; +/** + * + * + * + */ struct ec_completed_iter * ec_completed_iter(struct ec_completed *completed, enum ec_completed_type type); +/** + * + * + * + */ const struct ec_completed_item *ec_completed_iter_next( struct ec_completed_iter *iter); +/** + * + * + * + */ void ec_completed_iter_free(struct ec_completed_iter *iter); diff --git a/lib/ecoli_init.h b/lib/ecoli_init.h index b70e4dc..11cbad6 100644 --- a/lib/ecoli_init.h +++ b/lib/ecoli_init.h @@ -25,6 +25,10 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +/** + * Register initialization routines. + */ + #ifndef ECOLI_INIT_ #define ECOLI_INIT_ diff --git a/lib/ecoli_node.h b/lib/ecoli_node.h index b5a424b..11e9936 100644 --- a/lib/ecoli_node.h +++ b/lib/ecoli_node.h @@ -25,6 +25,43 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +/** + * Interface to manage the ecoli nodes. + * + * 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, ... + * + * 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", ...] + * + * 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_ #define ECOLI_NODE_ diff --git a/lib/ecoli_node_any.h b/lib/ecoli_node_any.h index e52cbe9..394a2e0 100644 --- a/lib/ecoli_node_any.h +++ b/lib/ecoli_node_any.h @@ -32,4 +32,6 @@ #ifndef ECOLI_NODE_ANY_ #define ECOLI_NODE_ANY_ +/* no specific API for this node */ + #endif diff --git a/lib/ecoli_parsed.c b/lib/ecoli_parsed.c index e951671..e8f3cb9 100644 --- a/lib/ecoli_parsed.c +++ b/lib/ecoli_parsed.c @@ -310,7 +310,7 @@ const struct ec_strvec *ec_parsed_strvec(const struct ec_parsed *parsed) return parsed->strvec; } -/* number of parsed strings in the vector */ +/* number of strings in the parsed vector */ size_t ec_parsed_len(const struct ec_parsed *parsed) { if (parsed == NULL || parsed->strvec == NULL) diff --git a/lib/ecoli_parsed.h b/lib/ecoli_parsed.h index 459f93d..0755336 100644 --- a/lib/ecoli_parsed.h +++ b/lib/ecoli_parsed.h @@ -71,7 +71,7 @@ struct ec_parsed *ec_node_parse_strvec(struct ec_node *node, #define EC_PARSED_NOMATCH INT_MIN /* internal: used by nodes * - * state is the current parse tree, which is built bit by bit while + * state is the current parse tree, which is built piece by piece while * parsing the node tree: ec_node_parse_child() creates a new child in * this state parse tree, and calls the parse() method for the child * node, with state pointing to this new child. If it does not match, diff --git a/lib/ecoli_test.c b/lib/ecoli_test.c index 9cf216d..80e6a9f 100644 --- a/lib/ecoli_test.c +++ b/lib/ecoli_test.c @@ -149,7 +149,8 @@ int ec_test_check_complete(struct ec_node *tk, ...) /* only check matching completions */ iter = ec_completed_iter(c, EC_MATCH); while ((item = ec_completed_iter_next(iter)) != NULL) { - if (item->str != NULL && strcmp(item->str, s) == 0) + const char *str = ec_completed_item_get_str(item); + if (str != NULL && strcmp(str, s) == 0) break; } diff --git a/lib/main-readline.c b/lib/main-readline.c index 87653f7..5bbe18e 100644 --- a/lib/main-readline.c +++ b/lib/main-readline.c @@ -58,6 +58,8 @@ static char *my_completion_entry(const char *s, int state) static struct ec_completed *c; static struct ec_completed_iter *iter; const struct ec_completed_item *item; + enum ec_completed_type item_type; + const char *item_str, *item_display; (void)s; @@ -90,23 +92,26 @@ static char *my_completion_entry(const char *s, int state) if (item == NULL) return NULL; + item_str = ec_completed_item_get_str(item); if (c->count_match == 1) { /* don't add the trailing space for partial completions */ if (state == 0) { - if (item->type == EC_MATCH) + item_type = ec_completed_item_get_type(item); + if (item_type == EC_MATCH) rl_completion_suppress_append = 0; else rl_completion_suppress_append = 1; } - return strdup(item->str); + return strdup(item_str); } else if (rl_completion_type == '?') { /* on second try only show the display part */ - return strdup(item->display); + item_display = ec_completed_item_get_display(item); + return strdup(item_display); } - return strdup(item->str); + return strdup(item_str); } static char **my_attempted_completion(const char *text, int start, int end) @@ -123,13 +128,15 @@ static char **my_attempted_completion(const char *text, int start, int end) /* this function builds the help string */ static char *get_node_help(const struct ec_completed_node *compnode) { - const struct ec_completed_item *item; - const struct ec_node *node; +// const struct ec_completed_item *item; +// const struct ec_node *node; char *help = NULL; const char *node_help = NULL; const char *node_desc = NULL; - size_t i; +// size_t i; + (void)compnode; +#if 0 //XXX /* Since we display only one help per node, only look at the first item * to get the path. The objective is to retrieve the most precise * help for this node. */ @@ -141,6 +148,7 @@ static char *get_node_help(const struct ec_completed_node *compnode) if (node_desc == NULL) node_desc = ec_node_desc(node); } +#endif if (node_help == NULL) node_help = ""; diff --git a/lib/todo.txt b/lib/todo.txt index 8d56dc5..0001de1 100644 --- a/lib/todo.txt +++ b/lib/todo.txt @@ -44,7 +44,7 @@ X rename: - anything better than weakref? - add get_max_parse_len() for all relevant nodes - add ec_node_defaults.[ch] providing usual implementations of node methods -- use vec for strvec +X use vec for strvec dependencies ============ @@ -56,7 +56,7 @@ X new node "once" logs ==== -- register log types +X register log types yaml ==== @@ -139,7 +139,7 @@ netconf example readline: -[tab] list possible completions (matches only) +[tab] list possible completions (matches/partial only) [?] list what is expected, example: "command [foo] toto|titi|" @@ -150,3 +150,212 @@ help("command f") -> titi (help of titi) (help of int) + +---------------- + +struct names +============ + +ideas: + +- ec_node: a node that can be parsed/completed +- ec_parse: a tree describing the result of parse(node, input) +- ec_comp: a list describing the result of complete(node, input) + +ec_comp_item + + +--------------- + +node tree +========= + +Example: + +1 seq +2 option +3 str(foo) +4 or +5 int(1,10) +6 str(bar) +7 str(foo) + +parse() returns a tree +======= + +- each node of the tree refers to a ec_node +- each node points to the strvec that matches +- parse returns the first matching solution +- usually try to match as many str in the vecs (seq node) + +[foo] -> +1 seq +2 option +4 or +7 str(foo) + +The parse cb of the node is: + +parse_cb(node, current_parse_state, strvec, *nmatch) + +return values: +- 0: success, child->strvec is set by node (NULL = no_match) +- -1: error (errno is set) +maybe complex to use: +- the node must set it (ex: "return ec_parsed_node_match()") +- the caller must use accessor to check if it matches or not + +alternative idea for return values: +- >= 0: match, ret == nb_tk +- -1: error (errno is set) +- -2 or MAX_INT: success, but no match +This is strange to have a specific value for no match + +alternative idea for return values: +- ec_parse_result_match(n_tokens >= 0) +- ec_parse_result_nomatch() +- ec_parse_result_error(errno) + +A node always try to consume the maximum number of tokens. +Example: +1 seq +2 option +3 str(foo) +4 str(foo) +5 str(bar) + +[foo, foo, bar] matches +[foo, bar] does *not* match + +complete() returns a list of possible completions +========== + +problems: +- partial completion: in a path dir/file, completion stops once + after the directory +- displayed value is not the completion token: when completing a + file in several subdirectories, the full path is not displayed +- any parent node can modify the completions, ex: add missing quotes + in ec_node_sh_lex(), filter completions in case of a ec_node_filter() +- a command line may want to display the help from the most specific + token, or not. +- some specific nodes can complete several tokens + +struct item { + const char *str; + type: full, partial, unknown +} + +full: the completion item matches token +partial: beginning of a completion, does not match the token + (good example is a directory in a path) +unknown: could complete, but the node does not know how + +struct completed_elt { + ec_parsed *parse_tree; // current tree state + ec_node *last; // last node of the tree + list of items; // list of items for this parse tree +} + +struct completed { + list(elt) +} + +The callback is: + +complete_cb(node, current_complete_state, current_parse_state, strvec) +return: +- 0 = success, the current complete state is updated +- -1 = error (set errno?) + + +a node can filter the completions + + +[] -> + foo 3 str(foo) + seq + option + str(foo) <- + + ? 5 int(1,10) + seq + option + or + int <- + + bar 6 str(bar) + foo 7 str(bar) +... + + +[foo, ] -> + + ? 5 int(1,10) + seq + option + str(foo) + or + int <- + + bar 6 str(bar) + foo 7 str(bar) + + + +----- + +changes: +- a completion item should contain a strvec for the value + (the display string remains a string) +- use a INT_MIN or a specific +- there is maybe no good reason to split in: + - ec_completed_item() + - ec_completed_item_set() + - ec_completed_item_set_display() + - ec_completed_item_add() + + +----- + +#include +#include + + +struct res { + int a; +}; + +static inline bool is_success(struct res r) +{ + if (r.a == 0) + return true; + return false; +} + + +static inline struct res res(int a) +{ + struct res r; + r.a = a; + return r; +} + +int main(void) +{ + struct res r; + + r = res(0); + + printf("%d\n", r.a); + if (is_success(r)) + printf("success: %d\n", r.a); + + r = res(1); + + printf("%d\n", r.a); + if (is_success(r)) + printf("success: %d\n", r.a); + + return 0; +} -- 2.20.1