save
authorOlivier Matz <zer0@droids-corp.org>
Thu, 21 Dec 2017 17:16:58 +0000 (18:16 +0100)
committerOlivier Matz <zer0@droids-corp.org>
Thu, 21 Dec 2017 17:16:58 +0000 (18:16 +0100)
lib/ecoli_assert.h
lib/ecoli_completed.c
lib/ecoli_completed.h
lib/ecoli_init.h
lib/ecoli_node.h
lib/ecoli_node_any.h
lib/ecoli_parsed.c
lib/ecoli_parsed.h
lib/ecoli_test.c
lib/main-readline.c
lib/todo.txt

index 61989e9..f0eb9be 100644 (file)
@@ -29,7 +29,7 @@
  * Assert API
  *
  * Helpers to check at runtime if a condition is true, and abort
- * otherwise.
+ * (exit) otherwise.
  */
 
 #ifndef ECOLI_ASSERT_
index 553eccc..4836803 100644 (file)
 #include <ecoli_parsed.h>
 #include <ecoli_completed.h>
 
+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)
index 22016e5..e66cd71 100644 (file)
  * 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);
 
 
index b70e4dc..11cbad6 100644 (file)
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+/**
+ * Register initialization routines.
+ */
+
 #ifndef ECOLI_INIT_
 #define ECOLI_INIT_
 
index b5a424b..11e9936 100644 (file)
  * 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_
 
index e52cbe9..394a2e0 100644 (file)
@@ -32,4 +32,6 @@
 #ifndef ECOLI_NODE_ANY_
 #define ECOLI_NODE_ANY_
 
+/* no specific API for this node */
+
 #endif
index e951671..e8f3cb9 100644 (file)
@@ -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)
index 459f93d..0755336 100644 (file)
@@ -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,
index 9cf216d..80e6a9f 100644 (file)
@@ -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;
                }
 
index 87653f7..5bbe18e 100644 (file)
@@ -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 = "";
index 8d56dc5..0001de1 100644 (file)
@@ -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|<int>"
@@ -150,3 +150,212 @@ help("command f") ->
   titi    (help of titi)
   <int>   (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 <stdio.h>
+#include <stdbool.h>
+
+
+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;
+}