Load grammar from YAML and parse or complete input.
Load grammar from YAML and parse or complete input.Demonstrates loading a grammar definition from a YAML file using ec_yaml_import() and using it to parse or complete command line input.
#include <errno.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ecoli.h>
static char *input_file;
static char *output_file;
static bool complete;
static const char short_options[] =
"h"
"i:"
"o:"
"c"
;
#define OPT_HELP "help"
#define OPT_INPUT_FILE "input-file"
#define OPT_OUTPUT_FILE "output-file"
#define OPT_COMPLETE "complete"
static const struct option long_options[] = {
{OPT_HELP, 0, NULL, 'h'},
{OPT_INPUT_FILE, 1, NULL, 'i'},
{OPT_OUTPUT_FILE, 1, NULL, 'o'},
{OPT_COMPLETE, 0, NULL, 'c'},
{NULL, 0, NULL, 0}
};
static void usage(const char *prgname)
{
fprintf(stderr,
"%s -o <file.sh> -i <file.yaml>\n"
" -h\n"
" --" OPT_HELP "\n"
" Show this help.\n"
" -i <input-file>\n"
" --" OPT_INPUT_FILE "=<file>\n"
" Set the yaml input file describing the grammar.\n"
" -o <output-file>\n"
" --" OPT_OUTPUT_FILE "=<file>\n"
" Set the output file.\n"
" -c\n"
" --" OPT_COMPLETE "\n"
" Output the completion list.\n",
prgname);
}
static int parse_args(int argc, char **argv)
{
int ret, opt;
while ((opt = getopt_long(argc, argv, short_options, long_options, NULL)) != EOF) {
switch (opt) {
case 'h':
usage(argv[0]);
exit(0);
case 'i':
input_file = strdup(optarg);
break;
case 'o':
output_file = strdup(optarg);
break;
case 'c':
complete = 1;
break;
default:
usage(argv[0]);
return -1;
}
}
if (input_file == NULL) {
fprintf(stderr, "No input file\n");
usage(argv[0]);
return -1;
}
if (output_file == NULL) {
fprintf(stderr, "No output file\n");
usage(argv[0]);
return -1;
}
ret = optind - 1;
optind = 1;
return ret;
}
static int __dump_as_shell(FILE *f,
const struct ec_pnode *parse,
size_t *seq)
{
size_t cur_seq, i, len;
char *quoted;
(*seq)++;
cur_seq = *seq;
fprintf(f, "ec_node%zu_id=%s\n", cur_seq, quoted);
free(quoted);
fprintf(f, "ec_node%zu_type=%s\n", cur_seq, quoted);
free(quoted);
fprintf(f, "ec_node%zu_strvec_len=%zu\n", cur_seq, len);
for (i = 0; i < len; i++) {
fprintf(f, "ec_node%zu_str%zu=%s\n", cur_seq, i, quoted);
free(quoted);
}
fprintf(f, "ec_node%zu_first_child='ec_node%zu'\n", cur_seq, cur_seq + 1);
}
fprintf(f, "ec_node%zu_parent='ec_node%zu'\n", *seq + 1, cur_seq);
__dump_as_shell(f, child, seq);
}
fprintf(f, "ec_node%zu_next='ec_node%zu'\n", cur_seq, *seq + 1);
}
return 0;
}
static int dump_as_shell(
const struct ec_pnode *parse)
{
FILE *f;
size_t seq = 0;
int ret;
f = fopen(output_file, "w");
if (f == NULL)
return -1;
ret = __dump_as_shell(f, parse, &seq);
fclose(f);
return ret;
}
static int interact(
struct ec_node *node)
{
if (shlex == NULL) {
fprintf(stderr, "Failed to add lexer node\n");
goto fail;
}
editline =
ec_editline(
"parse-yaml", stdin, stdout, stderr, 0);
if (editline == NULL) {
fprintf(stderr, "Failed to initialize editline\n");
goto fail;
}
fprintf(stderr, "Failed to set editline ec_node\n");
goto fail;
}
if (parse == NULL)
goto fail;
goto fail;
if (dump_as_shell(parse) < 0) {
fprintf(stderr, "Failed to dump the parsed result\n");
goto fail;
}
return 0;
fail:
return -1;
}
static int complete_words(
const struct ec_node *node,
int argc,
char *argv[])
{
struct ec_comp_item *item = NULL;
size_t count;
if (argc <= 1)
goto fail;
if (strvec == NULL)
goto fail;
if (comp == NULL)
goto fail;
if (count == 1) {
break;
}
}
return 0;
fail:
return -1;
}
int main(int argc, char *argv[])
{
int ret;
ret = parse_args(argc, argv);
if (ret < 0)
goto fail;
argc -= ret;
argv += ret;
fprintf(stderr, "cannot init ecoli: %s\n", strerror(errno));
return 1;
}
if (node == NULL) {
fprintf(stderr, "Failed to parse file\n");
goto fail;
}
if (complete) {
if (complete_words(node, argc, argv) < 0)
goto fail;
} else {
if (interact(node) < 0)
goto fail;
}
return 0;
fail:
return 1;
}
struct ec_comp * ec_comp(void)
Create an empty completion object (list of completion items).
const char * ec_comp_item_get_display(const struct ec_comp_item *item)
Get the display string value of a completion item.
struct ec_comp * ec_complete_strvec(const struct ec_node *node, const struct ec_strvec *strvec)
Get the list of completions from a string vector input.
size_t ec_comp_count(const struct ec_comp *comp, enum ec_comp_type type)
Get the number of completion items.
void ec_comp_free(struct ec_comp *comp)
Free a completion object and all its items.
const char * ec_comp_item_get_str(const struct ec_comp_item *item)
Get the string value of a completion item.
#define EC_COMP_FOREACH(item, comp, type)
Iterate items matching a given type.
@ EC_COMP_FULL
The item is fully completed.
@ EC_COMP_PARTIAL
The item is partially completed.
@ EC_COMP_UNKNOWN
Valid token but completion not possible.
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.
void ec_editline_free(struct ec_editline *editline)
Free an editline structure allocated with ec_editline().
struct ec_pnode * ec_editline_parse(struct ec_editline *editline)
Get a line interactively (with completion), and parse it.
int ec_editline_set_node(struct ec_editline *editline, const struct ec_node *node)
Attach an ecoli node to the editline structure.
int ec_init(void)
Initialize ecoli library.
struct ec_node * ec_node_sh_lex(const char *id, struct ec_node *child)
Create a shell lexer node.
struct ec_node * ec_node_clone(struct ec_node *node)
Clone a grammar node.
const char * ec_node_id(const struct ec_node *node)
Get node identifier.
struct ec_node * ec_node(const char *typename, const char *id)
Create a new node from its type name.
#define EC_NO_ID
Node has no identifier.
const struct ec_node_type * ec_node_type(const struct ec_node *node)
Get the type of a node.
const char * ec_node_type_name(const struct ec_node_type *type)
Get the name of a node type.
void ec_node_free(struct ec_node *node)
Decrement node reference counter and free the node if it is the last reference.
bool ec_pnode_matches(const struct ec_pnode *pnode)
Check if the parsing tree matches the input.
struct ec_pnode * ec_pnode_next(const struct ec_pnode *pnode)
Get the next sibling node.
void ec_pnode_free(struct ec_pnode *pnode)
Free a parsing tree.
const struct ec_strvec * ec_pnode_get_strvec(const struct ec_pnode *pnode)
Get the string vector associated with a parsing node.
#define EC_PNODE_FOREACH_CHILD(child, pnode)
Iterate the children of a node.
struct ec_pnode * ec_pnode_get_first_child(const struct ec_pnode *pnode)
Get the first child of a node in the parsing tree.
const struct ec_node * ec_pnode_get_node(const struct ec_pnode *pnode)
Get the grammar node corresponding to the parsing node.
struct ec_pnode * ec_pnode(const struct ec_node *node)
Create an empty parsing tree.
char * ec_str_quote(const char *str, char quote, bool force)
Quote a string, escaping nested quotes.
struct ec_strvec * ec_strvec_from_array(const char *const *strarr, size_t n)
Allocate a new string vector.
void ec_strvec_free(struct ec_strvec *strvec)
Free a string vector.
const char * ec_strvec_val(const struct ec_strvec *strvec, size_t idx)
Get a string element from a vector.
struct ec_strvec * ec_strvec(void)
Allocate a new empty string vector.
size_t ec_strvec_len(const struct ec_strvec *strvec)
Get the length of a string vector.
struct ec_node * ec_yaml_import(const char *filename)
Parse a YAML file and build an ec_node tree from it.