architecture(3)
architecture(3) β the libecoli β¨architectureβ¨ page, bestie edition π
Okay so real talk, this is architecture out of the Libecoli manual, section 3, and it's genuinely one of my faves. Libecoli is written in C and gives you an API for building interactive command line interfaces. Like, it's the whole personality of your CLI. π
πΈ NAME πΈ
architecture β the architecture of Libecoli. It's written in C, and it provides an API for building interactive command line interfaces. The library is made up of several components:
- Core β the main API for parsing and completing input strings.
- Nodes β the modular component of libecoli. Each node type implements its own specific parsing behavior (integers, strings, regular expressions, sequences, etc.).
- YAML parser β loads grammar trees from YAML configuration files.
- Editline β integration helpers for the editline library.
- Utilities β logging, string manipulation, string vectors, hash tables, and other support functions.
π¦ The Grammar Graph π¦
Nodes are organized into a directed graph that defines the grammar. It's typically a tree, but lowkey loops are permitted in certain cases. The grammar graph describes how input is parsed and completed. Periodt.
There's a figure here in the original, labeled Simple grammar tree β yes, bestie, it's a whole diagram. π
The same structure can be represented textually as:
sh_lex(
seq(
str(foo),
option(
str(bar)
)
)
)
This grammar matches:
'foo''foo bar'
It does not match:
'bar''foobar'''(empty input)
π₯ Parsing π₯
When libecoli parses input, it traverses the grammar graph using depth-first search and constructs a parse tree. The following example illustrates the process when ec_parse_strvec() is called:
- The input is a string vector containing
['foo bar']. - The
sh_lexnode tokenizes the input using shell lexing rules, producing['foo', 'bar'], and passes this to its child. - The
seqnode forwards the input to its first child. - The
strnode checks whether the first token equalsfoo. Since it does, the node returns1(the number of consumed tokens) to its parent. - The
seqnode passes the remaining tokens['bar']to its next child. - The
optionnode forwards the input to its child, which matches and returns1. - The
seqnode has processed all children and returns2(total consumed tokens). - The
sh_lexnode compares the return value against its token count and returns1(success) to the caller.
When a node fails to match, it returns EC_PARSE_NOMATCH to its parent. That value propagates up the tree until some node handles the failure β for example, the or node tries each child in sequence until one of them matches. No cap, that's the whole strategy. π
β The Parse Tree β
Consider another example grammar β the original has a figure here labeled Grammar with or node, and yep, it's a whole vibe. π
Without a lexer node, the input must already be tokenized. This grammar matches:
['foo']['bar', '1']['bar', '100']
It does not match:
['bar 1'](not tokenized)['bar'](missing required argument)[](empty input)
During parsing, a parse tree is constructed. When parsing succeeds, the tree describes which grammar nodes matched and what input each node consumed.
Parsing ['bar', '1'] produces the following parse tree β yep, another figure in the original, this one labeled Parse tree structure. π«Ά
Each parse tree node references the grammar node that matched and stores the corresponding input tokens. (Original figure: Parse tree with token references β iconic.)
Not all grammar nodes appear in the parse tree. In this example, str('foo') was not matched and therefore does not appear. Conversely, a single grammar node may appear multiple times in the parse tree. Consider this grammar that matches zero or more occurrences of foo (original figure: Grammar with many node):
Parsing [foo, foo, foo] produces β you guessed it, a figure, labeled Parse tree with repeated matches. Obsessed with the consistency tbh. π
π Node Identifiers π
Each grammar node may have an optional string identifier. This identifier enables locating specific nodes in the parse tree after parsing completes. For example, a node created with ec_node_int('COUNT', 0, 100, 10) can be found using ec_pnode_find(parse_tree, 'COUNT').
Identifiers do not need to be unique within the grammar graph. When multiple nodes share the same identifier, ec_pnode_find() returns the first match. Use ec_pnode_find_next() to iterate through additional matches. iykyk. π
π§ Completion π§
The completion mechanism operates similarly to parsing, but it collects possible continuations instead of validating input. When the user requests completion, libecoli traverses the grammar graph and queries each node for tokens that could follow the current partial input.
Completions are grouped by the grammar node that produced them. Each completion item has one of three types:
EC_COMP_FULLβ a complete token (e.g., a command keyword).EC_COMP_PARTIALβ a partial token requiring further input (e.g., a directory path).EC_COMP_UNKNOWNβ valid input that cannot be completed (e.g., an arbitrary string matching a regular expression).
π Node Attributes π
Grammar nodes support arbitrary key-value attributes. The interactive layer uses these attributes to store:
- Help text displayed during completion or on errors
- Callbacks invoked when parsing succeeds
- Short descriptions for contextual help
Use ec_node_attrs() to access the attribute dictionary and manipulate it with ec_dict_set() and ec_dict_get(). The Interactive command line API provides convenience functions such as ec_interact_set_help() and ec_interact_set_callback(). Slay.
π Node Configuration π
Nodes support a generic configuration system for setting parameters after creation. Each node type defines a schema describing its configuration options. The YAML parser uses this system to instantiate nodes from configuration files.
See Node configuration for details.
Fr fr, that's the whole page β every component, every node type, every match and non-match, all theEC_PARSE_NOMATCHandEC_COMP_*constants, and every figure, untouched. π
Okay and this page is literally brought to you by Version 0.11.3, dated Thu Apr 9 2026, and it's the architecture(3) page, so like β cite it correctly bestie, or don't cite it at all. πβ¨π¦
π¦ where this page comes from
- epel 9: libecoli-devel 0.11.3-1.el9 Β· BSD-3-Clause AND LicenseRef-Fedora-Public-Domain
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms