kconfig: fix ambiguous grammar in terms of new lines

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.

Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
This commit is contained in:
Masahiro Yamada 2018-12-11 20:00:49 +09:00
parent 21c5ecf604
commit cc66bca775
2 changed files with 24 additions and 15 deletions

View File

@ -16,6 +16,8 @@
#include "lkc.h"
#define YY_DECL static int yylex1(void)
#define START_STRSIZE 16
static struct {
@ -23,6 +25,7 @@ static struct {
int lineno;
} current_pos;
static int prev_token = T_EOL;
static char *text;
static int text_size, text_asize;
@ -268,6 +271,24 @@ n [A-Za-z0-9_-]
}
%%
/* second stage lexer */
int yylex(void)
{
int token;
repeat:
token = yylex1();
/* Do not pass unneeded T_EOL to the parser. */
if ((prev_token == T_EOL || prev_token == T_HELPTEXT) && token == T_EOL)
goto repeat;
prev_token = token;
return token;
}
static char *expand_token(const char *in, size_t n)
{
char *out;

View File

@ -31,7 +31,7 @@ struct symbol *symbol_hash[SYMBOL_HASHSIZE];
static struct menu *current_menu, *current_entry;
%}
%expect 29
%expect 21
%union
{
@ -111,9 +111,7 @@ static struct menu *current_menu, *current_entry;
%}
%%
input: nl start | start;
start: mainmenu_stmt stmt_list | stmt_list;
input: mainmenu_stmt stmt_list | stmt_list;
/* mainmenu entry */
@ -141,8 +139,7 @@ option_name:
;
common_stmt:
T_EOL
| if_stmt
if_stmt
| comment_stmt
| config_stmt
| menuconfig_stmt
@ -193,7 +190,6 @@ config_option_list:
| config_option_list depends
| config_option_list help
| config_option_list option_error
| config_option_list T_EOL
;
config_option: T_TYPE prompt_stmt_opt T_EOL
@ -293,7 +289,6 @@ choice_option_list:
| choice_option_list choice_option
| choice_option_list depends
| choice_option_list help
| choice_option_list T_EOL
| choice_option_list option_error
;
@ -443,7 +438,6 @@ help: help_start T_HELPTEXT
depends_list:
/* empty */
| depends_list depends
| depends_list T_EOL
| depends_list option_error
;
@ -458,7 +452,6 @@ depends: T_DEPENDS T_ON expr T_EOL
visibility_list:
/* empty */
| visibility_list visible
| visibility_list T_EOL
;
visible: T_VISIBLE if_expr T_EOL
@ -484,11 +477,6 @@ end: T_ENDMENU T_EOL { $$ = $1; }
| T_ENDIF T_EOL { $$ = $1; }
;
nl:
T_EOL
| nl T_EOL
;
if_expr: /* empty */ { $$ = NULL; }
| T_IF expr { $$ = $2; }
;