[PATCH 0/2] MR10897: vbscript: Allow Class declarations inline after Dim/Sub separated by ':'.
ClassDeclaration was only allowed at the top SourceElement level, so any prior statement on the same line caused err 1002 instead of either parsing successfully or, for a name collision, err 1041. Treat ClassDeclaration as a SimpleStatement matching the FunctionDecl pattern: it produces a STAT_CLASS statement that registers the class with the parser at compile time and errors out if the declaration is not at script global scope. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10897
From: Francis De Brabandere <francisdb@gmail.com> --- dlls/vbscript/tests/lang.vbs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/dlls/vbscript/tests/lang.vbs b/dlls/vbscript/tests/lang.vbs index 1b6cdf763e4..b7ea4855ed4 100644 --- a/dlls/vbscript/tests/lang.vbs +++ b/dlls/vbscript/tests/lang.vbs @@ -3986,6 +3986,23 @@ ExecuteGlobal "ReDim egDynArr(1) : egDynArr(0) = ""a"" : egDynArr(1) = ""b""" Call ok(egDynArr(0) = "a", "ExecuteGlobal ReDim egDynArr(0) = " & egDynArr(0)) Call ok(egDynArr(1) = "b", "ExecuteGlobal ReDim egDynArr(1) = " & egDynArr(1)) +' Class declaration inline after a Dim/Sub statement separated by ':' +Sub TestClassInlineAfterDim + on error resume next + Err.Clear + ExecuteGlobal "Dim egInlineA : Class EGInlineClassA : End Class" + todo_wine_ok Err.Number = 0, "ExecuteGlobal Dim:Class err=" & Err.Number + + Err.Clear + ExecuteGlobal "Sub EGInlineSub() : End Sub : Class EGInlineClassB : End Class" + todo_wine_ok Err.Number = 0, "ExecuteGlobal Sub:Class err=" & Err.Number + + Err.Clear + ExecuteGlobal "Dim EGInlineCollision : Class EGInlineCollision : End Class" + todo_wine_ok Err.Number = 1041, "ExecuteGlobal Dim x : Class x err=" & Err.Number +End Sub +Call TestClassInlineAfterDim + ' Execute tests x = 0 Execute "x = 99" -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10897
From: Francis De Brabandere <francisdb@gmail.com> ClassDeclaration was only allowed at the top SourceElement level, so any prior statement on the same line caused err 1002 instead of either parsing successfully or, for a name collision, err 1041. Treat ClassDeclaration as a SimpleStatement matching the FunctionDecl pattern: it produces a STAT_CLASS statement that registers the class with the parser at compile time and errors out if the declaration is not at script global scope. --- dlls/vbscript/compile.c | 15 +++++++++++++++ dlls/vbscript/parse.h | 6 ++++++ dlls/vbscript/parser.y | 24 +++++++++++++++--------- dlls/vbscript/tests/lang.vbs | 6 +++--- 4 files changed, 39 insertions(+), 12 deletions(-) diff --git a/dlls/vbscript/compile.c b/dlls/vbscript/compile.c index 0b3fbaf97d9..50d0c10ad32 100644 --- a/dlls/vbscript/compile.c +++ b/dlls/vbscript/compile.c @@ -1440,6 +1440,18 @@ static HRESULT compile_function_statement(compile_ctx_t *ctx, function_statement return S_OK; } +static HRESULT compile_class_statement(compile_ctx_t *ctx, class_statement_t *stat) +{ + if(ctx->func != &ctx->code->main_code) { + FIXME("Class is not in the global code\n"); + return E_FAIL; + } + + stat->class_decl->next = ctx->parser.class_decls; + ctx->parser.class_decls = stat->class_decl; + return S_OK; +} + static HRESULT compile_exitdo_statement(compile_ctx_t *ctx) { statement_ctx_t *iter; @@ -1711,6 +1723,9 @@ static HRESULT compile_statement(compile_ctx_t *ctx, statement_ctx_t *stat_ctx, case STAT_FUNC: hres = compile_function_statement(ctx, (function_statement_t*)stat); break; + case STAT_CLASS: + hres = compile_class_statement(ctx, (class_statement_t*)stat); + break; case STAT_IF: hres = compile_if_statement(ctx, (if_statement_t*)stat); break; diff --git a/dlls/vbscript/parse.h b/dlls/vbscript/parse.h index 4febe0ca535..66451c1cec0 100644 --- a/dlls/vbscript/parse.h +++ b/dlls/vbscript/parse.h @@ -112,6 +112,7 @@ typedef struct { typedef enum { STAT_ASSIGN, STAT_CALL, + STAT_CLASS, STAT_CONST, STAT_DIM, STAT_DOUNTIL, @@ -223,6 +224,11 @@ typedef struct _class_decl_t { struct _class_decl_t *next; } class_decl_t; +typedef struct { + statement_t stat; + class_decl_t *class_decl; +} class_statement_t; + typedef struct _elseif_decl_t { expression_t *expr; statement_t *stat; diff --git a/dlls/vbscript/parser.y b/dlls/vbscript/parser.y index ec67cde2215..5edd1942913 100644 --- a/dlls/vbscript/parser.y +++ b/dlls/vbscript/parser.y @@ -30,7 +30,6 @@ static int parser_error(unsigned*,parser_ctx_t*,const char*); static void handle_isexpression_script(parser_ctx_t *ctx, expression_t *expr); static void source_add_statement(parser_ctx_t*,statement_t*); -static void source_add_class(parser_ctx_t*,class_decl_t*); static void *new_expression(parser_ctx_t*,expression_type_t,size_t); static expression_t *new_bool_expression(parser_ctx_t*,VARIANT_BOOL); @@ -58,6 +57,7 @@ static statement_t *new_forto_statement(parser_ctx_t*,unsigned,const WCHAR*,expr static statement_t *new_foreach_statement(parser_ctx_t*,unsigned,const WCHAR*,expression_t*,statement_t*); static statement_t *new_if_statement(parser_ctx_t*,unsigned,expression_t*,statement_t*,elseif_decl_t*,statement_t*); static statement_t *new_function_statement(parser_ctx_t*,unsigned,function_decl_t*); +static statement_t *new_class_statement(parser_ctx_t*,unsigned,class_decl_t*); static statement_t *new_onerror_statement(parser_ctx_t*,unsigned,BOOL); static statement_t *new_const_statement(parser_ctx_t*,unsigned,const_decl_t*); static statement_t *new_select_statement(parser_ctx_t*,unsigned,expression_t*,case_clausule_t*); @@ -186,7 +186,6 @@ SourceElements | SourceElements GlobalDimDeclaration StSep { source_add_statement(ctx, $2); } | SourceElements StatementNl { source_add_statement(ctx, $2); } - | SourceElements ClassDeclaration { source_add_class(ctx, $2); } GlobalDimDeclaration : tPRIVATE tCONST ConstDeclList { $$ = new_const_statement(ctx, @$, $3); CHECK_ERROR; } @@ -267,6 +266,7 @@ SimpleStatement | tDO StSep StatementsNl_opt error { ctx->hres = MAKE_VBSERROR(VBSE_EXPECTED_LOOP); YYABORT; } | tDO error { ctx->hres = MAKE_VBSERROR(VBSE_EXPECTED_WHILE_UNTIL_EOS); YYABORT; } | FunctionDecl { $$ = new_function_statement(ctx, @$, $1); CHECK_ERROR; } + | ClassDeclaration { $$ = new_class_statement(ctx, @$, $1); CHECK_ERROR; } | tEXIT tDO { $$ = new_statement(ctx, STAT_EXITDO, 0, @2); CHECK_ERROR; } | tEXIT tFOR { $$ = new_statement(ctx, STAT_EXITFOR, 0, @2); CHECK_ERROR; } | tEXIT tFUNCTION { $$ = new_statement(ctx, STAT_EXITFUNC, 0, @2); CHECK_ERROR; } @@ -574,7 +574,7 @@ PrimaryExpression | tME { $$ = new_expression(ctx, EXPR_ME, 0); CHECK_ERROR; } ClassDeclaration - : tCLASS Identifier StSep ClassBody tEND tCLASS StSep { $4->name = $2; $4->loc = @2; $$ = $4; } + : tCLASS Identifier StSep ClassBody tEND tCLASS { $4->name = $2; $4->loc = @2; $$ = $4; } | tCLASS Identifier tEND tCLASS { ctx->error_loc = @3; ctx->hres = MAKE_VBSERROR(VBSE_EXPECTED_STATEMENT); YYABORT; } | tCLASS Identifier StSep ClassBody tEND error { ctx->hres = MAKE_VBSERROR(VBSE_EXPECTED_CLASS); YYABORT; } @@ -731,12 +731,6 @@ static void source_add_statement(parser_ctx_t *ctx, statement_t *stat) } } -static void source_add_class(parser_ctx_t *ctx, class_decl_t *class_decl) -{ - class_decl->next = ctx->class_decls; - ctx->class_decls = class_decl; -} - static void handle_isexpression_script(parser_ctx_t *ctx, expression_t *expr) { retval_statement_t *stat; @@ -1262,6 +1256,18 @@ static statement_t *new_function_statement(parser_ctx_t *ctx, unsigned loc, func return &stat->stat; } +static statement_t *new_class_statement(parser_ctx_t *ctx, unsigned loc, class_decl_t *decl) +{ + class_statement_t *stat; + + stat = new_statement(ctx, STAT_CLASS, sizeof(*stat), loc); + if(!stat) + return NULL; + + stat->class_decl = decl; + return &stat->stat; +} + static class_decl_t *new_class_decl(parser_ctx_t *ctx) { class_decl_t *class_decl; diff --git a/dlls/vbscript/tests/lang.vbs b/dlls/vbscript/tests/lang.vbs index b7ea4855ed4..373eac1cc66 100644 --- a/dlls/vbscript/tests/lang.vbs +++ b/dlls/vbscript/tests/lang.vbs @@ -3991,15 +3991,15 @@ Sub TestClassInlineAfterDim on error resume next Err.Clear ExecuteGlobal "Dim egInlineA : Class EGInlineClassA : End Class" - todo_wine_ok Err.Number = 0, "ExecuteGlobal Dim:Class err=" & Err.Number + call ok(Err.Number = 0, "ExecuteGlobal Dim:Class err=" & Err.Number) Err.Clear ExecuteGlobal "Sub EGInlineSub() : End Sub : Class EGInlineClassB : End Class" - todo_wine_ok Err.Number = 0, "ExecuteGlobal Sub:Class err=" & Err.Number + call ok(Err.Number = 0, "ExecuteGlobal Sub:Class err=" & Err.Number) Err.Clear ExecuteGlobal "Dim EGInlineCollision : Class EGInlineCollision : End Class" - todo_wine_ok Err.Number = 1041, "ExecuteGlobal Dim x : Class x err=" & Err.Number + call ok(Err.Number = 1041, "ExecuteGlobal Dim x : Class x err=" & Err.Number) End Sub Call TestClassInlineAfterDim -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10897
participants (2)
-
Francis De Brabandere -
Francis De Brabandere (@francisdb)