|
Libecoli
0.11.3
Extensible COmmand LIne library
|
Libecoli is written in C and provides an API for building interactive command line interfaces. The library consists of several components:
Nodes are organized into a directed graph that defines the grammar. Although this structure is typically a tree, loops are permitted in certain cases. The grammar graph describes how input is parsed and completed.
Consider the following example:
The same structure can be represented textually as:
This grammar matches:
"foo""foo bar"It does not match:
"bar""foobar""" (empty input)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:
["foo bar"].sh_lex node tokenizes the input using shell lexing rules, producing ["foo", "bar"], and passes this to its child.seq node forwards the input to its first child.str node checks whether the first token equals foo. Since it does, the node returns 1 (the number of consumed tokens) to its parent.seq node passes the remaining tokens ["bar"] to its next child.option node forwards the input to its child, which matches and returns 1.seq node has processed all children and returns 2 (total consumed tokens).sh_lex node compares the return value against its token count and returns 1 (success) to the caller.When a node fails to match, it returns EC_PARSE_NOMATCH to its parent. This value propagates up the tree until a node handles the failure. For example, the or node tries each child in sequence until one matches.
Consider another example grammar:
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:
Each parse tree node references the grammar node that matched and stores the corresponding input tokens:
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:
Parsing [foo, foo, foo] produces:
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 need not 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.
The completion mechanism operates similarly to parsing but 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:
Grammar nodes support arbitrary key-value attributes. The interactive layer uses these attributes to store:
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().
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.