To: vim_dev@googlegroups.com Subject: Patch 9.0.1060 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 9.0.1060 problem: Private and public object members are not implemented yet. Solution: Implement private and public object members. Files: src/vim9class.c, src/errors.h, src/structs.h, src/vim9expr.c, src/eval.c, src/testdir/test_vim9_class.vim *** ../vim-9.0.1059/src/vim9class.c 2022-12-14 15:06:07.061956386 +0000 --- src/vim9class.c 2022-12-14 19:37:37.410578885 +0000 *************** *** 110,128 **** break; } - // "this.varname" // "this._varname" ! // TODO: ! // "public this.varname" ! if (STRNCMP(line, "this", 4) == 0) { ! if (line[4] != '.' || !eval_isnamec1(line[5])) { ! semsg(_(e_invalid_object_member_declaration_str), line); break; } ! char_u *varname = line + 5; char_u *varname_end = to_name_end(varname, FALSE); char_u *colon = skipwhite(varname_end); char_u *type_arg = colon; --- 110,149 ---- break; } // "this._varname" ! // "this.varname" ! // "public this.varname" ! int has_public = FALSE; ! if (checkforcmd(&p, "public", 3)) { ! if (STRNCMP(line, "public", 6) != 0) ! { ! semsg(_(e_command_cannot_be_shortened_str), line); ! break; ! } ! has_public = TRUE; ! p = skipwhite(line + 6); ! ! if (STRNCMP(p, "this", 4) != 0) { ! emsg(_(e_public_must_be_followed_by_this)); break; } ! } ! if (STRNCMP(p, "this", 4) == 0) ! { ! if (p[4] != '.' || !eval_isnamec1(p[5])) ! { ! semsg(_(e_invalid_object_member_declaration_str), p); ! break; ! } ! char_u *varname = p + 5; char_u *varname_end = to_name_end(varname, FALSE); + if (*varname == '_' && has_public) + { + semsg(_(e_public_object_member_name_cannot_start_with_underscore_str), line); + break; + } char_u *colon = skipwhite(varname_end); char_u *type_arg = colon; *************** *** 199,204 **** --- 220,228 ---- objmember_T *m = ((objmember_T *)objmembers.ga_data) + objmembers.ga_len; m->om_name = vim_strnsave(varname, varname_end - varname); + m->om_access = has_public ? ACCESS_ALL + : *varname == '_' ? ACCESS_PRIVATE + : ACCESS_READ; m->om_type = type; if (expr_end > expr_start) m->om_init = vim_strnsave(expr_start, expr_end - expr_start); *************** *** 551,556 **** --- 575,587 ---- objmember_T *m = &cl->class_obj_members[i]; if (STRNCMP(name, m->om_name, len) == 0 && m->om_name[len] == NUL) { + if (*name == '_') + { + semsg(_(e_cannot_access_private_object_member_str), + m->om_name); + return FAIL; + } + // The object only contains a pointer to the class, the member // values array follows right after that. object_T *obj = rettv->vval.v_object; *** ../vim-9.0.1059/src/errors.h 2022-12-13 21:14:19.219930894 +0000 --- src/errors.h 2022-12-14 20:45:06.332433051 +0000 *************** *** 3378,3381 **** --- 3378,3391 ---- INIT(= N_("E1329: Cannot get object member type from initializer: %s")); EXTERN char e_invalid_type_for_object_member_str[] INIT(= N_("E1330: Invalid type for object member: %s")); + EXTERN char e_public_must_be_followed_by_this[] + INIT(= N_("E1331: Public must be followed by \"this\"")); + EXTERN char e_public_object_member_name_cannot_start_with_underscore_str[] + INIT(= N_("E1332: Public object member name cannot start with underscore: %s")); + EXTERN char e_cannot_access_private_object_member_str[] + INIT(= N_("E1333: Cannot access private object member: %s")); + EXTERN char e_object_member_not_found_str[] + INIT(= N_("E1334: Object member not found: %s")); + EXTERN char e_object_member_is_not_writable_str[] + INIT(= N_("E1335: Object member is not writable: %s")); #endif *** ../vim-9.0.1059/src/structs.h 2022-12-10 18:42:09.094378801 +0000 --- src/structs.h 2022-12-14 19:07:47.903514703 +0000 *************** *** 1459,1469 **** --- 1459,1476 ---- type_T *type_decl; // declared type or equal to type_current } type2_T; + typedef enum { + ACCESS_PRIVATE, // read/write only inside th class + ACCESS_READ, // read everywhere, write only inside th class + ACCESS_ALL // read/write everywhere + } omacc_T; + /* * Entry for an object member variable. */ typedef struct { char_u *om_name; // allocated + omacc_T om_access; type_T *om_type; char_u *om_init; // allocated } objmember_T; *************** *** 1720,1726 **** def_status_T uf_def_status; // UF_NOT_COMPILED, UF_TO_BE_COMPILED, etc. int uf_dfunc_idx; // only valid if uf_def_status is UF_COMPILED ! class_T *uf_class; // for object method and constructor garray_T uf_args; // arguments, including optional arguments garray_T uf_def_args; // default argument expressions --- 1727,1734 ---- def_status_T uf_def_status; // UF_NOT_COMPILED, UF_TO_BE_COMPILED, etc. int uf_dfunc_idx; // only valid if uf_def_status is UF_COMPILED ! class_T *uf_class; // for object method and constructor; does not ! // count for class_refcount garray_T uf_args; // arguments, including optional arguments garray_T uf_def_args; // default argument expressions *** ../vim-9.0.1059/src/vim9expr.c 2022-12-10 18:42:09.094378801 +0000 --- src/vim9expr.c 2022-12-14 19:44:13.638410537 +0000 *************** *** 281,286 **** --- 281,293 ---- objmember_T *m = &cl->class_obj_members[i]; if (STRNCMP(name, m->om_name, len) == 0 && m->om_name[len] == NUL) { + if (*name == '_' && cctx->ctx_ufunc->uf_class != cl) + { + semsg(_(e_cannot_access_private_object_member_str), + m->om_name); + return FAIL; + } + generate_GET_OBJ_MEMBER(cctx, i, m->om_type); *arg = name_end; *** ../vim-9.0.1059/src/eval.c 2022-12-14 17:49:56.611042669 +0000 --- src/eval.c 2022-12-14 20:43:38.608552363 +0000 *************** *** 1194,1199 **** --- 1194,1200 ---- while (*p == '[' || (*p == '.' && p[1] != '=' && p[1] != '.')) { if (*p == '.' && lp->ll_tv->v_type != VAR_DICT + && lp->ll_tv->v_type != VAR_OBJECT && lp->ll_tv->v_type != VAR_CLASS) { if (!quiet) *************** *** 1203,1208 **** --- 1204,1210 ---- if (lp->ll_tv->v_type != VAR_LIST && lp->ll_tv->v_type != VAR_DICT && lp->ll_tv->v_type != VAR_BLOB + && lp->ll_tv->v_type != VAR_OBJECT && lp->ll_tv->v_type != VAR_CLASS) { if (!quiet) *************** *** 1509,1518 **** lp->ll_tv = &lp->ll_li->li_tv; } ! else // v_type == VAR_CLASS { ! // TODO: check object members and methods if ! // "key" points name start, "p" to the end } } --- 1511,1565 ---- lp->ll_tv = &lp->ll_li->li_tv; } ! else // v_type == VAR_CLASS || v_type == VAR_OBJECT { ! class_T *cl = (lp->ll_tv->v_type == VAR_OBJECT ! && lp->ll_tv->vval.v_object != NULL) ! ? lp->ll_tv->vval.v_object->obj_class ! : lp->ll_tv->vval.v_class; ! // TODO: what if class is NULL? ! if (cl != NULL) ! { ! lp->ll_valtype = NULL; ! for (int i = 0; i < cl->class_obj_member_count; ++i) ! { ! objmember_T *om = cl->class_obj_members + i; ! if (STRNCMP(om->om_name, key, p - key) == 0 ! && om->om_name[p - key] == NUL) ! { ! switch (om->om_access) ! { ! case ACCESS_PRIVATE: ! semsg(_(e_cannot_access_private_object_member_str), ! om->om_name); ! return NULL; ! case ACCESS_READ: ! if (!(flags & GLV_READ_ONLY)) ! { ! semsg(_(e_object_member_is_not_writable_str), ! om->om_name); ! return NULL; ! } ! break; ! case ACCESS_ALL: ! break; ! } ! ! lp->ll_valtype = om->om_type; ! ! if (lp->ll_tv->v_type == VAR_OBJECT) ! lp->ll_tv = ((typval_T *)( ! lp->ll_tv->vval.v_object + 1)) + i; ! // TODO: what about a class? ! break; ! } ! } ! if (lp->ll_valtype == NULL) ! { ! semsg(_(e_object_member_not_found_str), key); ! return NULL; ! } ! } } } *************** *** 1640,1646 **** else { /* ! * Assign to a List or Dictionary item. */ if ((flags & (ASSIGN_CONST | ASSIGN_FINAL)) && (flags & ASSIGN_FOR_LOOP) == 0) --- 1687,1693 ---- else { /* ! * Assign to a List, Dictionary or Object item. */ if ((flags & (ASSIGN_CONST | ASSIGN_FINAL)) && (flags & ASSIGN_FOR_LOOP) == 0) *** ../vim-9.0.1059/src/testdir/test_vim9_class.vim 2022-12-14 17:30:34.105232856 +0000 --- src/testdir/test_vim9_class.vim 2022-12-14 20:52:05.499900404 +0000 *************** *** 283,288 **** --- 283,315 ---- v9.CheckScriptFailure(lines, 'E1330:') enddef + def Test_class_object_member_access() + var lines =<< trim END + vim9script + class Triple + this._one = 1 + this.two = 2 + public this.three = 3 + + def GetOne(): number + return this._one + enddef + endclass + + var trip = Triple.new() + assert_equal(1, trip.GetOne()) + assert_equal(2, trip.two) + assert_equal(3, trip.three) + assert_fails('echo trip._one', 'E1333') + + assert_fails('trip._one = 11', 'E1333') + assert_fails('trip.two = 22', 'E1335') + trip.three = 33 + assert_equal(33, trip.three) + END + v9.CheckScriptSuccess(lines) + enddef + def Test_class_object_to_string() var lines =<< trim END vim9script *** ../vim-9.0.1059/src/version.c 2022-12-14 17:49:56.611042669 +0000 --- src/version.c 2022-12-14 20:51:05.867973365 +0000 *************** *** 697,698 **** --- 697,700 ---- { /* Add new patch number below this line */ + /**/ + 1060, /**/ -- "To whoever finds this note - I have been imprisoned by my father who wishes me to marry against my will. Please please please please come and rescue me. I am in the tall tower of Swamp Castle." SIR LAUNCELOT's eyes light up with holy inspiration. "Monty Python and the Holy Grail" PYTHON (MONTY) PICTURES LTD /// 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 ///