kconfig: fix ambiguous grammar in terms of new lines
authorMasahiro Yamada <yamada.masahiro@socionext.com>
Tue, 11 Dec 2018 11:00:49 +0000 (20:00 +0900)
committerChristian Lamparter <chunkeey@gmail.com>
Sun, 10 Feb 2019 21:31:58 +0000 (22:31 +0100)
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>
Signed-off-by: Christian Lamparter <chunkeey@gmail.com>
config/zconf.l
config/zconf.y

index 323fb5524fe54f4c48839d7e238262b31a0a22b9..bbbef0c9072c7ff185a6146c83a60b3bff5e489f 100644 (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;
index 4aa5ad8dc874049a3ad8130d23078df8090f67d7..be5d68941b027f985d35a5dfaec006569a02feda 100644 (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; }
 ;