Libecoli 0.11.1
Extensible COmmand LIne library
Loading...
Searching...
No Matches
About Libecoli

Libecoli is an Extensible COmmand LIne library written in C that provides a modular, composable framework for building interactive command line interfaces with dynamic completion, contextual help, and parsing capabilities.

Project page: https://github.com/rjarry/libecoli

Key Features

  • Grammar-based parsing: Define command syntax using composable grammar nodes that form a directed graph.
  • Dynamic completion: Automatic TAB completion based on the grammar, with support for runtime-generated suggestions.
  • Contextual help: Display relevant help messages based on current input position.
  • Shell-like tokenization: Built-in lexers handle quoting, escaping, and variable expansion.
  • Interactive editing: Integration with libedit for line editing, history, and key bindings.
  • YAML configuration: Define grammars in YAML files instead of C code.
  • Expression evaluation: Parse and evaluate arithmetic or custom expressions with user-defined operators.

Core Concepts

Grammar Graph

A grammar is built by composing Grammar nodes into a directed graph. Each node type matches specific input patterns:

Parsing

The Parse nodes API validates input against a grammar and produces a parse tree. Each node in the tree references the grammar node that matched it and the tokens it consumed. Use ec_pnode_find() to locate specific nodes by their identifier and extract matched values.

Completion

The Complete API generates completion suggestions for partial input. Completions are grouped by the grammar node that produced them and can be filtered by type (full match, partial match, or unknown).

Interactive Mode

The Editline API provides a complete interactive command line with:

  • Line editing with libedit
  • TAB and ? key completion
  • Command history with file persistence
  • Callback-based command execution
  • Contextual help display on errors

Quick Example

#include <ecoli.h>
static bool done;
static int hello_cb(const struct ec_pnode *p) {
const char *name = ec_strvec_val(
printf("Hello, %s!\n", name);
return 0;
}
static int exit_cb(const struct ec_pnode *p) {
(void)p;
done = true;
return 0;
}
static bool check_exit_cb(void *priv) {
(void)priv;
return done;
}
int main(void) {
struct ec_node *cmd, *root;
struct ec_editline *edit;
// Build grammar: "hello <name>" or "exit"
root = ec_node("or", EC_NO_ID);
ec_node_str(EC_NO_ID, "hello"),
ec_node_any("NAME", NULL));
ec_interact_set_callback(cmd, hello_cb);
ec_interact_set_help(cmd, "Say hello to someone.");
ec_node_or_add(root, cmd);
cmd = ec_node_str(EC_NO_ID, "exit");
ec_interact_set_callback(cmd, exit_cb);
ec_interact_set_help(cmd, "Exit the program.");
ec_node_or_add(root, cmd);
// Add shell lexer for quote/escape handling
root = ec_node_sh_lex(EC_NO_ID, root);
// Create interactive session
edit = ec_editline("example", stdin, stdout, stderr, 0);
ec_editline_set_node(edit, root);
ec_editline_interact(edit, check_exit_cb, NULL);
ec_node_free(root);
return 0;
}
int ec_editline_set_prompt(struct ec_editline *editline, const char *prompt)
struct ec_editline * ec_editline(const char *prog, FILE *f_in, FILE *f_out, FILE *f_err, enum ec_editline_init_flags flags)
void ec_editline_free(struct ec_editline *editline)
int ec_editline_interact(struct ec_editline *editline, ec_editline_check_exit_cb_t check_exit_cb, void *opaque)
int ec_editline_set_node(struct ec_editline *editline, const struct ec_node *node)
int ec_init(void)
void ec_exit(void)
int ec_interact_set_help(struct ec_node *node, const char *help)
int ec_interact_set_callback(struct ec_node *node, ec_interact_command_cb_t cb)
struct ec_node * ec_node_any(const char *id, const char *attr)
int ec_node_or_add(struct ec_node *node, struct ec_node *child)
#define EC_NODE_SEQ(args...)
Definition node_seq.h:29
struct ec_node * ec_node_sh_lex(const char *id, struct ec_node *child)
struct ec_node * ec_node_str(const char *id, const char *str)
struct ec_node * ec_node(const char *typename, const char *id)
#define EC_NO_ID
Definition node.h:64
void ec_node_free(struct ec_node *node)
const struct ec_strvec * ec_pnode_get_strvec(const struct ec_pnode *pnode)
const struct ec_pnode * ec_pnode_find(const struct ec_pnode *root, const char *id)
struct ec_pnode * ec_pnode(const struct ec_node *node)
const char * ec_strvec_val(const struct ec_strvec *strvec, size_t idx)

See the Examples page for complete working examples demonstrating grammar construction, completion, readline integration, custom node types, and dynamic completion from runtime data.

Available Node Types

Terminal Nodes

Node Type Description
String node Matches a specific literal string
Integer node Matches signed integers with range/base constraints
Regex node Matches input against a POSIX extended regex
File node Matches and completes filesystem paths
Any node Matches any single token
Empty node Matches zero tokens (always succeeds)
None node Matches nothing (always fails)
Space node Matches whitespace

Composite Nodes

Node Type Description
Sequence node Matches children in strict order
Or node Matches any one of its children
Many node Matches a child repeatedly (with min/max)
Option node Makes a child optional
Subset node Matches any subset of children in any order

Advanced Nodes

Node Type Description
Command node Parses commands using a format string syntax
Expression node Parses expressions with operators and precedence
Dynamic node Builds grammar dynamically at parse time
Dynamic list node Matches names from a runtime-generated list
Condition node Conditionally matches based on an expression
Once node Prevents a child from matching more than once
Bypass node Pass-through node for building graph loops

Lexer Nodes

Node Type Description
Shell lexer node Shell-like tokenization with quotes and escapes
Regex lexer node Regex-based tokenization

API Reference

Real-World Usage

Libecoli powers the CLI of several production systems including the grout graph router and 6WIND's Virtual Service Router. These projects demonstrate patterns such as:

  • Modular command registration with constructor functions
  • Dynamic completion callbacks that query live system state
  • Hierarchical command contexts (e.g., ip route add ...)
  • Typed argument extraction helpers
  • JSON export of command trees for documentation generation