To: vim_dev@googlegroups.com Subject: Patch 9.0.1123 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 9.0.1123 Problem: Class function not implemented yet. Solution: Implement defining and calling a class function. Files: src/vim9class.c, src/proto/vim9class.pro, src/structs.h, src/vim9expr.c, src/testdir/test_vim9_class.vim *** ../vim-9.0.1122/src/vim9class.c 2022-12-23 17:56:21.405511534 +0000 --- src/vim9class.c 2023-01-01 12:29:03.529703540 +0000 *************** *** 223,231 **** garray_T classmembers; ga_init2(&classmembers, sizeof(ocmember_T), 10); ! // Growarray with object methods declared in the class. ! garray_T classmethods; ! ga_init2(&classmethods, sizeof(ufunc_T *), 10); // Growarray with object members declared in the class. garray_T objmembers; --- 223,231 ---- garray_T classmembers; ga_init2(&classmembers, sizeof(ocmember_T), 10); ! // Growarray with functions declared in the class. ! garray_T classfunctions; ! ga_init2(&classfunctions, sizeof(ufunc_T *), 10); // Growarray with object members declared in the class. garray_T objmembers; *************** *** 288,293 **** --- 288,306 ---- } } + int has_static = FALSE; + char_u *ps = p; + if (checkforcmd(&p, "static", 4)) + { + if (STRNCMP(ps, "static", 6) != 0) + { + semsg(_(e_command_cannot_be_shortened_str), ps); + break; + } + has_static = TRUE; + p = skipwhite(ps + 6); + } + // object members (public, read access, private): // "this._varname" // "this.varname" *************** *** 314,360 **** } } - // class members and methods - else if (checkforcmd(&p, "static", 6)) - { - p = skipwhite(p); - if (checkforcmd(&p, "def", 3)) - { - // TODO: class method - // static def someMethod() - // enddef - // static def someMethod() - // enddef - } - else - { - // class members (public, read access, private): - // "static _varname" - // "static varname" - // "public static varname" - char_u *varname = p; - char_u *varname_end = NULL; - 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) - { - vim_free(init_expr); - break; - } - } - } - // constructors: // def new() // enddef // def newOther() // enddef ! // methods: ! // def someMethod() // enddef // TODO: // def someMethod() --- 327,341 ---- } } // constructors: // def new() // enddef // def newOther() // enddef ! // object methods and class functions: ! // def SomeMethod() ! // enddef ! // static def ClassFunction() // enddef // TODO: // def someMethod() *************** *** 364,369 **** --- 345,352 ---- exarg_T ea; garray_T lines_to_free; + // TODO: error for "public static def Func()"? + CLEAR_FIELD(ea); ea.cmd = line; ea.arg = p; *************** *** 376,388 **** ga_clear_strings(&lines_to_free); // TODO: how about errors? ! if (uf != NULL && ga_grow(&objmethods, 1) == OK) { ! if (STRNCMP(uf->uf_name, "new", 3) == 0) uf->uf_flags |= FC_NEW; ! ((ufunc_T **)objmethods.ga_data)[objmethods.ga_len] = uf; ! ++objmethods.ga_len; } } --- 359,396 ---- ga_clear_strings(&lines_to_free); // TODO: how about errors? ! int is_new = STRNCMP(uf->uf_name, "new", 3) == 0; ! garray_T *fgap = has_static || is_new ! ? &classfunctions : &objmethods; ! if (uf != NULL && ga_grow(fgap, 1) == OK) { ! if (is_new) uf->uf_flags |= FC_NEW; ! ((ufunc_T **)fgap->ga_data)[fgap->ga_len] = uf; ! ++fgap->ga_len; ! } ! } ! ! // class members ! else if (has_static) ! { ! // class members (public, read access, private): ! // "static _varname" ! // "static varname" ! // "public static varname" ! char_u *varname = p; ! char_u *varname_end = NULL; ! 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) ! { ! vim_free(init_expr); ! break; } } *************** *** 445,452 **** } int have_new = FALSE; ! for (int i = 0; i < objmethods.ga_len; ++i) ! if (STRCMP(((ufunc_T **)objmethods.ga_data)[i]->uf_name, "new") == 0) { have_new = TRUE; --- 453,460 ---- } int have_new = FALSE; ! for (int i = 0; i < classfunctions.ga_len; ++i) ! if (STRCMP(((ufunc_T **)classfunctions.ga_data)[i]->uf_name, "new") == 0) { have_new = TRUE; *************** *** 483,492 **** ga_clear_strings(&lines_to_free); vim_free(fga.ga_data); ! if (nf != NULL && ga_grow(&objmethods, 1) == OK) { ! ((ufunc_T **)objmethods.ga_data)[objmethods.ga_len] = nf; ! ++objmethods.ga_len; nf->uf_flags |= FC_NEW; nf->uf_ret_type = get_type_ptr(&type_list); --- 491,500 ---- ga_clear_strings(&lines_to_free); vim_free(fga.ga_data); ! if (nf != NULL && ga_grow(&classfunctions, 1) == OK) { ! ((ufunc_T **)classfunctions.ga_data)[classfunctions.ga_len] = nf; ! ++classfunctions.ga_len; nf->uf_flags |= FC_NEW; nf->uf_ret_type = get_type_ptr(&type_list); *************** *** 500,520 **** } } ! // TODO: class methods ! cl->class_obj_method_count = objmethods.ga_len; ! cl->class_obj_methods = ALLOC_MULT(ufunc_T *, objmethods.ga_len); ! if (cl->class_obj_methods == NULL) ! goto cleanup; ! mch_memmove(cl->class_obj_methods, objmethods.ga_data, ! sizeof(ufunc_T *) * objmethods.ga_len); ! vim_free(objmethods.ga_data); ! ! // Set the class pointer on all the object methods. ! for (int i = 0; i < objmethods.ga_len; ++i) ! { ! ufunc_T *fp = cl->class_obj_methods[i]; ! fp->uf_class = cl; ! fp->uf_flags |= FC_OBJECT; // TODO: not for class method } cl->class_type.tt_type = VAR_CLASS; --- 508,542 ---- } } ! // loop 1: class functions, loop 2: object methods ! for (int loop = 1; loop <= 2; ++loop) ! { ! garray_T *gap = loop == 1 ? &classfunctions : &objmethods; ! int *fcount = loop == 1 ? &cl->class_class_function_count ! : &cl->class_obj_method_count; ! ufunc_T ***fup = loop == 1 ? &cl->class_class_functions ! : &cl->class_obj_methods; ! ! *fcount = gap->ga_len; ! if (gap->ga_len == 0) ! { ! *fup = NULL; ! continue; ! } ! *fup = ALLOC_MULT(ufunc_T *, gap->ga_len); ! if (*fup == NULL) ! goto cleanup; ! mch_memmove(*fup, gap->ga_data, sizeof(ufunc_T *) * gap->ga_len); ! vim_free(gap->ga_data); ! ! // Set the class pointer on all the object methods. ! for (int i = 0; i < gap->ga_len; ++i) ! { ! ufunc_T *fp = (*fup)[i]; ! fp->uf_class = cl; ! if (loop == 2) ! fp->uf_flags |= FC_OBJECT; ! } } cl->class_type.tt_type = VAR_CLASS; *************** *** 539,544 **** --- 561,567 ---- if (cl != NULL) { vim_free(cl->class_name); + vim_free(cl->class_class_functions); vim_free(cl->class_obj_members); vim_free(cl->class_obj_methods); vim_free(cl); *************** *** 565,570 **** --- 588,601 ---- func_clear_free(uf, FALSE); } ga_clear(&objmethods); + + for (int i = 0; i < classfunctions.ga_len; ++i) + { + ufunc_T *uf = ((ufunc_T **)classfunctions.ga_data)[i]; + func_clear_free(uf, FALSE); + } + ga_clear(&classfunctions); + clear_type_list(&type_list); } *************** *** 627,633 **** /* * Evaluate what comes after a class: * - class member: SomeClass.varname ! * - class method: SomeClass.SomeMethod() * - class constructor: SomeClass.new() * - object member: someObject.varname * - object method: someObject.SomeMethod() --- 658,664 ---- /* * Evaluate what comes after a class: * - class member: SomeClass.varname ! * - class function: SomeClass.SomeMethod() * - class constructor: SomeClass.new() * - object member: someObject.varname * - object method: someObject.SomeMethod() *************** *** 664,672 **** : rettv->vval.v_object->obj_class; if (*name_end == '(') { ! for (int i = 0; i < cl->class_obj_method_count; ++i) { ! ufunc_T *fp = cl->class_obj_methods[i]; // Use a separate pointer to avoid that ASAN complains about // uf_name[] only being 4 characters. char_u *ufname = (char_u *)fp->uf_name; --- 695,707 ---- : rettv->vval.v_object->obj_class; if (*name_end == '(') { ! int on_class = rettv->v_type == VAR_CLASS; ! int count = on_class ? cl->class_class_function_count ! : cl->class_obj_method_count; ! for (int i = 0; i < count; ++i) { ! ufunc_T *fp = on_class ? cl->class_class_functions[i] ! : cl->class_obj_methods[i]; // Use a separate pointer to avoid that ASAN complains about // uf_name[] only being 4 characters. char_u *ufname = (char_u *)fp->uf_name; *************** *** 805,813 **** goto fail_after_eval; len = fname_end - fname; ! for (int i = 0; i < cl->class_obj_method_count; ++i) { ! ufunc_T *fp = cl->class_obj_methods[i]; // Use a separate pointer to avoid that ASAN complains about // uf_name[] only being 4 characters. char_u *ufname = (char_u *)fp->uf_name; --- 840,852 ---- goto fail_after_eval; len = fname_end - fname; ! int count = tv.v_type == VAR_CLASS ? cl->class_class_function_count ! : cl->class_obj_method_count; ! ufunc_T **funcs = tv.v_type == VAR_CLASS ? cl->class_class_functions ! : cl->class_obj_methods; ! for (int i = 0; i < count; ++i) { ! ufunc_T *fp = funcs[i]; // Use a separate pointer to avoid that ASAN complains about // uf_name[] only being 4 characters. char_u *ufname = (char_u *)fp->uf_name; *************** *** 824,829 **** --- 863,897 ---- } /* + * If "cctx->ctx_ufunc" indicates we are in a class, check if "name" is a class + * member. If it is then return TRUE and set "cl_ret" and "idx_ret". + */ + int + class_member_exists( + char_u *name, + class_T **cl_ret, + int *idx_ret, + cctx_T *cctx) + { + if (cctx->ctx_ufunc == NULL || cctx->ctx_ufunc->uf_class == NULL) + return FALSE; + class_T *cl = cctx->ctx_ufunc->uf_class; + + for (int idx = 0; idx < cl->class_class_member_count; ++idx) + { + ocmember_T *m = &cl->class_class_members[idx]; + if (STRCMP(m->ocm_name, name) == 0) + { + *cl_ret = cl; + *idx_ret = idx; + return TRUE; + } + } + + return FALSE; + } + + /* * Make a copy of an object. */ void *** ../vim-9.0.1122/src/proto/vim9class.pro 2022-12-10 18:42:09.090378817 +0000 --- src/proto/vim9class.pro 2023-01-01 12:28:33.305662665 +0000 *************** *** 6,11 **** --- 6,12 ---- void ex_type(exarg_T *eap); int class_object_index(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int verbose); ufunc_T *find_class_func(char_u **arg); + int class_member_exists(char_u *name, class_T **cl_ret, int *idx_ret, cctx_T *cctx); void copy_object(typval_T *from, typval_T *to); void object_unref(object_T *obj); void copy_class(typval_T *from, typval_T *to); *** ../vim-9.0.1122/src/structs.h 2022-12-29 20:56:20.021538298 +0000 --- src/structs.h 2022-12-31 20:59:34.665726295 +0000 *************** *** 1493,1501 **** ocmember_T *class_class_members; // allocated typval_T *class_members_tv; // allocated array of class member vals ! // class methods: "static def SomeMethod()" ! int class_class_method_count; ! ufunc_T **class_class_methods; // allocated // object members: "this.varname" int class_obj_member_count; --- 1493,1501 ---- ocmember_T *class_class_members; // allocated typval_T *class_members_tv; // allocated array of class member vals ! // class functions: "static def SomeMethod()" ! int class_class_function_count; ! ufunc_T **class_class_functions; // allocated // object members: "this.varname" int class_obj_member_count; *** ../vim-9.0.1122/src/vim9expr.c 2022-12-29 20:56:20.025538293 +0000 --- src/vim9expr.c 2023-01-01 12:47:25.878330043 +0000 *************** *** 587,593 **** } else { ! lvar_T lvar; if (lookup_local(*arg, len, &lvar, cctx) == OK) { --- 587,594 ---- } else { ! lvar_T lvar; ! class_T *cl = NULL; if (lookup_local(*arg, len, &lvar, cctx) == OK) { *************** *** 602,607 **** --- 603,612 ---- else gen_load = TRUE; } + else if (class_member_exists(name, &cl, &idx, cctx)) + { + res = generate_CLASSMEMBER(cctx, TRUE, cl, idx); + } else { // "var" can be script-local even without using "s:" if it *** ../vim-9.0.1122/src/testdir/test_vim9_class.vim 2022-12-31 19:00:58.845477088 +0000 --- src/testdir/test_vim9_class.vim 2023-01-01 12:56:33.794406510 +0000 *************** *** 377,383 **** static _secret = 7 public static anybody = 42 ! def AddToCounter(nr: number) counter += nr enddef endclass --- 377,383 ---- static _secret = 7 public static anybody = 42 ! static def AddToCounter(nr: number) counter += nr enddef endclass *************** *** 402,407 **** --- 402,433 ---- END v9.CheckScriptSuccess(lines) enddef + + def Test_class_function() + var lines =<< trim END + vim9script + class Value + this.value = 0 + static objects = 0 + + def new(v: number) + this.value = v + ++objects + enddef + + static def GetCount(): number + return objects + enddef + endclass + + assert_equal(0, Value.GetCount()) + var v1 = Value.new(2) + assert_equal(1, Value.GetCount()) + var v2 = Value.new(7) + assert_equal(2, Value.GetCount()) + END + v9.CheckScriptSuccess(lines) + enddef def Test_class_object_to_string() var lines =<< trim END *** ../vim-9.0.1122/src/version.c 2022-12-31 19:00:58.845477088 +0000 --- src/version.c 2023-01-01 12:57:07.734409940 +0000 *************** *** 697,698 **** --- 697,700 ---- { /* Add new patch number below this line */ + /**/ + 1123, /**/ -- If you only have a hammer, you tend to see every problem as a nail. If you only have MS-Windows, you tend to solve every problem by rebooting. /// 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 ///