This commit decreases 8 shift/reduce conflicts. A certain amount of grammatical ambiguity comes from how to reduce excessive T_EOL tokens. Let's take a look at the example code below: 1 config A 2 bool "a" 3 4 depends on B 5 6 config B 7 def_bool y The line 3 is melt into "config_option_list", but the line 5 can be either a part of "config_option_list" or "common_stmt" by itself. Currently, the lexer converts '\n' to T_EOL verbatim. In Kconfig, a new line works as a statement terminator, but new lines in empty lines are not critical since empty lines (or lines that contain only whitespaces/comments) are just no-op. If the lexer simply discards no-op lines, the parser will not be bothered by excessive T_EOL tokens. Of course, this means we are shifting the complexity from the parser to the lexer, but it is much easier than tackling on shift/reduce conflicts. I introduced the second stage lexer to tweak the behavior. Discard T_EOL if the previous token is T_EOL or T_HELPTEXT. Two T_EOL tokens in a row is meaningless. T_HELPTEXT is a special token that is reduced without T_EOL.
Unified · Split
| @@ -16,6 +16,8 @@ · 15 unmodified lines | |||
| 16 | 16 | ||
| 17 | #include "lkc.h" | 17 | #include "lkc.h" |
| 18 | 18 | ||
| 19 | #define YY_DECL static int yylex1(void) | ||
| 20 | |||
| 19 | #define START_STRSIZE 16 | 21 | #define START_STRSIZE 16 |
| 20 | 22 | ||
| 21 | static struct { | 23 | static struct { |
| @@ -23,6 +25,7 @@ static struct { · 1 unmodified line | |||
| 23 | int lineno; | 25 | int lineno; |
| 24 | } current_pos; | 26 | } current_pos; |
| 25 | 27 | ||
| 28 | static int prev_token = T_EOL; | ||
| 26 | static char *text; | 29 | static char *text; |
| 27 | static int text_size, text_asize; | 30 | static int text_size, text_asize; |
| 28 | 31 | ||
| @@ -268,6 +271,24 @@ n [A-Za-z0-9_-] · 239 unmodified lines | |||
| 268 | } | 271 | } |
| 269 | 272 | ||
| 270 | %% | 273 | %% |
| 274 | |||
| 275 | /* second stage lexer */ | ||
| 276 | int yylex(void) | ||
| 277 | { | ||
| 278 | int token; | ||
| 279 | |||
| 280 | repeat: | ||
| 281 | token = yylex1(); | ||
| 282 | |||
| 283 | /* Do not pass unneeded T_EOL to the parser. */ | ||
| 284 | if ((prev_token == T_EOL || prev_token == T_HELPTEXT) && token == T_EOL) | ||
| 285 | goto repeat; | ||
| 286 | |||
| 287 | prev_token = token; | ||
| 288 | |||
| 289 | return token; | ||
| 290 | } | ||
| 291 | |||
| 271 | static char *expand_token(const char *in, size_t n) | 292 | static char *expand_token(const char *in, size_t n) |
| 272 | { | 293 | { |
| 273 | char *out; | 294 | char *out; |
| @@ -31,7 +31,7 @@ struct symbol *symbol_hash[SYMBOL_HASHSIZE]; · 30 unmodified lines | |||
| 31 | static struct menu *current_menu, *current_entry; | 31 | static struct menu *current_menu, *current_entry; |
| 32 | 32 | ||
| 33 | %} | 33 | %} |
| 34 | %expect 29 | 34 | %expect 21 |
| 35 | 35 | ||
| 36 | %union | 36 | %union |
| 37 | { | 37 | { |
| @@ -111,9 +111,7 @@ static struct menu *current_menu, *current_entry; · 73 unmodified lines | |||
| 111 | %} | 111 | %} |
| 112 | 112 | ||
| 113 | %% | 113 | %% |
| 114 | input: nl start | start; | 114 | input: mainmenu_stmt stmt_list | stmt_list; |
| 115 | |||
| 116 | start: mainmenu_stmt stmt_list | stmt_list; | ||
| 117 | 115 | ||
| 118 | /* mainmenu entry */ | 116 | /* mainmenu entry */ |
| 119 | 117 | ||
| @@ -141,8 +139,7 @@ option_name: · 21 unmodified lines | |||
| 141 | ; | 139 | ; |
| 142 | 140 | ||
| 143 | common_stmt: | 141 | common_stmt: |
| 144 | T_EOL | 142 | if_stmt |
| 145 | | if_stmt | ||
| 146 | | comment_stmt | 143 | | comment_stmt |
| 147 | | config_stmt | 144 | | config_stmt |
| 148 | | menuconfig_stmt | 145 | | menuconfig_stmt |
| @@ -193,7 +190,6 @@ config_option_list: · 44 unmodified lines | |||
| 193 | | config_option_list depends | 190 | | config_option_list depends |
| 194 | | config_option_list help | 191 | | config_option_list help |
| 195 | | config_option_list option_error | 192 | | config_option_list option_error |
| 196 | | config_option_list T_EOL | ||
| 197 | ; | 193 | ; |
| 198 | 194 | ||
| 199 | config_option: T_TYPE prompt_stmt_opt T_EOL | 195 | config_option: T_TYPE prompt_stmt_opt T_EOL |
| @@ -293,7 +289,6 @@ choice_option_list: · 93 unmodified lines | |||
| 293 | | choice_option_list choice_option | 289 | | choice_option_list choice_option |
| 294 | | choice_option_list depends | 290 | | choice_option_list depends |
| 295 | | choice_option_list help | 291 | | choice_option_list help |
| 296 | | choice_option_list T_EOL | ||
| 297 | | choice_option_list option_error | 292 | | choice_option_list option_error |
| 298 | ; | 293 | ; |
| 299 | 294 | ||
| @@ -443,7 +438,6 @@ help: help_start T_HELPTEXT · 143 unmodified lines | |||
| 443 | depends_list: | 438 | depends_list: |
| 444 | /* empty */ | 439 | /* empty */ |
| 445 | | depends_list depends | 440 | | depends_list depends |
| 446 | | depends_list T_EOL | ||
| 447 | | depends_list option_error | 441 | | depends_list option_error |
| 448 | ; | 442 | ; |
| 449 | 443 | ||
| @@ -458,7 +452,6 @@ depends: T_DEPENDS T_ON expr T_EOL · 8 unmodified lines | |||
| 458 | visibility_list: | 452 | visibility_list: |
| 459 | /* empty */ | 453 | /* empty */ |
| 460 | | visibility_list visible | 454 | | visibility_list visible |
| 461 | | visibility_list T_EOL | ||
| 462 | ; | 455 | ; |
| 463 | 456 | ||
| 464 | visible: T_VISIBLE if_expr T_EOL | 457 | visible: T_VISIBLE if_expr T_EOL |
| @@ -484,11 +477,6 @@ end: T_ENDMENU T_EOL { $$ = $1; } · 19 unmodified lines | |||
| 484 | | T_ENDIF T_EOL { $$ = $1; } | 477 | | T_ENDIF T_EOL { $$ = $1; } |
| 485 | ; | 478 | ; |
| 486 | 479 | ||
| 487 | nl: | ||
| 488 | T_EOL | ||
| 489 | | nl T_EOL | ||
| 490 | ; | ||
| 491 | |||
| 492 | if_expr: /* empty */ { $$ = NULL; } | 480 | if_expr: /* empty */ { $$ = NULL; } |
| 493 | | T_IF expr { $$ = $2; } | 481 | | T_IF expr { $$ = $2; } |
| 494 | ; | 482 | ; |