To: vim_dev@googlegroups.com Subject: Patch 9.0.1150 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 9.0.1150 Problem: :interface is not implemented yet. Solution: Implement the basics of :interface. Files: src/ex_cmds.h, src/ex_docmd.c, src/vim9class.c, src/proto/vim9class.pro, src/errors.h, src/vim.h, src/userfunc.c, src/proto/userfunc.pro, src/vim9compile.c, src/vim9execute.c, src/testdir/test_vim9_class.vim *** ../vim-9.0.1149/src/ex_cmds.h 2022-12-04 20:11:12.791828025 +0000 --- src/ex_cmds.h 2023-01-05 15:14:46.898026429 +0000 *************** *** 756,762 **** EXCMD(CMD_intro, "intro", ex_intro, EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK, ADDR_NONE), ! EXCMD(CMD_interface, "interface", ex_interface, EX_EXTRA|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK, ADDR_NONE), EXCMD(CMD_isearch, "isearch", ex_findpat, --- 756,762 ---- EXCMD(CMD_intro, "intro", ex_intro, EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK, ADDR_NONE), ! EXCMD(CMD_interface, "interface", ex_class, EX_EXTRA|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK, ADDR_NONE), EXCMD(CMD_isearch, "isearch", ex_findpat, *** ../vim-9.0.1149/src/ex_docmd.c 2023-01-02 16:54:48.932860868 +0000 --- src/ex_docmd.c 2023-01-05 15:12:03.626228517 +0000 *************** *** 288,294 **** # define ex_execute ex_ni # define ex_finally ex_ni # define ex_incdec ex_ni - # define ex_interface ex_ni # define ex_finish ex_ni # define ex_function ex_ni # define ex_if ex_ni --- 288,293 ---- *** ../vim-9.0.1149/src/vim9class.c 2023-01-02 21:03:59.900493426 +0000 --- src/vim9class.c 2023-01-05 18:31:17.896153189 +0000 *************** *** 26,32 **** * Returns OK or FAIL. When OK then "varname_end" is set to just after the * variable name and "type_ret" is set to the decleared or detected type. * "init_expr" is set to the initialisation expression (allocated), if there is ! * one. */ static int parse_member( --- 26,32 ---- * Returns OK or FAIL. When OK then "varname_end" is set to just after the * variable name and "type_ret" is set to the decleared or detected type. * "init_expr" is set to the initialisation expression (allocated), if there is ! * one. For an interface "init_expr" is NULL. */ static int parse_member( *************** *** 119,125 **** --- 119,132 ---- *type_ret = type; if (expr_end > expr_start) + { + if (init_expr == NULL) + { + emsg(_(e_cannot_initialize_member_in_interface)); + return FAIL; + } *init_expr = vim_strnsave(expr_start, expr_end - expr_start); + } return OK; } *************** *** 175,189 **** /* * Handle ":class" and ":abstract class" up to ":endclass". */ void ex_class(exarg_T *eap) { if (!current_script_is_vim9() || (cmdmod.cmod_flags & CMOD_LEGACY) || !getline_equal(eap->getline, eap->cookie, getsourceline)) { ! emsg(_(e_class_can_only_be_defined_in_vim9_script)); return; } --- 182,202 ---- /* * Handle ":class" and ":abstract class" up to ":endclass". + * Handle ":interface" up to ":endinterface". */ void ex_class(exarg_T *eap) { + int is_class = eap->cmdidx == CMD_class; // FALSE for :interface + if (!current_script_is_vim9() || (cmdmod.cmod_flags & CMOD_LEGACY) || !getline_equal(eap->getline, eap->cookie, getsourceline)) { ! if (is_class) ! emsg(_(e_class_can_only_be_defined_in_vim9_script)); ! else ! emsg(_(e_interface_can_only_be_defined_in_vim9_script)); return; } *************** *** 201,213 **** if (!ASCII_ISUPPER(*arg)) { ! semsg(_(e_class_name_must_start_with_uppercase_letter_str), arg); return; } char_u *name_end = find_name_end(arg, NULL, NULL, FNE_CHECK_START); if (!IS_WHITE_OR_NUL(*name_end)) { ! semsg(_(e_white_space_required_after_class_name_str), arg); return; } --- 214,230 ---- if (!ASCII_ISUPPER(*arg)) { ! if (is_class) ! semsg(_(e_class_name_must_start_with_uppercase_letter_str), arg); ! else ! semsg(_(e_interface_name_must_start_with_uppercase_letter_str), ! arg); return; } char_u *name_end = find_name_end(arg, NULL, NULL, FNE_CHECK_START); if (!IS_WHITE_OR_NUL(*name_end)) { ! semsg(_(e_white_space_required_after_name_str), arg); return; } *************** *** 239,245 **** ga_init2(&objmethods, sizeof(ufunc_T *), 10); /* ! * Go over the body of the class until "endclass" is found. */ char_u *theline = NULL; int success = FALSE; --- 256,263 ---- ga_init2(&objmethods, sizeof(ufunc_T *), 10); /* ! * Go over the body of the class/interface until "endclass" or ! * "endinterface" is found. */ char_u *theline = NULL; int success = FALSE; *************** *** 262,270 **** } char_u *p = line; ! if (checkforcmd(&p, "endclass", 4)) { ! if (STRNCMP(line, "endclass", 8) != 0) semsg(_(e_command_cannot_be_shortened_str), line); else if (*p == '|' || !ends_excmd2(line, p)) semsg(_(e_trailing_characters_str), p); --- 280,289 ---- } char_u *p = line; ! char *end_name = is_class ? "endclass" : "endinterface"; ! if (checkforcmd(&p, end_name, is_class ? 4 : 5)) { ! if (STRNCMP(line, end_name, is_class ? 8 : 12) != 0) semsg(_(e_command_cannot_be_shortened_str), line); else if (*p == '|' || !ends_excmd2(line, p)) semsg(_(e_trailing_characters_str), p); *************** *** 272,277 **** --- 291,302 ---- success = TRUE; break; } + char *wrong_name = is_class ? "endinterface" : "endclass"; + if (checkforcmd(&p, wrong_name, is_class ? 5 : 4)) + { + semsg(_(e_invalid_command_str), line); + break; + } int has_public = FALSE; if (checkforcmd(&p, "public", 3)) *************** *** 320,326 **** type_T *type = NULL; char_u *init_expr = NULL; if (parse_member(eap, line, varname, has_public, ! &varname_end, &type_list, &type, &init_expr) == FAIL) break; if (add_member(&objmembers, varname, varname_end, has_public, type, init_expr) == FAIL) --- 345,352 ---- type_T *type = NULL; char_u *init_expr = NULL; if (parse_member(eap, line, varname, has_public, ! &varname_end, &type_list, &type, ! is_class ? &init_expr: NULL) == FAIL) break; if (add_member(&objmembers, varname, varname_end, has_public, type, init_expr) == FAIL) *************** *** 358,364 **** ea.cookie = eap->cookie; ga_init2(&lines_to_free, sizeof(char_u *), 50); ! ufunc_T *uf = define_function(&ea, NULL, &lines_to_free, TRUE); ga_clear_strings(&lines_to_free); if (uf != NULL) --- 384,391 ---- ea.cookie = eap->cookie; ga_init2(&lines_to_free, sizeof(char_u *), 50); ! ufunc_T *uf = define_function(&ea, NULL, &lines_to_free, ! is_class ? CF_CLASS : CF_INTERFACE); ga_clear_strings(&lines_to_free); if (uf != NULL) *************** *** 389,395 **** type_T *type = NULL; char_u *init_expr = NULL; if (parse_member(eap, line, varname, has_public, ! &varname_end, &type_list, &type, &init_expr) == FAIL) break; if (add_member(&classmembers, varname, varname_end, has_public, type, init_expr) == FAIL) --- 416,423 ---- type_T *type = NULL; char_u *init_expr = NULL; if (parse_member(eap, line, varname, has_public, ! &varname_end, &type_list, &type, ! is_class ? &init_expr : NULL) == FAIL) break; if (add_member(&classmembers, varname, varname_end, has_public, type, init_expr) == FAIL) *************** *** 401,407 **** else { ! semsg(_(e_not_valid_command_in_class_str), line); break; } } --- 429,438 ---- else { ! if (is_class) ! semsg(_(e_not_valid_command_in_class_str), line); ! else ! semsg(_(e_not_valid_command_in_interface_str), line); break; } } *************** *** 415,420 **** --- 446,454 ---- cl = ALLOC_CLEAR_ONE(class_T); if (cl == NULL) goto cleanup; + if (!is_class) + cl->class_flags = CLASS_INTERFACE; + cl->class_refcount = 1; cl->class_name = vim_strnsave(arg, name_end - arg); if (cl->class_name == NULL) *************** *** 429,435 **** &cl->class_obj_member_count) == FAIL) goto cleanup; ! if (cl->class_class_member_count > 0) { // Allocate a typval for each class member and initialize it. cl->class_members_tv = ALLOC_CLEAR_MULT(typval_T, --- 463,469 ---- &cl->class_obj_member_count) == FAIL) goto cleanup; ! if (is_class && cl->class_class_member_count > 0) { // Allocate a typval for each class member and initialize it. cl->class_members_tv = ALLOC_CLEAR_MULT(typval_T, *************** *** 491,497 **** garray_T lines_to_free; ga_init2(&lines_to_free, sizeof(char_u *), 50); ! ufunc_T *nf = define_function(&fea, NULL, &lines_to_free, TRUE); ga_clear_strings(&lines_to_free); vim_free(fga.ga_data); --- 525,532 ---- garray_T lines_to_free; ga_init2(&lines_to_free, sizeof(char_u *), 50); ! ufunc_T *nf = define_function(&fea, NULL, &lines_to_free, ! is_class ? CF_CLASS : CF_INTERFACE); ga_clear_strings(&lines_to_free); vim_free(fga.ga_data); *************** *** 634,648 **** } /* - * Handle ":interface" up to ":endinterface". - */ - void - ex_interface(exarg_T *eap UNUSED) - { - // TODO - } - - /* * Handle ":enum" up to ":endenum". */ void --- 669,674 ---- *** ../vim-9.0.1149/src/proto/vim9class.pro 2023-01-01 19:53:26.582445815 +0000 --- src/proto/vim9class.pro 2023-01-05 15:15:14.373993702 +0000 *************** *** 1,7 **** /* vim9class.c */ void ex_class(exarg_T *eap); type_T *class_member_type(class_T *cl, char_u *name, char_u *name_end, int *member_idx); - void ex_interface(exarg_T *eap); void ex_enum(exarg_T *eap); void ex_type(exarg_T *eap); int class_object_index(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int verbose); --- 1,6 ---- *** ../vim-9.0.1149/src/errors.h 2023-01-02 18:10:00.015271225 +0000 --- src/errors.h 2023-01-05 19:32:27.942723658 +0000 *************** *** 3346,3353 **** #ifdef FEAT_EVAL EXTERN char e_class_name_must_start_with_uppercase_letter_str[] INIT(= N_("E1314: Class name must start with an uppercase letter: %s")); ! EXTERN char e_white_space_required_after_class_name_str[] ! INIT(= N_("E1315: White space required after class name: %s")); EXTERN char e_class_can_only_be_defined_in_vim9_script[] INIT(= N_("E1316: Class can only be defined in Vim9 script")); EXTERN char e_invalid_object_member_declaration_str[] --- 3346,3353 ---- #ifdef FEAT_EVAL EXTERN char e_class_name_must_start_with_uppercase_letter_str[] INIT(= N_("E1314: Class name must start with an uppercase letter: %s")); ! EXTERN char e_white_space_required_after_name_str[] ! INIT(= N_("E1315: White space required after name: %s")); EXTERN char e_class_can_only_be_defined_in_vim9_script[] INIT(= N_("E1316: Class can only be defined in Vim9 script")); EXTERN char e_invalid_object_member_declaration_str[] *************** *** 3406,3409 **** --- 3406,3417 ---- INIT(= N_("E1340: Argument already declared in the class: %s")); EXTERN char e_variable_already_declared_in_class_str[] INIT(= N_("E1341: Variable already declared in the class: %s")); + EXTERN char e_interface_can_only_be_defined_in_vim9_script[] + INIT(= N_("E1342: Interface can only be defined in Vim9 script")); + EXTERN char e_interface_name_must_start_with_uppercase_letter_str[] + INIT(= N_("E1343: Interface name must start with an uppercase letter: %s")); + EXTERN char e_cannot_initialize_member_in_interface[] + INIT(= N_("E1344: Cannot initialize a member in an interface")); + EXTERN char e_not_valid_command_in_interface_str[] + INIT(= N_("E1345: Not a valid command in an interface: %s")); #endif *** ../vim-9.0.1149/src/vim.h 2022-12-09 21:41:43.908327271 +0000 --- src/vim.h 2023-01-05 15:35:43.760309581 +0000 *************** *** 2854,2857 **** --- 2854,2861 ---- #define MAX_LSHIFT_BITS (varnumber_T)((sizeof(uvarnumber_T) * 8) - 1) + // Flags used by "class_flags" of define_function() + #define CF_CLASS 1 // inside a class + #define CF_INTERFACE 2 // inside an interface + #endif // VIM__H *** ../vim-9.0.1149/src/userfunc.c 2023-01-04 13:16:16.555907760 +0000 --- src/userfunc.c 2023-01-05 15:36:49.500376818 +0000 *************** *** 215,221 **** garray_T *default_args, int skip, exarg_T *eap, // can be NULL ! int in_class, // TRUE when inside a class garray_T *newlines, // function body lines garray_T *lines_to_free) { --- 215,221 ---- garray_T *default_args, int skip, exarg_T *eap, // can be NULL ! int in_class, // non-zero when inside a class or interface garray_T *newlines, // function body lines garray_T *lines_to_free) { *************** *** 4462,4468 **** * When "name_arg" is not NULL this is a nested function, using "name_arg" for * the function name. * "lines_to_free" is a list of strings to be freed later. ! * If "in_class" is TRUE then the function is defined inside a class. * Returns a pointer to the function or NULL if no function defined. */ ufunc_T * --- 4462,4470 ---- * When "name_arg" is not NULL this is a nested function, using "name_arg" for * the function name. * "lines_to_free" is a list of strings to be freed later. ! * If "class_flags" has CF_CLASS then the function is defined inside a class. ! * With CF_INTERFACE the function is define inside an interface, only the ! * ":def"/":function" line is expected, no function body. * Returns a pointer to the function or NULL if no function defined. */ ufunc_T * *************** *** 4470,4476 **** exarg_T *eap, char_u *name_arg, garray_T *lines_to_free, ! int in_class) { int j; int c; --- 4472,4478 ---- exarg_T *eap, char_u *name_arg, garray_T *lines_to_free, ! int class_flags) { int j; int c; *************** *** 4545,4551 **** /* * Get the function name. There are these situations: ! * func normal function name, also when "in_class" is TRUE * "name" == func, "fudi.fd_dict" == NULL * dict.func new dictionary entry * "name" == NULL, "fudi.fd_dict" set, --- 4547,4553 ---- /* * Get the function name. There are these situations: ! * func normal function name, also when "class_flags" is non-zero * "name" == func, "fudi.fd_dict" == NULL * dict.func new dictionary entry * "name" == NULL, "fudi.fd_dict" set, *************** *** 4586,4592 **** } int tfn_flags = TFN_NO_AUTOLOAD | TFN_NEW_FUNC ! | (in_class ? TFN_IN_CLASS : 0); name = save_function_name(&p, &is_global, eap->skip, tfn_flags, &fudi); paren = (vim_strchr(p, '(') != NULL); if (name == NULL && (fudi.fd_dict == NULL || !paren) && !eap->skip) --- 4588,4594 ---- } int tfn_flags = TFN_NO_AUTOLOAD | TFN_NEW_FUNC ! | (class_flags != 0 ? TFN_IN_CLASS : 0); name = save_function_name(&p, &is_global, eap->skip, tfn_flags, &fudi); paren = (vim_strchr(p, '(') != NULL); if (name == NULL && (fudi.fd_dict == NULL || !paren) && !eap->skip) *************** *** 4789,4795 **** if (get_function_args(&p, ')', &newargs, eap->cmdidx == CMD_def ? &argtypes : NULL, FALSE, NULL, &varargs, &default_args, eap->skip, ! eap, in_class, &newlines, lines_to_free) == FAIL) goto errret_2; whitep = p; --- 4791,4797 ---- if (get_function_args(&p, ')', &newargs, eap->cmdidx == CMD_def ? &argtypes : NULL, FALSE, NULL, &varargs, &default_args, eap->skip, ! eap, class_flags, &newlines, lines_to_free) == FAIL) goto errret_2; whitep = p; *************** *** 4899,4905 **** // Do not define the function when getting the body fails and when // skipping. ! if (get_function_body(eap, &newlines, line_arg, lines_to_free) == FAIL || eap->skip) goto erret; --- 4901,4909 ---- // Do not define the function when getting the body fails and when // skipping. ! if (((class_flags & CF_INTERFACE) == 0 ! && get_function_body(eap, &newlines, line_arg, lines_to_free) ! == FAIL) || eap->skip) goto erret; *************** *** 4934,4940 **** if (name == NULL) goto erret; } ! else if (!in_class) { hashtab_T *ht; char_u *find_name = name; --- 4938,4944 ---- if (name == NULL) goto erret; } ! else if (class_flags == 0) { hashtab_T *ht; char_u *find_name = name; *************** *** 5159,5165 **** hi = hash_find(&func_hashtab, name); hi->hi_key = UF2HIKEY(fp); } ! else if (!in_class && hash_add(&func_hashtab, UF2HIKEY(fp), "add function") == FAIL) { free_fp = TRUE; --- 5163,5169 ---- hi = hash_find(&func_hashtab, name); hi->hi_key = UF2HIKEY(fp); } ! else if (class_flags == 0 && hash_add(&func_hashtab, UF2HIKEY(fp), "add function") == FAIL) { free_fp = TRUE; *************** *** 5251,5257 **** garray_T lines_to_free; ga_init2(&lines_to_free, sizeof(char_u *), 50); ! (void)define_function(eap, NULL, &lines_to_free, FALSE); ga_clear_strings(&lines_to_free); } --- 5255,5261 ---- garray_T lines_to_free; ga_init2(&lines_to_free, sizeof(char_u *), 50); ! (void)define_function(eap, NULL, &lines_to_free, 0); ga_clear_strings(&lines_to_free); } *** ../vim-9.0.1149/src/proto/userfunc.pro 2022-12-09 21:41:43.908327271 +0000 --- src/proto/userfunc.pro 2023-01-05 15:36:56.676383590 +0000 *************** *** 46,52 **** char_u *alloc_printable_func_name(char_u *fname); char_u *save_function_name(char_u **name, int *is_global, int skip, int flags, funcdict_T *fudi); void list_functions(regmatch_T *regmatch); ! ufunc_T *define_function(exarg_T *eap, char_u *name_arg, garray_T *lines_to_free, int in_class); void ex_function(exarg_T *eap); ufunc_T *find_func_by_name(char_u *name, compiletype_T *compile_type); void ex_defcompile(exarg_T *eap); --- 46,52 ---- char_u *alloc_printable_func_name(char_u *fname); char_u *save_function_name(char_u **name, int *is_global, int skip, int flags, funcdict_T *fudi); void list_functions(regmatch_T *regmatch); ! ufunc_T *define_function(exarg_T *eap, char_u *name_arg, garray_T *lines_to_free, int class_flags); void ex_function(exarg_T *eap); ufunc_T *find_func_by_name(char_u *name, compiletype_T *compile_type); void ex_defcompile(exarg_T *eap); *** ../vim-9.0.1149/src/vim9compile.c 2023-01-03 19:08:46.005020599 +0000 --- src/vim9compile.c 2023-01-05 19:32:15.026773197 +0000 *************** *** 991,997 **** int save_KeyTyped = KeyTyped; KeyTyped = FALSE; ! ufunc = define_function(eap, lambda_name, lines_to_free, FALSE); KeyTyped = save_KeyTyped; --- 991,997 ---- int save_KeyTyped = KeyTyped; KeyTyped = FALSE; ! ufunc = define_function(eap, lambda_name, lines_to_free, 0); KeyTyped = save_KeyTyped; *** ../vim-9.0.1149/src/vim9execute.c 2023-01-03 19:08:46.005020599 +0000 --- src/vim9execute.c 2023-01-05 15:35:26.336290068 +0000 *************** *** 4280,4286 **** CLEAR_FIELD(ea); ea.cmd = ea.arg = iptr->isn_arg.string; ga_init2(&lines_to_free, sizeof(char_u *), 50); ! define_function(&ea, NULL, &lines_to_free, FALSE); ga_clear_strings(&lines_to_free); } break; --- 4280,4286 ---- CLEAR_FIELD(ea); ea.cmd = ea.arg = iptr->isn_arg.string; ga_init2(&lines_to_free, sizeof(char_u *), 50); ! define_function(&ea, NULL, &lines_to_free, 0); ga_clear_strings(&lines_to_free); } break; *** ../vim-9.0.1149/src/testdir/test_vim9_class.vim 2023-01-05 13:16:00.304020639 +0000 --- src/testdir/test_vim9_class.vim 2023-01-05 19:33:13.118555970 +0000 *************** *** 552,556 **** --- 552,616 ---- v9.CheckScriptSuccess(lines) enddef + def Test_interface_basics() + var lines =<< trim END + vim9script + interface Something + this.value: string + static count: number + def GetCount(): number + endinterface + END + v9.CheckScriptSuccess(lines) + + lines =<< trim END + interface SomethingWrong + static count = 7 + endinterface + END + v9.CheckScriptFailure(lines, 'E1342:') + + lines =<< trim END + vim9script + + interface Some + static count: number + def Method(count: number) + endinterface + END + # TODO: this should give an error for "count" shadowing + v9.CheckScriptSuccess(lines) + + lines =<< trim END + vim9script + interface somethingWrong + static count = 7 + endinterface + END + v9.CheckScriptFailure(lines, 'E1343: Interface name must start with an uppercase letter: somethingWrong') + + lines =<< trim END + vim9script + interface SomethingWrong + this.value: string + static count = 7 + def GetCount(): number + endinterface + END + v9.CheckScriptFailure(lines, 'E1344:') + + lines =<< trim END + vim9script + interface SomethingWrong + this.value: string + static count: number + def GetCount(): number + return 5 + enddef + endinterface + END + v9.CheckScriptFailure(lines, 'E1345: Not a valid command in an interface: return 5') + enddef + " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker *** ../vim-9.0.1149/src/version.c 2023-01-05 13:16:00.304020639 +0000 --- src/version.c 2023-01-05 18:20:08.591743051 +0000 *************** *** 697,698 **** --- 697,700 ---- { /* Add new patch number below this line */ + /**/ + 1150, /**/ -- "I've been teaching myself to play the piano for about 5 years and now write most of my songs on it, mainly because I can never find any paper." Jeff Lynne, ELO's greatest hits /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// \\\ \\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///