kconfig: split the lexer out of zconf.y
[carl9170fw.git] / config / zconf.y
1 /* SPDX-License-Identifier: GPL-2.0 */
2 /*
3  * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
4  */
5 %{
6
7 #include <ctype.h>
8 #include <stdarg.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <stdbool.h>
13
14 #include "lkc.h"
15
16 #define printd(mask, fmt...) if (cdebug & (mask)) printf(fmt)
17
18 #define PRINTD          0x0001
19 #define DEBUG_PARSE     0x0002
20
21 int cdebug = PRINTD;
22
23 static void yyerror(const char *err);
24 static void zconfprint(const char *err, ...);
25 static void zconf_error(const char *err, ...);
26 static bool zconf_endtoken(const char *tokenname,
27                            const char *expected_tokenname);
28
29 struct symbol *symbol_hash[SYMBOL_HASHSIZE];
30
31 static struct menu *current_menu, *current_entry;
32
33 %}
34
35 %union
36 {
37         char *string;
38         struct file *file;
39         struct symbol *symbol;
40         struct expr *expr;
41         struct menu *menu;
42         enum symbol_type type;
43         enum variable_flavor flavor;
44 }
45
46 %token <string> T_HELPTEXT
47 %token <string> T_WORD
48 %token <string> T_WORD_QUOTE
49 %token T_ALLNOCONFIG_Y
50 %token T_BOOL
51 %token T_CHOICE
52 %token T_CLOSE_PAREN
53 %token T_COLON_EQUAL
54 %token T_COMMENT
55 %token T_CONFIG
56 %token T_DEFAULT
57 %token T_DEFCONFIG_LIST
58 %token T_DEF_BOOL
59 %token T_DEF_TRISTATE
60 %token T_DEPENDS
61 %token T_ENDCHOICE
62 %token T_ENDIF
63 %token T_ENDMENU
64 %token T_HELP
65 %token T_HEX
66 %token T_IF
67 %token T_IMPLY
68 %token T_INT
69 %token T_MAINMENU
70 %token T_MENU
71 %token T_MENUCONFIG
72 %token T_MODULES
73 %token T_ON
74 %token T_OPEN_PAREN
75 %token T_OPTION
76 %token T_OPTIONAL
77 %token T_PLUS_EQUAL
78 %token T_PROMPT
79 %token T_RANGE
80 %token T_SELECT
81 %token T_SOURCE
82 %token T_STRING
83 %token T_TRISTATE
84 %token T_VISIBLE
85 %token T_EOL
86 %token <string> T_ASSIGN_VAL
87
88 %left T_OR
89 %left T_AND
90 %left T_EQUAL T_UNEQUAL
91 %left T_LESS T_LESS_EQUAL T_GREATER T_GREATER_EQUAL
92 %nonassoc T_NOT
93
94 %type <string> prompt
95 %type <symbol> nonconst_symbol
96 %type <symbol> symbol
97 %type <type> type logic_type default
98 %type <expr> expr
99 %type <expr> if_expr
100 %type <string> end
101 %type <menu> if_entry menu_entry choice_entry
102 %type <string> word_opt assign_val
103 %type <flavor> assign_op
104
105 %destructor {
106         fprintf(stderr, "%s:%d: missing end statement for this entry\n",
107                 $$->file->name, $$->lineno);
108         if (current_menu == $$)
109                 menu_end_menu();
110 } if_entry menu_entry choice_entry
111
112 %%
113 input: mainmenu_stmt stmt_list | stmt_list;
114
115 /* mainmenu entry */
116
117 mainmenu_stmt: T_MAINMENU prompt T_EOL
118 {
119         menu_add_prompt(P_MENU, $2, NULL);
120 };
121
122 stmt_list:
123           /* empty */
124         | stmt_list common_stmt
125         | stmt_list choice_stmt
126         | stmt_list menu_stmt
127         | stmt_list T_WORD error T_EOL  { zconf_error("unknown statement \"%s\"", $2); }
128         | stmt_list error T_EOL         { zconf_error("invalid statement"); }
129 ;
130
131 common_stmt:
132           if_stmt
133         | comment_stmt
134         | config_stmt
135         | menuconfig_stmt
136         | source_stmt
137         | assignment_stmt
138 ;
139
140 /* config/menuconfig entry */
141
142 config_entry_start: T_CONFIG nonconst_symbol T_EOL
143 {
144         $2->flags |= SYMBOL_OPTIONAL;
145         menu_add_entry($2);
146         printd(DEBUG_PARSE, "%s:%d:config %s\n", zconf_curname(), zconf_lineno(), $2->name);
147 };
148
149 config_stmt: config_entry_start config_option_list
150 {
151         printd(DEBUG_PARSE, "%s:%d:endconfig\n", zconf_curname(), zconf_lineno());
152 };
153
154 menuconfig_entry_start: T_MENUCONFIG nonconst_symbol T_EOL
155 {
156         $2->flags |= SYMBOL_OPTIONAL;
157         menu_add_entry($2);
158         printd(DEBUG_PARSE, "%s:%d:menuconfig %s\n", zconf_curname(), zconf_lineno(), $2->name);
159 };
160
161 menuconfig_stmt: menuconfig_entry_start config_option_list
162 {
163         if (current_entry->prompt)
164                 current_entry->prompt->type = P_MENU;
165         else
166                 zconfprint("warning: menuconfig statement without prompt");
167         printd(DEBUG_PARSE, "%s:%d:endconfig\n", zconf_curname(), zconf_lineno());
168 };
169
170 config_option_list:
171           /* empty */
172         | config_option_list config_option
173         | config_option_list depends
174         | config_option_list help
175 ;
176
177 config_option: type prompt_stmt_opt T_EOL
178 {
179         menu_set_type($1);
180         printd(DEBUG_PARSE, "%s:%d:type(%u)\n",
181                 zconf_curname(), zconf_lineno(),
182                 $1);
183 };
184
185 config_option: T_PROMPT prompt if_expr T_EOL
186 {
187         menu_add_prompt(P_PROMPT, $2, $3);
188         printd(DEBUG_PARSE, "%s:%d:prompt\n", zconf_curname(), zconf_lineno());
189 };
190
191 config_option: default expr if_expr T_EOL
192 {
193         menu_add_expr(P_DEFAULT, $2, $3);
194         if ($1 != S_UNKNOWN)
195                 menu_set_type($1);
196         printd(DEBUG_PARSE, "%s:%d:default(%u)\n",
197                 zconf_curname(), zconf_lineno(),
198                 $1);
199 };
200
201 config_option: T_SELECT nonconst_symbol if_expr T_EOL
202 {
203         menu_add_symbol(P_SELECT, $2, $3);
204         printd(DEBUG_PARSE, "%s:%d:select\n", zconf_curname(), zconf_lineno());
205 };
206
207 config_option: T_IMPLY nonconst_symbol if_expr T_EOL
208 {
209         menu_add_symbol(P_IMPLY, $2, $3);
210         printd(DEBUG_PARSE, "%s:%d:imply\n", zconf_curname(), zconf_lineno());
211 };
212
213 config_option: T_RANGE symbol symbol if_expr T_EOL
214 {
215         menu_add_expr(P_RANGE, expr_alloc_comp(E_RANGE,$2, $3), $4);
216         printd(DEBUG_PARSE, "%s:%d:range\n", zconf_curname(), zconf_lineno());
217 };
218
219 config_option: T_OPTION T_MODULES T_EOL
220 {
221         menu_add_option_modules();
222 };
223
224 config_option: T_OPTION T_DEFCONFIG_LIST T_EOL
225 {
226         menu_add_option_defconfig_list();
227 };
228
229 config_option: T_OPTION T_ALLNOCONFIG_Y T_EOL
230 {
231         menu_add_option_allnoconfig_y();
232 };
233
234 /* choice entry */
235
236 choice: T_CHOICE word_opt T_EOL
237 {
238         struct symbol *sym = sym_lookup($2, SYMBOL_CHOICE);
239         sym->flags |= SYMBOL_NO_WRITE;
240         menu_add_entry(sym);
241         menu_add_expr(P_CHOICE, NULL, NULL);
242         free($2);
243         printd(DEBUG_PARSE, "%s:%d:choice\n", zconf_curname(), zconf_lineno());
244 };
245
246 choice_entry: choice choice_option_list
247 {
248         $$ = menu_add_menu();
249 };
250
251 choice_end: end
252 {
253         if (zconf_endtoken($1, "choice")) {
254                 menu_end_menu();
255                 printd(DEBUG_PARSE, "%s:%d:endchoice\n", zconf_curname(), zconf_lineno());
256         }
257 };
258
259 choice_stmt: choice_entry choice_block choice_end
260 ;
261
262 choice_option_list:
263           /* empty */
264         | choice_option_list choice_option
265         | choice_option_list depends
266         | choice_option_list help
267 ;
268
269 choice_option: T_PROMPT prompt if_expr T_EOL
270 {
271         menu_add_prompt(P_PROMPT, $2, $3);
272         printd(DEBUG_PARSE, "%s:%d:prompt\n", zconf_curname(), zconf_lineno());
273 };
274
275 choice_option: logic_type prompt_stmt_opt T_EOL
276 {
277         menu_set_type($1);
278         printd(DEBUG_PARSE, "%s:%d:type(%u)\n",
279                zconf_curname(), zconf_lineno(), $1);
280 };
281
282 choice_option: T_OPTIONAL T_EOL
283 {
284         current_entry->sym->flags |= SYMBOL_OPTIONAL;
285         printd(DEBUG_PARSE, "%s:%d:optional\n", zconf_curname(), zconf_lineno());
286 };
287
288 choice_option: T_DEFAULT nonconst_symbol if_expr T_EOL
289 {
290         menu_add_symbol(P_DEFAULT, $2, $3);
291         printd(DEBUG_PARSE, "%s:%d:default\n",
292                zconf_curname(), zconf_lineno());
293 };
294
295 type:
296           logic_type
297         | T_INT                 { $$ = S_INT; }
298         | T_HEX                 { $$ = S_HEX; }
299         | T_STRING              { $$ = S_STRING; }
300
301 logic_type:
302           T_BOOL                { $$ = S_BOOLEAN; }
303         | T_TRISTATE            { $$ = S_TRISTATE; }
304
305 default:
306           T_DEFAULT             { $$ = S_UNKNOWN; }
307         | T_DEF_BOOL            { $$ = S_BOOLEAN; }
308         | T_DEF_TRISTATE        { $$ = S_TRISTATE; }
309
310 choice_block:
311           /* empty */
312         | choice_block common_stmt
313 ;
314
315 /* if entry */
316
317 if_entry: T_IF expr T_EOL
318 {
319         printd(DEBUG_PARSE, "%s:%d:if\n", zconf_curname(), zconf_lineno());
320         menu_add_entry(NULL);
321         menu_add_dep($2);
322         $$ = menu_add_menu();
323 };
324
325 if_end: end
326 {
327         if (zconf_endtoken($1, "if")) {
328                 menu_end_menu();
329                 printd(DEBUG_PARSE, "%s:%d:endif\n", zconf_curname(), zconf_lineno());
330         }
331 };
332
333 if_stmt: if_entry stmt_list if_end
334 ;
335
336 /* menu entry */
337
338 menu: T_MENU prompt T_EOL
339 {
340         menu_add_entry(NULL);
341         menu_add_prompt(P_MENU, $2, NULL);
342         printd(DEBUG_PARSE, "%s:%d:menu\n", zconf_curname(), zconf_lineno());
343 };
344
345 menu_entry: menu menu_option_list
346 {
347         $$ = menu_add_menu();
348 };
349
350 menu_end: end
351 {
352         if (zconf_endtoken($1, "menu")) {
353                 menu_end_menu();
354                 printd(DEBUG_PARSE, "%s:%d:endmenu\n", zconf_curname(), zconf_lineno());
355         }
356 };
357
358 menu_stmt: menu_entry stmt_list menu_end
359 ;
360
361 menu_option_list:
362           /* empty */
363         | menu_option_list visible
364         | menu_option_list depends
365 ;
366
367 source_stmt: T_SOURCE prompt T_EOL
368 {
369         printd(DEBUG_PARSE, "%s:%d:source %s\n", zconf_curname(), zconf_lineno(), $2);
370         zconf_nextfile($2);
371         free($2);
372 };
373
374 /* comment entry */
375
376 comment: T_COMMENT prompt T_EOL
377 {
378         menu_add_entry(NULL);
379         menu_add_prompt(P_COMMENT, $2, NULL);
380         printd(DEBUG_PARSE, "%s:%d:comment\n", zconf_curname(), zconf_lineno());
381 };
382
383 comment_stmt: comment comment_option_list
384 ;
385
386 comment_option_list:
387           /* empty */
388         | comment_option_list depends
389 ;
390
391 /* help option */
392
393 help_start: T_HELP T_EOL
394 {
395         printd(DEBUG_PARSE, "%s:%d:help\n", zconf_curname(), zconf_lineno());
396         zconf_starthelp();
397 };
398
399 help: help_start T_HELPTEXT
400 {
401         if (current_entry->help) {
402                 free(current_entry->help);
403                 zconfprint("warning: '%s' defined with more than one help text -- only the last one will be used",
404                            current_entry->sym->name ?: "<choice>");
405         }
406
407         /* Is the help text empty or all whitespace? */
408         if ($2[strspn($2, " \f\n\r\t\v")] == '\0')
409                 zconfprint("warning: '%s' defined with blank help text",
410                            current_entry->sym->name ?: "<choice>");
411
412         current_entry->help = $2;
413 };
414
415 /* depends option */
416
417 depends: T_DEPENDS T_ON expr T_EOL
418 {
419         menu_add_dep($3);
420         printd(DEBUG_PARSE, "%s:%d:depends on\n", zconf_curname(), zconf_lineno());
421 };
422
423 /* visibility option */
424 visible: T_VISIBLE if_expr T_EOL
425 {
426         menu_add_visibility($2);
427 };
428
429 /* prompt statement */
430
431 prompt_stmt_opt:
432           /* empty */
433         | prompt if_expr
434 {
435         menu_add_prompt(P_PROMPT, $1, $2);
436 };
437
438 prompt:   T_WORD
439         | T_WORD_QUOTE
440 ;
441
442 end:      T_ENDMENU T_EOL       { $$ = "menu"; }
443         | T_ENDCHOICE T_EOL     { $$ = "choice"; }
444         | T_ENDIF T_EOL         { $$ = "if"; }
445 ;
446
447 if_expr:  /* empty */                   { $$ = NULL; }
448         | T_IF expr                     { $$ = $2; }
449 ;
450
451 expr:     symbol                                { $$ = expr_alloc_symbol($1); }
452         | symbol T_LESS symbol                  { $$ = expr_alloc_comp(E_LTH, $1, $3); }
453         | symbol T_LESS_EQUAL symbol            { $$ = expr_alloc_comp(E_LEQ, $1, $3); }
454         | symbol T_GREATER symbol               { $$ = expr_alloc_comp(E_GTH, $1, $3); }
455         | symbol T_GREATER_EQUAL symbol         { $$ = expr_alloc_comp(E_GEQ, $1, $3); }
456         | symbol T_EQUAL symbol                 { $$ = expr_alloc_comp(E_EQUAL, $1, $3); }
457         | symbol T_UNEQUAL symbol               { $$ = expr_alloc_comp(E_UNEQUAL, $1, $3); }
458         | T_OPEN_PAREN expr T_CLOSE_PAREN       { $$ = $2; }
459         | T_NOT expr                            { $$ = expr_alloc_one(E_NOT, $2); }
460         | expr T_OR expr                        { $$ = expr_alloc_two(E_OR, $1, $3); }
461         | expr T_AND expr                       { $$ = expr_alloc_two(E_AND, $1, $3); }
462 ;
463
464 /* For symbol definitions, selects, etc., where quotes are not accepted */
465 nonconst_symbol: T_WORD { $$ = sym_lookup($1, 0); free($1); };
466
467 symbol:   nonconst_symbol
468         | T_WORD_QUOTE  { $$ = sym_lookup($1, SYMBOL_CONST); free($1); }
469 ;
470
471 word_opt: /* empty */                   { $$ = NULL; }
472         | T_WORD
473
474 /* assignment statement */
475
476 assignment_stmt:  T_WORD assign_op assign_val T_EOL     { variable_add($1, $3, $2); free($1); free($3); }
477
478 assign_op:
479           T_EQUAL       { $$ = VAR_RECURSIVE; }
480         | T_COLON_EQUAL { $$ = VAR_SIMPLE; }
481         | T_PLUS_EQUAL  { $$ = VAR_APPEND; }
482 ;
483
484 assign_val:
485         /* empty */             { $$ = xstrdup(""); };
486         | T_ASSIGN_VAL
487 ;
488
489 %%
490
491 void conf_parse(const char *name)
492 {
493         struct symbol *sym;
494         int i;
495
496         zconf_initscan(name);
497
498         _menu_init();
499
500         if (getenv("ZCONF_DEBUG"))
501                 yydebug = 1;
502         yyparse();
503
504         /* Variables are expanded in the parse phase. We can free them here. */
505         variable_all_del();
506
507         if (yynerrs)
508                 exit(1);
509         if (!modules_sym)
510                 modules_sym = sym_find( "n" );
511
512         if (!menu_has_prompt(&rootmenu)) {
513                 current_entry = &rootmenu;
514                 menu_add_prompt(P_MENU, "Main menu", NULL);
515         }
516
517         menu_finalize(&rootmenu);
518         for_all_symbols(i, sym) {
519                 if (sym_check_deps(sym))
520                         yynerrs++;
521         }
522         if (yynerrs)
523                 exit(1);
524         sym_set_change_count(1);
525 }
526
527 static bool zconf_endtoken(const char *tokenname,
528                            const char *expected_tokenname)
529 {
530         if (strcmp(tokenname, expected_tokenname)) {
531                 zconf_error("unexpected '%s' within %s block",
532                             tokenname, expected_tokenname);
533                 yynerrs++;
534                 return false;
535         }
536         if (current_menu->file != current_file) {
537                 zconf_error("'%s' in different file than '%s'",
538                             tokenname, expected_tokenname);
539                 fprintf(stderr, "%s:%d: location of the '%s'\n",
540                         current_menu->file->name, current_menu->lineno,
541                         expected_tokenname);
542                 yynerrs++;
543                 return false;
544         }
545         return true;
546 }
547
548 static void zconfprint(const char *err, ...)
549 {
550         va_list ap;
551
552         fprintf(stderr, "%s:%d: ", zconf_curname(), zconf_lineno());
553         va_start(ap, err);
554         vfprintf(stderr, err, ap);
555         va_end(ap);
556         fprintf(stderr, "\n");
557 }
558
559 static void zconf_error(const char *err, ...)
560 {
561         va_list ap;
562
563         yynerrs++;
564         fprintf(stderr, "%s:%d: ", zconf_curname(), zconf_lineno());
565         va_start(ap, err);
566         vfprintf(stderr, err, ap);
567         va_end(ap);
568         fprintf(stderr, "\n");
569 }
570
571 static void yyerror(const char *err)
572 {
573         fprintf(stderr, "%s:%d: %s\n", zconf_curname(), zconf_lineno() + 1, err);
574 }
575
576 static void print_quoted_string(FILE *out, const char *str)
577 {
578         const char *p;
579         int len;
580
581         putc('"', out);
582         while ((p = strchr(str, '"'))) {
583                 len = p - str;
584                 if (len)
585                         fprintf(out, "%.*s", len, str);
586                 fputs("\\\"", out);
587                 str = p + 1;
588         }
589         fputs(str, out);
590         putc('"', out);
591 }
592
593 static void print_symbol(FILE *out, struct menu *menu)
594 {
595         struct symbol *sym = menu->sym;
596         struct property *prop;
597
598         if (sym_is_choice(sym))
599                 fprintf(out, "\nchoice\n");
600         else
601                 fprintf(out, "\nconfig %s\n", sym->name);
602         switch (sym->type) {
603         case S_BOOLEAN:
604                 fputs("  bool\n", out);
605                 break;
606         case S_TRISTATE:
607                 fputs("  tristate\n", out);
608                 break;
609         case S_STRING:
610                 fputs("  string\n", out);
611                 break;
612         case S_INT:
613                 fputs("  integer\n", out);
614                 break;
615         case S_HEX:
616                 fputs("  hex\n", out);
617                 break;
618         default:
619                 fputs("  ???\n", out);
620                 break;
621         }
622         for (prop = sym->prop; prop; prop = prop->next) {
623                 if (prop->menu != menu)
624                         continue;
625                 switch (prop->type) {
626                 case P_PROMPT:
627                         fputs("  prompt ", out);
628                         print_quoted_string(out, prop->text);
629                         if (!expr_is_yes(prop->visible.expr)) {
630                                 fputs(" if ", out);
631                                 expr_fprint(prop->visible.expr, out);
632                         }
633                         fputc('\n', out);
634                         break;
635                 case P_DEFAULT:
636                         fputs( "  default ", out);
637                         expr_fprint(prop->expr, out);
638                         if (!expr_is_yes(prop->visible.expr)) {
639                                 fputs(" if ", out);
640                                 expr_fprint(prop->visible.expr, out);
641                         }
642                         fputc('\n', out);
643                         break;
644                 case P_CHOICE:
645                         fputs("  #choice value\n", out);
646                         break;
647                 case P_SELECT:
648                         fputs( "  select ", out);
649                         expr_fprint(prop->expr, out);
650                         fputc('\n', out);
651                         break;
652                 case P_IMPLY:
653                         fputs( "  imply ", out);
654                         expr_fprint(prop->expr, out);
655                         fputc('\n', out);
656                         break;
657                 case P_RANGE:
658                         fputs( "  range ", out);
659                         expr_fprint(prop->expr, out);
660                         fputc('\n', out);
661                         break;
662                 case P_MENU:
663                         fputs( "  menu ", out);
664                         print_quoted_string(out, prop->text);
665                         fputc('\n', out);
666                         break;
667                 case P_SYMBOL:
668                         fputs( "  symbol ", out);
669                         fprintf(out, "%s\n", prop->sym->name);
670                         break;
671                 default:
672                         fprintf(out, "  unknown prop %d!\n", prop->type);
673                         break;
674                 }
675         }
676         if (menu->help) {
677                 int len = strlen(menu->help);
678                 while (menu->help[--len] == '\n')
679                         menu->help[len] = 0;
680                 fprintf(out, "  help\n%s\n", menu->help);
681         }
682 }
683
684 void zconfdump(FILE *out)
685 {
686         struct property *prop;
687         struct symbol *sym;
688         struct menu *menu;
689
690         menu = rootmenu.list;
691         while (menu) {
692                 if ((sym = menu->sym))
693                         print_symbol(out, menu);
694                 else if ((prop = menu->prompt)) {
695                         switch (prop->type) {
696                         case P_COMMENT:
697                                 fputs("\ncomment ", out);
698                                 print_quoted_string(out, prop->text);
699                                 fputs("\n", out);
700                                 break;
701                         case P_MENU:
702                                 fputs("\nmenu ", out);
703                                 print_quoted_string(out, prop->text);
704                                 fputs("\n", out);
705                                 break;
706                         default:
707                                 ;
708                         }
709                         if (!expr_is_yes(prop->visible.expr)) {
710                                 fputs("  depends ", out);
711                                 expr_fprint(prop->visible.expr, out);
712                                 fputc('\n', out);
713                         }
714                 }
715
716                 if (menu->list)
717                         menu = menu->list;
718                 else if (menu->next)
719                         menu = menu->next;
720                 else while ((menu = menu->parent)) {
721                         if (menu->prompt && menu->prompt->type == P_MENU)
722                                 fputs("\nendmenu\n", out);
723                         if (menu->next) {
724                                 menu = menu->next;
725                                 break;
726                         }
727                 }
728         }
729 }
730
731 #include "util.c"
732 #include "menu.c"