From: Francis De Brabandere <francisdb@gmail.com> Introduce StatementsBody, InlineStatements and InlineStatements_opt non-terminals whose FIRST sets do not overlap with StSep. This allows splitting ElseIf into multi-line (strict, requires statement separator) and inline (lenient, allows trailing End If) forms, and splitting Else_opt into separator and inline forms that naturally reject "Else End If" without a semantic check. The grammar now compiles with zero shift/reduce and zero reduce/reduce conflicts. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=55196 --- dlls/vbscript/parser.y | 26 +++++++++++++++--- dlls/vbscript/tests/lang.vbs | 53 ++++++++++++++++++++++++++++++++++++ dlls/vbscript/tests/run.c | 15 ++++++++++ 3 files changed, 90 insertions(+), 4 deletions(-) diff --git a/dlls/vbscript/parser.y b/dlls/vbscript/parser.y index c5d2764562e..f0ff3b552d4 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,30 @@ 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; } + | StSep StatementsBody { $$ = $2; } + | 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