add ecoli_editline and enhance yaml parser
[protos/libecoli.git] / examples / yaml / parse-yaml.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright 2018, Olivier MATZ <zer0@droids-corp.org>
3  */
4
5 #include <stdlib.h>
6 #include <stdio.h>
7 #include <string.h>
8 #include <getopt.h>
9
10 #include <ecoli_strvec.h>
11 #include <ecoli_node.h>
12 #include <ecoli_parse.h>
13 #include <ecoli_complete.h>
14 #include <ecoli_yaml.h>
15 #include <ecoli_editline.h>
16 #include <ecoli_node_sh_lex.h>
17
18 static char *input_file;
19 static char *output_file;
20 static bool complete;
21
22 static const char short_options[] =
23         "h"  /* help */
24         "i:" /* input-file */
25         "o:" /* output-file */
26         "c"  /* complete */
27         ;
28
29 #define OPT_HELP "help"
30 #define OPT_INPUT_FILE "input-file"
31 #define OPT_OUTPUT_FILE "output-file"
32 #define OPT_COMPLETE "complete"
33
34 static const struct option long_options[] = {
35         {OPT_HELP, 0, NULL, 'h'},
36         {OPT_INPUT_FILE, 1, NULL, 'i'},
37         {OPT_OUTPUT_FILE, 1, NULL, 'o'},
38         {OPT_COMPLETE, 0, NULL, 'c'},
39         {NULL, 0, NULL, 0}
40 };
41
42 static void usage(const char *prgname)
43 {
44         fprintf(stderr, "%s -o <file.sh> -i <file.yaml>\n"
45                 "  -h\n"
46                 "  --"OPT_HELP"\n"
47                 "      Show this help.\n"
48                 "  -i <input-file>\n"
49                 "  --"OPT_INPUT_FILE"=<file>\n"
50                 "      Set the yaml input file describing the grammar.\n"
51                 "  -o <output-file>\n"
52                 "  --"OPT_OUTPUT_FILE"=<file>\n"
53                 "      Set the output file.\n"
54                 "  -c\n"
55                 "  --"OPT_COMPLETE"\n"
56                 "      Output the completion list."
57                 , prgname);
58 }
59
60 static int parse_args(int argc, char **argv)
61 {
62         int ret, opt;
63
64         while ((opt = getopt_long(argc, argv, short_options,
65                                 long_options, NULL)) != EOF) {
66
67                 switch (opt) {
68                 case 'h': /* help */
69                         usage(argv[0]);
70                         exit(0);
71
72                 case 'i': /* input-file */
73                         input_file = strdup(optarg);
74                         break;
75
76                 case 'o': /* output-file */
77                         output_file = strdup(optarg);
78                         break;
79
80                 case 'c': /* complete */
81                         complete = 1;
82                         break;
83
84                 default:
85                         usage(argv[0]);
86                         return -1;
87                 }
88
89         }
90
91         if (input_file == NULL) {
92                 fprintf(stderr, "No input file\n");
93                 usage(argv[0]);
94                 return -1;
95         }
96         if (output_file == NULL) {
97                 fprintf(stderr, "No output file\n");
98                 usage(argv[0]);
99                 return -1;
100         }
101
102         ret = optind - 1;
103         optind = 1;
104
105         return ret;
106 }
107
108 static int
109 __dump_as_shell(FILE *f, const struct ec_parse *parse, size_t *seq)
110 {
111         const struct ec_node *node = ec_parse_get_node(parse);
112         struct ec_parse *child;
113         size_t cur_seq, i, len;
114         const char *s;
115
116         (*seq)++;
117         cur_seq = *seq;
118
119         // XXX protect strings
120
121
122         fprintf(f, "ec_node%zu_id='%s'\n", cur_seq, ec_node_id(node));
123         fprintf(f, "ec_node%zu_type='%s'\n", cur_seq,
124                 ec_node_type_name(ec_node_type(node)));
125
126         len = ec_strvec_len(ec_parse_strvec(parse));
127         fprintf(f, "ec_node%zu_strvec_len=%zu\n", cur_seq, len);
128         for (i = 0; i < len; i++) {
129                 s = ec_strvec_val(ec_parse_strvec(parse), i);
130                 fprintf(f, "ec_node%zu_str%zu='%s'\n", cur_seq, i, s);
131         }
132
133         if (ec_parse_get_first_child(parse) != NULL) {
134                 fprintf(f, "ec_node%zu_first_child='ec_node%zu'\n",
135                         cur_seq, cur_seq + 1);
136         }
137
138         EC_PARSE_FOREACH_CHILD(child, parse) {
139                 fprintf(f, "ec_node%zu_parent='ec_node%zu'\n",
140                         *seq + 1, cur_seq);
141                 __dump_as_shell(f, child, seq);
142         }
143
144         if (ec_parse_get_next(parse) != NULL) {
145                 fprintf(f, "ec_node%zu_next='ec_node%zu'\n",
146                         cur_seq, *seq + 1);
147         }
148
149         return 0;
150 }
151
152 static int
153 dump_as_shell(const struct ec_parse *parse)
154 {
155         FILE *f;
156         size_t seq = 0;
157         int ret;
158
159         f = fopen(output_file, "w");
160         if (f == NULL)
161                 return -1;
162
163         ret = __dump_as_shell(f, parse, &seq);
164
165         fclose(f);
166
167         return ret;
168 }
169
170 static int
171 interact(struct ec_node *node)
172 {
173         struct ec_editline *editline = NULL;
174         struct ec_parse *parse = NULL;
175         struct ec_node *shlex = NULL;
176         char *line = NULL;
177
178         shlex = ec_node_sh_lex(EC_NO_ID, ec_node_clone(node)); //XXX
179         if (shlex == NULL) {
180                 fprintf(stderr, "Failed to add lexer node\n");
181                 goto fail;
182         }
183
184         editline = ec_editline("ecoli", stdin, stdout, stderr, 0);
185         if (editline == NULL) {
186                 fprintf(stderr, "Failed to initialize editline\n");
187                 goto fail;
188         }
189
190         parse = ec_editline_parse(editline, shlex);
191         if (parse == NULL)
192                 goto fail;
193
194         if (!ec_parse_matches(parse))
195                 goto fail;
196
197         //ec_parse_dump(stdout, parse);
198
199         if (dump_as_shell(parse) < 0) {
200                 fprintf(stderr, "Failed to dump the parsed result\n");
201                 goto fail;
202         }
203
204         ec_parse_free(parse);
205         ec_editline_free(editline);
206         ec_node_free(shlex);
207         return 0;
208
209 fail:
210         ec_parse_free(parse);
211         ec_editline_free(editline);
212         free(line);
213         ec_node_free(shlex);
214         return -1;
215 }
216
217 static int
218 complete_words(const struct ec_node *node, int argc, char *argv[])
219 {
220         struct ec_comp *comp = NULL;
221         struct ec_strvec *strvec = NULL;
222         struct ec_comp_iter *iter = NULL;
223         struct ec_comp_item *item = NULL;
224         size_t count;
225
226         if (argc <= 1)
227                 goto fail;
228         strvec = ec_strvec_from_array((const char * const *)&argv[1],
229                                 argc - 1);
230         if (strvec == NULL)
231                 goto fail;
232
233         comp = ec_node_complete_strvec(node, strvec);
234         if (comp == NULL)
235                 goto fail;
236
237         count = ec_comp_count(comp, EC_COMP_UNKNOWN | EC_COMP_FULL |
238                         EC_COMP_PARTIAL);
239
240         iter = ec_comp_iter(comp,
241                 EC_COMP_UNKNOWN | EC_COMP_FULL | EC_COMP_PARTIAL);
242         if (iter == NULL)
243                 goto fail;
244
245         while ((item = ec_comp_iter_next(iter)) != NULL) {
246
247                 /* only one match, display it fully */
248                 if (count == 1) {
249                         printf("%s\n", ec_comp_item_get_str(item));
250                         break;
251                 }
252
253                 /* else show the 'display' part only */
254                 printf("%s\n", ec_comp_item_get_display(item));
255         }
256
257         ec_comp_iter_free(iter);
258         ec_comp_free(comp);
259         ec_strvec_free(strvec);
260         return 0;
261
262 fail:
263         ec_comp_free(comp);
264         ec_strvec_free(strvec);
265         return -1;
266 }
267
268 int
269 main(int argc, char *argv[])
270 {
271         struct ec_node *node = NULL;
272         int ret;
273
274         ret = parse_args(argc, argv);
275         if (ret < 0)
276                 goto fail;
277
278         argc -= ret;
279         argv += ret;
280
281         if (ec_init() < 0) {
282                 fprintf(stderr, "cannot init ecoli: %s\n", strerror(errno));
283                 return 1;
284         }
285
286         node = ec_yaml_import(input_file);
287         if (node == NULL) {
288                 fprintf(stderr, "Failed to parse file\n");
289                 goto fail;
290         }
291         //ec_node_dump(stdout, node);
292
293         if (complete) {
294                 if (complete_words(node, argc, argv) < 0)
295                         goto fail;
296         } else {
297                 if (interact(node) < 0)
298                         goto fail;
299         }
300
301         ec_node_free(node);
302
303         return 0;
304
305 fail:
306         ec_node_free(node);
307         return 1;
308 }