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:

- Terminal nodes match actual input tokens: ec_node_str() matches a literal string, ec_node_int() matches integers, ec_node_re() matches regular expressions, the file node matches filesystem paths.
- Composite nodes combine other nodes: ec_node_seq() matches children in sequence, ec_node_or() matches any one child, ec_node_many() matches a child repeatedly, ec_node_option() makes a child optional.
- Lexer nodes tokenize raw input before passing to children: ec_node_sh_lex() provides shell-like tokenization with quote handling.

### 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(
ec_pnode_get_strvec(ec_pnode_find(p, "NAME")), 0);
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;
ec_init();
// Build grammar: "hello <name>" or "exit"
root = ec_node("or", EC_NO_ID);
cmd = EC_NODE_SEQ(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_prompt(edit, "> ");
ec_editline_set_node(edit, root);
ec_editline_interact(edit, check_exit_cb, NULL);
ec_editline_free(edit);
ec_node_free(root);
ec_exit();
return 0;
}
ec_editline_set_prompt
int ec_editline_set_prompt(struct ec_editline *editline, const char *prompt)
Set editline prompt.
ec_editline
struct ec_editline * ec_editline(const char *prog, FILE *f_in, FILE *f_out, FILE *f_err, enum ec_editline_init_flags flags)
Create an editline instance with default behavior.
ec_editline_free
void ec_editline_free(struct ec_editline *editline)
Free an editline structure allocated with ec_editline().
ec_editline_interact
int ec_editline_interact(struct ec_editline *editline, ec_editline_check_exit_cb_t check_exit_cb, void *opaque)
Interact with the user until exit.
ec_editline_set_node
int ec_editline_set_node(struct ec_editline *editline, const struct ec_node *node)
Attach an ecoli node to the editline structure.
ec_init
int ec_init(void)
Initialize ecoli library.
ec_exit
void ec_exit(void)
Uninitialize ecoli library.
ec_interact_set_help
int ec_interact_set_help(struct ec_node *node, const char *help)
Set help on a grammar node.
ec_interact_set_callback
int ec_interact_set_callback(struct ec_node *node, ec_interact_command_cb_t cb)
Set callback function on a grammar node.
ec_node_any
struct ec_node * ec_node_any(const char *id, const char *attr)
Create a "any" node.
ec_node_or_add
int ec_node_or_add(struct ec_node *node, struct ec_node *child)
Add a child to an "or" node.
EC_NODE_SEQ
#define EC_NODE_SEQ(args...)
Create a sequence node from a list of child nodes.
Definition node_seq.h:29
ec_node_sh_lex
struct ec_node * ec_node_sh_lex(const char *id, struct ec_node *child)
Create a shell lexer node.
ec_node_str
struct ec_node * ec_node_str(const char *id, const char *str)
Create a string node that matches a specific string.
ec_node
struct ec_node * ec_node(const char *typename, const char *id)
Create a new node from its type name.
EC_NO_ID
#define EC_NO_ID
Node has no identifier.
Definition node.h:64
ec_node_free
void ec_node_free(struct ec_node *node)
Decrement node reference counter and free the node if it is the last reference.
ec_pnode_get_strvec
const struct ec_strvec * ec_pnode_get_strvec(const struct ec_pnode *pnode)
Get the string vector associated with a parsing node.
ec_pnode_find
const struct ec_pnode * ec_pnode_find(const struct ec_pnode *root, const char *id)
Find a node from its identifier.
ec_pnode
struct ec_pnode * ec_pnode(const struct ec_node *node)
Create an empty parsing tree.
ec_strvec_val
const char * ec_strvec_val(const struct ec_strvec *strvec, size_t idx)
Get a string element from a vector.

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

- Architecture - How libecoli works internally
- Initialization - Library initialization
- Grammar nodes - Grammar node types and operations
- Parse nodes - Parsing API
- Complete - Completion API
- Editline - Interactive editing with libedit
- Interactive command line - Command callbacks and help system
- YAML import/export - YAML grammar import/export
- Node configuration - Node configuration system
- Log - Logging facilities

## 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