build: Provide target distclean.
[lopsub.git] / lopsubgen.l
1 /*
2  * Copyright (C) 2016 Andre Noll <maan@tuebingen.mpg.de>
3  *
4  * Licensed under the GPL v3, see http://www.gnu.org/licenses/gpl-3.0.html
5  */
6
7 %option stack
8 %option never-interactive
9 %option yylineno
10
11 %s SC_COMMAND
12 %s SC_OPTION
13 %s SC_ARG_INFO
14 %s SC_ARG_TYPE
15 %s SC_OPTION_FLAG
16 %s SC_CHAR
17 %s SC_INTEGER
18 %s SC_UNQUOTED_LINE
19 %s SC_UNQUOTED_LINE_CHECK_DEFAULT
20 %s SC_ERROR
21 %s SC_VALUES_ID
22 %s SC_VALUES_COMMA_OR_BRACE
23
24 %x SC_INTRODUCTION
25 %x SC_DESCRIPTION
26 %x SC_CLOSING
27 %x SC_HELP
28 %x SC_CONCLUSION
29 %x SC_SECTION
30 %{
31         #include <ctype.h>
32         #include <stdlib.h>
33         #include <assert.h>
34         #include <stdbool.h>
35
36         #include "lsg.h"
37
38         static char **charpp;
39         static char *text_buf;
40         static size_t text_buf_len;
41         struct lsg_suite suite;
42
43         #define CURCMD (suite.commands[suite.num_subcommands])
44         #define CUROPT (CURCMD.options[CURCMD.num_options - 1])
45         #define CURSECT (suite.sections[suite.num_sections - 1])
46
47         static int yywrap(void) {return 1;}
48         static void *xmalloc(size_t size)
49         {
50                 void *p;
51
52                 assert(size > 0);
53                 p = malloc(size);
54                 assert(p);
55                 return p;
56         }
57         void *xrealloc(void *ptr, size_t size)
58         {
59                 void *p;
60                 assert(size > 0);
61                 p = realloc(ptr, size);
62                 assert(p);
63                 return p;
64         }
65
66         /*
67          * Extract ID from a string like [foo ID] or bar = ID, or a substring
68          * of a list: ID1, ID2, ...
69          */
70         static char *parse_identifier(char *p)
71         {
72                 char *p2, *eq;
73
74                 if (*p == '[') {
75                         while (*p && isspace(*p))
76                                 p++;
77                         while (*p && !isspace(*p))
78                                 p++;
79                 } else {
80                         if ((eq = strchr(p + 1, '=')))
81                                 p = eq + 1;
82                         else
83                                 while (*p && (isspace(*p) || *p == ','))
84                                         p++;
85                 }
86                 assert(*p);
87                 while (*p && isspace(*p))
88                         p++;
89                 assert(*p);
90                 p2 = p;
91                 while (isalnum(*p2) || *p2 == '_' || *p2 == '-')
92                         p2++;
93                 *p2 = '\0';
94                 return strdup(p);
95         }
96
97         static char *parse_simple_string(void)
98         {
99                 char *result, *p = yytext;
100                 while (isspace(*p))
101                         p++;
102                 assert(*p == '[');
103                 p++;
104                 while (isspace(*p))
105                         p++;
106                 while (!isspace(*p)) /* skip "section" */
107                         p++;
108                 while (isspace(*p))
109                         p++;
110                 result = strdup(p);
111                 p = strrchr(result, ']');
112                 do {
113                         *p = '\0';
114                         p--;
115                 } while (isspace(*p));
116                 return result;
117         }
118         static void parse_id_string(void)
119         {
120                 char *p, *q;
121                 int val_num = CUROPT.num_values++, num_vals = val_num + 1;
122                 bool backslash;
123
124                 CUROPT.values = xrealloc(CUROPT.values,
125                         num_vals * sizeof(char *));
126                 CUROPT.value_ids = xrealloc(CUROPT.value_ids,
127                         num_vals * sizeof(char *));
128                 CUROPT.value_literals = xrealloc(CUROPT.value_literals,
129                         num_vals * sizeof(char *));
130                 CUROPT.value_ids[val_num] = p = strdup(yytext);
131                 while (*p && !isspace(*p) && *p != '=')
132                         p++;
133                 *p = '\0';
134                 p++;
135                 while (*p && *p != '\"')
136                         p++;
137                 CUROPT.value_literals[val_num] = p;
138                 p++;
139                 CUROPT.values[val_num] = q = xmalloc(strlen(p) + 1);
140                 for (backslash = false; *p; p++) {
141                         if (*p == '\\') {
142                                 if (!backslash) {
143                                         backslash = true;
144                                         continue;
145                                 }
146                         } else if (*p == '"') {
147                                 if (!backslash) {
148                                         *q = '\0';
149                                         return;
150                                 }
151                         }
152                         *q++ = *p;
153                         backslash = false;
154                 }
155                 assert(false);
156         }
157         static char *parse_unquoted_line(void)
158         {
159                 char *p = strdup(yytext);
160                 size_t n = strlen(p);
161                 for (; n > 0; n--) {
162                         if (isspace(p[n - 1]))
163                                 continue;
164                         p[n] = '\0';
165                         break;
166                 }
167                 return p;
168         }
169         static void check_default_val(void)
170         {
171                 int i;
172
173                 if (!CUROPT.default_val)
174                         return;
175                 if (!CUROPT.values)
176                         return;
177                 for (i = 0; i < CUROPT.num_values; i++) {
178                         char *val = CUROPT.values[i];
179                         char buf[40];
180                         if (strcmp(val, CUROPT.default_val))
181                                 continue;
182                         sprintf(buf, "%i", i);
183                         free(CUROPT.default_val);
184                         CUROPT.default_val = strdup(buf);
185                         return;
186                 }
187                 fprintf(stderr, "option %s: bad default value %s\n",
188                         CUROPT.name.orig, CUROPT.default_val);
189                 exit(EXIT_FAILURE);
190         }
191
192 %}
193 EQUALS [[:space:]]*=[[:space:]]*
194 IDENTIFIER [a-zA-Z]+[a-zA-Z0-9_-]*
195 C99_DECIMAL_CONSTANT -?([[:digit:]]{-}[0])[[:digit:]]*
196 C99_HEXADECIMAL_CONSTANT 0[xX][[:xdigit:]]+
197 C99_OCTAL_CONSTANT 0[01234567]*
198 INT_CONSTANT {C99_DECIMAL_CONSTANT}|{C99_HEXADECIMAL_CONSTANT}|{C99_OCTAL_CONSTANT}
199 STRING_VALUE \"([^\"\\\n]|(\\[\"\\]))*\"
200 SIMPLE_STRING [[:alnum:]]([[:alnum:]]|[[:space:]])*
201 %%
202
203  /* skip comments and whitespace */
204 ^[[:space:]]*#.*\n ;
205 [[:space:]]|\n+ ;
206
207 <INITIAL>\[[[:space:]]*suite[[:space:]]+{IDENTIFIER}[[:space:]]*\] {
208         free(suite.name.orig);
209         suite.name.orig = parse_identifier(yytext);
210 }
211
212 <INITIAL>caption{EQUALS} {
213         charpp = &suite.caption;
214         yy_push_state(SC_UNQUOTED_LINE);
215 }
216
217 <INITIAL>title{EQUALS} {
218         charpp = &suite.title;
219         yy_push_state(SC_UNQUOTED_LINE);
220 }
221
222 <INITIAL>mansect{EQUALS} {
223         charpp = &suite.mansect;
224         yy_push_state(SC_UNQUOTED_LINE);
225 }
226
227 <INITIAL>date{EQUALS} {
228         charpp = &suite.date;
229         yy_push_state(SC_UNQUOTED_LINE);
230 }
231
232 <INITIAL>version-string{EQUALS} {
233         charpp = &suite.version_string;
234         yy_push_state(SC_UNQUOTED_LINE);
235 }
236
237 <INITIAL>manual_title{EQUALS} {
238         charpp = &suite.manual_title;
239         yy_push_state(SC_UNQUOTED_LINE);
240 }
241
242 <INITIAL>aux_info_prefix{EQUALS} {
243         charpp = &suite.aux_info_prefix;
244         yy_push_state(SC_UNQUOTED_LINE);
245 }
246
247 <INITIAL>aux_info_default{EQUALS} {
248         charpp = &suite.aux_info_default;
249         yy_push_state(SC_UNQUOTED_LINE);
250 }
251
252 <INITIAL,SC_COMMAND,SC_OPTION>\[[[:space:]]*introduction[[:space:]]*\] {
253         text_buf = NULL;
254         text_buf_len = 0;
255         yy_push_state(SC_INTRODUCTION);
256 }
257
258 <SC_INTRODUCTION>[[:space:]]*\[[[:space:]]*\/introduction[[:space:]]*\]\n {
259         suite.introduction = text_buf;
260         yy_pop_state();
261 }
262
263 <INITIAL,SC_COMMAND,SC_OPTION>\[[[:space:]]*conclusion[[:space:]]*\] {
264         text_buf = NULL;
265         text_buf_len = 0;
266         yy_push_state(SC_CONCLUSION);
267 }
268
269 <SC_CONCLUSION>[[:space:]]*\[[[:space:]]*\/conclusion[[:space:]]*\]\n {
270         suite.conclusion = text_buf;
271         yy_pop_state();
272 }
273
274 <INITIAL>\[[[:space:]]*supercommand[[:space:]]+{IDENTIFIER}[[:space:]]*\] {
275         struct lsg_command *cmd;
276
277         if (!suite.commands)
278                 suite.commands = xmalloc(sizeof(*suite.commands));
279         cmd = suite.commands;
280         memset(cmd, 0, sizeof(*cmd));
281         cmd->name.orig = parse_identifier(yytext);
282         cmd->options = xmalloc(sizeof(*cmd->options));
283         BEGIN(SC_COMMAND);
284 }
285
286 <INITIAL,SC_COMMAND,SC_OPTION>\[[[:space:]]*subcommand[[:space:]]+{IDENTIFIER}[[:space:]]*\] {
287         int command_num = ++suite.num_subcommands;
288         struct lsg_command *cmd;
289
290         suite.commands = realloc(suite.commands,
291                 (suite.num_subcommands + 1) * sizeof(*suite.commands));
292         cmd = suite.commands + command_num;
293         memset(cmd, 0, sizeof(*cmd));
294         cmd->name.orig = parse_identifier(yytext);
295         cmd->options = xmalloc(sizeof(*cmd->options));
296         BEGIN(SC_COMMAND);
297 }
298
299 <SC_COMMAND>\[[[:space:]]*description[[:space:]]*\] {
300         text_buf = NULL;
301         text_buf_len = 0;
302         yy_push_state(SC_DESCRIPTION);
303 }
304
305 <SC_COMMAND,SC_OPTION>\[[[:space:]]*closing[[:space:]]*\] {
306         text_buf = NULL;
307         text_buf_len = 0;
308         yy_push_state(SC_CLOSING);
309 }
310
311 <SC_OPTION>\[[[:space:]]*help[[:space:]]*\] {
312         text_buf = NULL;
313         text_buf_len = 0;
314         yy_push_state(SC_HELP);
315 }
316 <INITIAL,SC_OPTION,SC_COMMAND>\[[[:space:]]*section[[:space:]]+{SIMPLE_STRING}[[:space:]]*\] {
317         int sect_num = suite.num_sections++;
318         suite.sections = realloc(suite.sections,
319                  suite.num_sections * sizeof(*suite.sections));
320         CURSECT.name.orig = parse_simple_string();
321         text_buf = NULL;
322         text_buf_len = 0;
323         yy_push_state(SC_SECTION);
324 }
325
326 <SC_DESCRIPTION>[[:space:]]*\[[[:space:]]*\/description[[:space:]]*\]\n {
327         CURCMD.description = text_buf;
328         yy_pop_state();
329 }
330
331 <SC_CLOSING>[[:space:]]*\[[[:space:]]*\/closing[[:space:]]*\]\n {
332         CURCMD.closing = text_buf;
333         yy_pop_state();
334 }
335
336 <SC_HELP>[[:space:]]*\[[[:space:]]*\/help[[:space:]]*\]\n {
337         CUROPT.help = text_buf;
338         yy_pop_state();
339 }
340
341 <SC_SECTION>[[:space:]]*\[[[:space:]]*\/section[[:space:]]*\]\n {
342         CURSECT.text = text_buf;
343         yy_pop_state();
344 }
345
346 <SC_DESCRIPTION,SC_HELP,SC_SECTION,SC_CLOSING,SC_INTRODUCTION,SC_CONCLUSION>.*\n {
347         size_t new_len = text_buf_len + yyleng;
348         size_t num_tabs = 0;
349
350         while (yytext[num_tabs] == '\t')
351                 num_tabs++;
352         new_len = text_buf_len + yyleng - num_tabs;
353         text_buf = realloc(text_buf, new_len + 1);
354         memcpy(text_buf + text_buf_len, yytext + num_tabs, yyleng - num_tabs);
355         text_buf[new_len] = '\0';
356         text_buf_len = new_len;
357 }
358
359 <SC_COMMAND,SC_OPTION>\[[[:space:]]*option[[:space:]]+{IDENTIFIER}[[:space:]]*\] {
360         int option_num = CURCMD.num_options++;
361         struct lsg_option *opt;
362
363         CURCMD.options = realloc(CURCMD.options,
364                 CURCMD.num_options * sizeof(*CURCMD.options));
365         memset(&CUROPT, 0, sizeof(CUROPT));
366         CUROPT.name.orig = parse_identifier(yytext);
367         BEGIN(SC_OPTION);
368 }
369
370 <SC_OPTION>arg_info{EQUALS} BEGIN(SC_ARG_INFO);
371 <SC_ARG_INFO>no_arg CUROPT.arg_info = "LLS_NO_ARGUMENT"; BEGIN(SC_OPTION);
372 <SC_ARG_INFO>required_arg CUROPT.arg_info = "LLS_REQUIRED_ARGUMENT"; BEGIN(SC_OPTION);
373 <SC_ARG_INFO>optional_arg CUROPT.arg_info = "LLS_OPTIONAL_ARGUMENT"; BEGIN(SC_OPTION);
374
375 <SC_OPTION>arg_type{EQUALS} BEGIN(SC_ARG_TYPE);
376 <SC_ARG_TYPE>none CUROPT.arg_type = "LLS_NONE"; BEGIN(SC_OPTION);
377 <SC_ARG_TYPE>string CUROPT.arg_type = "LLS_STRING"; BEGIN(SC_OPTION);
378 <SC_ARG_TYPE>int32 CUROPT.arg_type = "LLS_INT32"; BEGIN(SC_OPTION);
379 <SC_ARG_TYPE>uint32 CUROPT.arg_type = "LLS_UINT32"; BEGIN(SC_OPTION);
380 <SC_ARG_TYPE>int64 CUROPT.arg_type = "LLS_INT64"; BEGIN(SC_OPTION);
381 <SC_ARG_TYPE>uint64 CUROPT.arg_type = "LLS_UINT64"; BEGIN(SC_OPTION);
382
383 <SC_OPTION>flag BEGIN(SC_OPTION_FLAG);
384 <SC_OPTION_FLAG>multiple CUROPT.multiple = true; BEGIN(SC_OPTION);
385 <SC_OPTION_FLAG>required CUROPT.required = true; BEGIN(SC_OPTION);
386 <SC_OPTION_FLAG>ignored CUROPT.ignored = true; BEGIN(SC_OPTION);
387
388 <SC_OPTION>default_val{EQUALS} {
389         charpp = &CUROPT.default_val;
390         if (!CUROPT.arg_type || strcmp(CUROPT.arg_type, "LLS_NONE") == 0) {
391                 fprintf(stderr, "default_value for option w/o arguments!?\n");
392                 exit(1);
393         } else if (strcmp(CUROPT.arg_type, "LLS_STRING") == 0)
394                 yy_push_state(SC_UNQUOTED_LINE_CHECK_DEFAULT);
395         else
396                 yy_push_state(SC_INTEGER);
397 }
398 <SC_OPTION>short_opt{EQUALS} BEGIN(SC_CHAR);
399 <SC_CHAR>[a-zA-Z0-9] CUROPT.short_opt = yytext[0]; BEGIN(SC_OPTION);
400
401 <SC_OPTION>values{EQUALS}\{ {
402         if (!CUROPT.arg_type || strcmp(CUROPT.arg_type, "LLS_STRING")) {
403                 fprintf(stderr, "value list is only supported for string options\n");
404                 exit(EXIT_FAILURE);
405         }
406         if (!CUROPT.arg_info || !strcmp(CUROPT.arg_info, "LLS_NO_ARGUMENT")) {
407                 fprintf(stderr, "enum options must take an argument\n");
408                 exit(EXIT_FAILURE);
409         }
410         if (CUROPT.default_val) {
411                 fprintf(stderr, "value list must preceed default value\n");
412                 exit(EXIT_FAILURE);
413         }
414         BEGIN(SC_VALUES_ID);
415 }
416
417 <SC_VALUES_ID>{IDENTIFIER}{EQUALS}{STRING_VALUE} {
418         parse_id_string();
419         BEGIN(SC_VALUES_COMMA_OR_BRACE);
420 }
421
422 <SC_VALUES_COMMA_OR_BRACE>[,\}] {
423         if (*yytext == ',')
424                 BEGIN(SC_VALUES_ID);
425         else {
426                 check_default_val();
427                 BEGIN(SC_OPTION);
428         }
429 }
430
431 <SC_OPTION>summary{EQUALS} {
432         charpp = &CUROPT.summary;
433         yy_push_state(SC_UNQUOTED_LINE);
434 }
435 <SC_OPTION>typestr{EQUALS} {
436         charpp = &CUROPT.typestr;
437         yy_push_state(SC_UNQUOTED_LINE);
438 }
439
440 <SC_COMMAND>purpose{EQUALS} {
441         charpp = &CURCMD.purpose;
442         yy_push_state(SC_UNQUOTED_LINE);
443 }
444
445 <SC_COMMAND>non-opts-name{EQUALS} {
446         charpp = &CURCMD.non_opts_name;
447         yy_push_state(SC_UNQUOTED_LINE);
448 }
449
450 <SC_COMMAND>synopsis{EQUALS} {
451         charpp = &CURCMD.synopsis;
452         yy_push_state(SC_UNQUOTED_LINE);
453 }
454
455 <SC_COMMAND>aux_info{EQUALS} {
456         charpp = &CURCMD.aux_info;
457         yy_push_state(SC_UNQUOTED_LINE);
458 }
459
460 <SC_UNQUOTED_LINE>.*\n {
461         *charpp = parse_unquoted_line();
462         yy_pop_state();
463 }
464 <SC_UNQUOTED_LINE_CHECK_DEFAULT>.*\n {
465         *charpp = parse_unquoted_line();
466         check_default_val();
467         yy_pop_state();
468 }
469
470 <SC_INTEGER>{INT_CONSTANT} *charpp = strdup(yytext); yy_pop_state();
471
472  /* This rule runs iff none of the above patterns matched */
473 . {
474         fprintf(stderr, "parse error at line %d. Unmatched: \"%s\"\n",
475                 yyget_lineno(), yytext);
476         BEGIN(SC_ERROR);
477 }
478 <SC_ERROR>.*\n {
479         fprintf(stderr, "subsequent unparsed input: %s\n", yytext);
480         exit(EXIT_FAILURE);
481 }