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