2 * Copyright (C) 2016 Andre Noll <maan@tuebingen.mpg.de>
4 * Licensed under the GPL v3, see https://www.gnu.org/licenses/gpl-3.0.html
8 %option never-interactive
19 %s SC_UNQUOTED_LINE_CHECK_DEFAULT
22 %s SC_VALUES_COMMA_OR_BRACE
39 static char *text_buf;
40 static size_t text_buf_len;
41 struct lsg_suite suite;
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])
47 static int yywrap(void) {return 1;}
48 static void *xmalloc(size_t size)
57 void *xrealloc(void *ptr, size_t size)
61 p = realloc(ptr, size);
67 * Extract ID from a string like [foo ID] or bar = ID, or a substring
68 * of a list: ID1, ID2, ...
70 static char *parse_identifier(char *p)
75 while (*p && isspace(*p))
77 while (*p && !isspace(*p))
80 if ((eq = strchr(p + 1, '=')))
83 while (*p && (isspace(*p) || *p == ','))
87 while (*p && isspace(*p))
91 while (isalnum(*p2) || *p2 == '_' || *p2 == '-')
97 static char *parse_simple_string(void)
99 char *result, *p = yytext;
106 while (!isspace(*p)) /* skip "section" */
111 p = strrchr(result, ']');
115 } while (isspace(*p));
118 static void parse_id_string(void)
121 int val_num = CUROPT.num_values++, num_vals = val_num + 1;
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 != '=')
135 while (*p && *p != '\"')
137 CUROPT.value_literals[val_num] = p;
139 CUROPT.values[val_num] = q = xmalloc(strlen(p) + 1);
140 for (backslash = false; *p; p++) {
146 } else if (*p == '"') {
157 static char *parse_unquoted_line(void)
159 char *p = strdup(yytext);
160 size_t n = strlen(p);
162 if (isspace(p[n - 1]))
169 static void check_default_val(void)
173 if (!CUROPT.default_val)
177 for (i = 0; i < CUROPT.num_values; i++) {
178 char *val = CUROPT.values[i];
180 if (strcmp(val, CUROPT.default_val))
182 sprintf(buf, "%i", i);
183 free(CUROPT.default_val);
184 CUROPT.default_val = strdup(buf);
187 fprintf(stderr, "option %s: bad default value %s\n",
188 CUROPT.name.orig, CUROPT.default_val);
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:]])*
203 /* skip comments and whitespace */
207 <INITIAL>\[[[:space:]]*suite[[:space:]]+{IDENTIFIER}[[:space:]]*\] {
208 free(suite.name.orig);
209 suite.name.orig = parse_identifier(yytext);
212 <INITIAL>caption{EQUALS} {
213 charpp = &suite.caption;
214 yy_push_state(SC_UNQUOTED_LINE);
217 <INITIAL>title{EQUALS} {
218 charpp = &suite.title;
219 yy_push_state(SC_UNQUOTED_LINE);
222 <INITIAL>mansect{EQUALS} {
223 charpp = &suite.mansect;
224 yy_push_state(SC_UNQUOTED_LINE);
227 <INITIAL>date{EQUALS} {
228 charpp = &suite.date;
229 yy_push_state(SC_UNQUOTED_LINE);
232 <INITIAL>version-string{EQUALS} {
233 charpp = &suite.version_string;
234 yy_push_state(SC_UNQUOTED_LINE);
237 <INITIAL>manual_title{EQUALS} {
238 charpp = &suite.manual_title;
239 yy_push_state(SC_UNQUOTED_LINE);
242 <INITIAL>aux_info_prefix{EQUALS} {
243 charpp = &suite.aux_info_prefix;
244 yy_push_state(SC_UNQUOTED_LINE);
247 <INITIAL>aux_info_default{EQUALS} {
248 charpp = &suite.aux_info_default;
249 yy_push_state(SC_UNQUOTED_LINE);
252 <INITIAL,SC_COMMAND,SC_OPTION>\[[[:space:]]*introduction[[:space:]]*\] {
255 yy_push_state(SC_INTRODUCTION);
258 <SC_INTRODUCTION>[[:space:]]*\[[[:space:]]*\/introduction[[:space:]]*\]\n {
259 suite.introduction = text_buf;
263 <INITIAL,SC_COMMAND,SC_OPTION>\[[[:space:]]*conclusion[[:space:]]*\] {
266 yy_push_state(SC_CONCLUSION);
269 <SC_CONCLUSION>[[:space:]]*\[[[:space:]]*\/conclusion[[:space:]]*\]\n {
270 suite.conclusion = text_buf;
274 <INITIAL>\[[[:space:]]*supercommand[[:space:]]+{IDENTIFIER}[[:space:]]*\] {
275 struct lsg_command *cmd;
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));
286 <INITIAL,SC_COMMAND,SC_OPTION>\[[[:space:]]*subcommand[[:space:]]+{IDENTIFIER}[[:space:]]*\] {
287 int command_num = ++suite.num_subcommands;
288 struct lsg_command *cmd;
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));
299 <SC_COMMAND>\[[[:space:]]*description[[:space:]]*\] {
302 yy_push_state(SC_DESCRIPTION);
305 <SC_COMMAND,SC_OPTION>\[[[:space:]]*closing[[:space:]]*\] {
308 yy_push_state(SC_CLOSING);
311 <SC_OPTION>\[[[:space:]]*help[[:space:]]*\] {
314 yy_push_state(SC_HELP);
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();
323 yy_push_state(SC_SECTION);
326 <SC_DESCRIPTION>[[:space:]]*\[[[:space:]]*\/description[[:space:]]*\]\n {
327 CURCMD.description = text_buf;
331 <SC_CLOSING>[[:space:]]*\[[[:space:]]*\/closing[[:space:]]*\]\n {
332 CURCMD.closing = text_buf;
336 <SC_HELP>[[:space:]]*\[[[:space:]]*\/help[[:space:]]*\]\n {
337 CUROPT.help = text_buf;
341 <SC_SECTION>[[:space:]]*\[[[:space:]]*\/section[[:space:]]*\]\n {
342 CURSECT.text = text_buf;
346 <SC_DESCRIPTION,SC_HELP,SC_SECTION,SC_CLOSING,SC_INTRODUCTION,SC_CONCLUSION>.*\n {
347 size_t new_len = text_buf_len + yyleng;
350 while (yytext[num_tabs] == '\t')
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;
359 <SC_COMMAND,SC_OPTION>\[[[:space:]]*option[[:space:]]+{IDENTIFIER}[[:space:]]*\] {
360 int option_num = CURCMD.num_options++;
361 struct lsg_option *opt;
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);
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);
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);
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);
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");
393 } else if (strcmp(CUROPT.arg_type, "LLS_STRING") == 0)
394 yy_push_state(SC_UNQUOTED_LINE_CHECK_DEFAULT);
396 yy_push_state(SC_INTEGER);
398 <SC_OPTION>short_opt{EQUALS} BEGIN(SC_CHAR);
399 <SC_CHAR>[a-zA-Z0-9] CUROPT.short_opt = yytext[0]; BEGIN(SC_OPTION);
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");
406 if (!CUROPT.arg_info || !strcmp(CUROPT.arg_info, "LLS_NO_ARGUMENT")) {
407 fprintf(stderr, "enum options must take an argument\n");
410 if (CUROPT.default_val) {
411 fprintf(stderr, "value list must precede default value\n");
417 <SC_VALUES_ID>{IDENTIFIER}{EQUALS}{STRING_VALUE} {
419 BEGIN(SC_VALUES_COMMA_OR_BRACE);
422 <SC_VALUES_COMMA_OR_BRACE>[,\}] {
431 <SC_OPTION>summary{EQUALS} {
432 charpp = &CUROPT.summary;
433 yy_push_state(SC_UNQUOTED_LINE);
435 <SC_OPTION>typestr{EQUALS} {
436 charpp = &CUROPT.typestr;
437 yy_push_state(SC_UNQUOTED_LINE);
440 <SC_COMMAND>purpose{EQUALS} {
441 charpp = &CURCMD.purpose;
442 yy_push_state(SC_UNQUOTED_LINE);
445 <SC_COMMAND>non-opts-name{EQUALS} {
446 charpp = &CURCMD.non_opts_name;
447 yy_push_state(SC_UNQUOTED_LINE);
450 <SC_COMMAND>synopsis{EQUALS} {
451 charpp = &CURCMD.synopsis;
452 yy_push_state(SC_UNQUOTED_LINE);
455 <SC_COMMAND>aux_info{EQUALS} {
456 charpp = &CURCMD.aux_info;
457 yy_push_state(SC_UNQUOTED_LINE);
460 <SC_UNQUOTED_LINE>.*\n {
461 *charpp = parse_unquoted_line();
464 <SC_UNQUOTED_LINE_CHECK_DEFAULT>.*\n {
465 *charpp = parse_unquoted_line();
470 <SC_INTEGER>{INT_CONSTANT} *charpp = strdup(yytext); yy_pop_state();
472 /* This rule runs iff none of the above patterns matched */
474 fprintf(stderr, "parse error at line %d. Unmatched: \"%s\"\n",
475 yyget_lineno(), yytext);
479 fprintf(stderr, "subsequent unparsed input: %s\n", yytext);