[PATCH v5 0/1] MR10333: Draft: vbscript: Refactor ElseIf and Else grammar rules to avoid reduce/reduce conflicts.
Split ElseIf into multi-line (requires statement separator after Then) and inline (allows trailing End If) forms, and split Else into separator and inline forms so that "Else End If" without a body is naturally rejected by the grammar instead of requiring a semantic check. This is achieved by introducing StatementsBody, InlineStatements and InlineStatements_opt non-terminals whose FIRST sets do not overlap with StSep, replacing the previous StSep_opt usage that caused reduce/reduce conflicts. No new parser conflicts are introduced. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=55196 -- v5: vbscript: Fix ElseIf and Else handling for inline and multi-line forms. https://gitlab.winehq.org/wine/wine/-/merge_requests/10333
From: Francis De Brabandere <francisdb@gmail.com> Split ElseIf into multi-line (requires statement separator after Then) and inline (allows trailing End If) forms, and split Else into separator and inline forms so that "Else End If" without a body is naturally rejected by the grammar instead of requiring a semantic check. This is achieved by introducing StatementsBody, InlineStatements and InlineStatements_opt non-terminals whose FIRST sets do not overlap with StSep, replacing the previous StSep_opt usage that caused reduce/reduce conflicts. No new parser conflicts are introduced. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=55196 --- dlls/vbscript/parser.y | 25 ++++++++++++++--- dlls/vbscript/tests/lang.vbs | 53 ++++++++++++++++++++++++++++++++++++ dlls/vbscript/tests/run.c | 15 ++++++++++ 3 files changed, 89 insertions(+), 4 deletions(-) diff --git a/dlls/vbscript/parser.y b/dlls/vbscript/parser.y index c5d2764562e..99663eac831 100644 --- a/dlls/vbscript/parser.y +++ b/dlls/vbscript/parser.y @@ -137,7 +137,7 @@ static statement_t *link_statements(statement_t*,statement_t*); %token <date> tDate %type <statement> Statement SimpleStatement StatementNl StatementsNl StatementsNl_opt BodyStatements IfStatement Else_opt -%type <statement> GlobalDimDeclaration +%type <statement> GlobalDimDeclaration StatementsBody InlineStatements InlineStatements_opt %type <expression> Expression LiteralExpression PrimaryExpression EqualityExpression CallExpression ExpressionNl_opt %type <expression> ConcatExpression AdditiveExpression ModExpression IntdivExpression MultiplicativeExpression ExpExpression %type <expression> NotExpression UnaryExpression AndExpression OrExpression XorExpression EqvExpression SignExpression @@ -325,12 +325,29 @@ ElseIfs | ElseIf ElseIfs { $1->next = $2; $$ = $1; } ElseIf - : tELSEIF Expression tTHEN StSep_opt StatementsNl_opt - { $$ = new_elseif_decl(ctx, @$, $2, $5); } + : tELSEIF Expression tTHEN StSep StatementsNl_opt + { $$ = new_elseif_decl(ctx, @$, $2, $5); } + | tELSEIF Expression tTHEN InlineStatements_opt + { $$ = new_elseif_decl(ctx, @$, $2, $4); } Else_opt : /* empty */ { $$ = NULL; } - | tELSE StSep_opt StatementsNl_opt { $$ = $3; } + | tELSE StSep StatementsBody { $$ = $3; } + | tELSE InlineStatements { $$ = $2; } + +StatementsBody + : /* empty */ { $$ = NULL; } + | SimpleStatement { $$ = $1; } + | SimpleStatement StSep StatementsBody { $1->next = $3; $$ = $1; } + +InlineStatements_opt + : /* empty */ { $$ = NULL; } + | SimpleStatement { $$ = $1; } + | SimpleStatement StSep StatementsBody { $1->next = $3; $$ = $1; } + +InlineStatements + : SimpleStatement { $$ = $1; } + | SimpleStatement StSep StatementsBody { $1->next = $3; $$ = $1; } CaseClausules : /* empty */ { $$ = NULL; } diff --git a/dlls/vbscript/tests/lang.vbs b/dlls/vbscript/tests/lang.vbs index 12d91a16548..5b4144fca09 100644 --- a/dlls/vbscript/tests/lang.vbs +++ b/dlls/vbscript/tests/lang.vbs @@ -359,6 +359,59 @@ ElseIf not False Then End If Call ok(x, "elseif not called?") +' Else with trailing End If on same line +x = 1 +If false Then + x = 2 +Else x = 3 End If +Call ok(x = 3, "Else with trailing End If failed") + +' Else with colon separator before End If +x = 1 +If false Then + x = 2 +Else x = 3:End If +Call ok(x = 3, "Else with colon before End If failed") + +' Else with colon then statement with trailing End If +x = 1 +If false Then + x = 2 +Else:x = 3 End If +Call ok(x = 3, "Else colon then statement with trailing End If failed") + +' Else with multiple statements and trailing End If +x = 1 +y = 1 +If false Then + x = 2 +Else x = 3:y = 4 End If +Call ok(x = 3, "Else multi-statement trailing End If failed for x") +Call ok(y = 4, "Else multi-statement trailing End If failed for y") + +' Else with body statement on next line with trailing End If +x = 1 +If false Then + x = 2 +Else + x = 3 End If +Call ok(x = 3, "Else body statement with trailing End If failed") + +' ElseIf single-line with trailing End If +x = 1 +If false Then + x = 2 +ElseIf true Then x = 3 End If +Call ok(x = 3, "ElseIf single-line with trailing End If failed") + +' Empty Else block with newline +x = 1 +If false Then + x = 2 +Else +End If +Call ok(x = 1, "empty Else block should be accepted") + x = false If true Then :x = true diff --git a/dlls/vbscript/tests/run.c b/dlls/vbscript/tests/run.c index 8aaafbedf54..8cc8a82aa8d 100644 --- a/dlls/vbscript/tests/run.c +++ b/dlls/vbscript/tests/run.c @@ -2656,6 +2656,21 @@ static void test_parse_errors(void) " x = 0 End If\n", 1, 10 }, + { + /* ElseIf...End If */ + L"If False Then\n" + " x = 0\n" + "ElseIf True Then\n" + " x = 1 End If\n", + 3, 10 + }, + { + /* Else End If (no separator) */ + L"If False Then\n" + " x = 0\n" + "Else End If\n", + 2, 5 + }, { /* While...End While */ L"While False\n" -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10333
Jacek Caban (@jacek) commented about dlls/vbscript/parser.y:
- { $$ = new_elseif_decl(ctx, @$, $2, $5); } + : tELSEIF Expression tTHEN StSep StatementsNl_opt + { $$ = new_elseif_decl(ctx, @$, $2, $5); } + | tELSEIF Expression tTHEN InlineStatements_opt + { $$ = new_elseif_decl(ctx, @$, $2, $4); }
Else_opt : /* empty */ { $$ = NULL; } - | tELSE StSep_opt StatementsNl_opt { $$ = $3; } + | tELSE StSep StatementsBody { $$ = $3; } + | tELSE InlineStatements { $$ = $2; } + +StatementsBody + : /* empty */ { $$ = NULL; } + | SimpleStatement { $$ = $1; } + | SimpleStatement StSep StatementsBody { $1->next = $3; $$ = $1; } It is identical to `InlineStatements_opt` below, let's not duplicate it.
Also, please keep bracket indentation consistent within a rule. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10333#note_132569
participants (3)
-
Francis De Brabandere -
Francis De Brabandere (@francisdb) -
Jacek Caban (@jacek)