Mercurial > vim
diff src/vim9class.c @ 31639:62237ea155d9 v9.0.1152
patch 9.0.1152: class "implements" argument not implemented
Commit: https://github.com/vim/vim/commit/94674f2223aafeaa4690f25e12f3ebe07814c5ba
Author: Bram Moolenaar <Bram@vim.org>
Date: Fri Jan 6 18:42:20 2023 +0000
patch 9.0.1152: class "implements" argument not implemented
Problem: Class "implements" argument not implemented.
Solution: Implement "implements" argument. Add basic checks for when a
class implements an interface.
author | Bram Moolenaar <Bram@vim.org> |
---|---|
date | Fri, 06 Jan 2023 19:45:05 +0100 |
parents | 5c1b7a87466e |
children | 88aa7f62cc78 |
line wrap: on
line diff
--- a/src/vim9class.c +++ b/src/vim9class.c @@ -227,15 +227,50 @@ ex_class(exarg_T *eap) semsg(_(e_white_space_required_after_name_str), arg); return; } + char_u *name_start = arg; // TODO: // generics: <Tkey, Tentry> - // extends SomeClass - // implements SomeInterface - // specifies SomeInterface - // check that nothing follows // handle "is_export" if it is set + // Names for "implements SomeInterface" + garray_T ga_impl; + ga_init2(&ga_impl, sizeof(char_u *), 5); + + arg = skipwhite(name_end); + while (*arg != NUL && *arg != '#' && *arg != '\n') + { + // TODO: + // extends SomeClass + // specifies SomeInterface + if (STRNCMP(arg, "implements", 10) == 0 && IS_WHITE_OR_NUL(arg[10])) + { + arg = skipwhite(arg + 10); + char_u *impl_end = find_name_end(arg, NULL, NULL, FNE_CHECK_START); + if (!IS_WHITE_OR_NUL(*impl_end)) + { + semsg(_(e_white_space_required_after_name_str), arg); + goto early_ret; + } + char_u *iname = vim_strnsave(arg, impl_end - arg); + if (iname == NULL) + goto early_ret; + if (ga_add_string(&ga_impl, iname) == FAIL) + { + vim_free(iname); + goto early_ret; + } + arg = skipwhite(impl_end); + } + else + { + semsg(_(e_trailing_characters_str), arg); +early_ret: + ga_clear_strings(&ga_impl); + return; + } + } + garray_T type_list; // list of pointers to allocated types ga_init2(&type_list, sizeof(type_T *), 10); @@ -438,6 +473,114 @@ ex_class(exarg_T *eap) } vim_free(theline); + // Check a few things before defining the class. + if (success && ga_impl.ga_len > 0) + { + // Check all "implements" entries are valid and correct. + for (int i = 0; i < ga_impl.ga_len && success; ++i) + { + char_u *impl = ((char_u **)ga_impl.ga_data)[i]; + typval_T tv; + tv.v_type = VAR_UNKNOWN; + if (eval_variable(impl, 0, 0, &tv, NULL, + EVAL_VAR_VERBOSE + EVAL_VAR_IMPORT) == FAIL) + { + semsg(_(e_interface_name_not_found_str), impl); + success = FALSE; + break; + } + + if (tv.v_type != VAR_CLASS + || tv.vval.v_class == NULL + || (tv.vval.v_class->class_flags & CLASS_INTERFACE) == 0) + { + semsg(_(e_not_valid_interface_str), impl); + success = FALSE; + } + + // check the members of the interface match the members of the class + class_T *ifcl = tv.vval.v_class; + for (int loop = 1; loop <= 2 && success; ++loop) + { + // loop == 1: check class members + // loop == 2: check object members + int if_count = loop == 1 ? ifcl->class_class_member_count + : ifcl->class_obj_member_count; + if (if_count == 0) + continue; + ocmember_T *if_ms = loop == 1 ? ifcl->class_class_members + : ifcl->class_obj_members; + ocmember_T *cl_ms = (ocmember_T *)(loop == 1 + ? classmembers.ga_data + : objmembers.ga_data); + int cl_count = loop == 1 ? classmembers.ga_len + : objmembers.ga_len; + for (int if_i = 0; if_i < if_count; ++if_i) + { + int cl_i; + for (cl_i = 0; cl_i < cl_count; ++cl_i) + { + ocmember_T *m = &cl_ms[cl_i]; + if (STRCMP(if_ms[if_i].ocm_name, m->ocm_name) == 0) + { + // TODO: check type + break; + } + } + if (cl_i == cl_count) + { + semsg(_(e_member_str_of_interface_str_not_implemented), + if_ms[if_i].ocm_name, impl); + success = FALSE; + break; + } + } + } + + // check the functions/methods of the interface match the + // functions/methods of the class + for (int loop = 1; loop <= 2 && success; ++loop) + { + // loop == 1: check class functions + // loop == 2: check object methods + int if_count = loop == 1 ? ifcl->class_class_function_count + : ifcl->class_obj_method_count; + if (if_count == 0) + continue; + ufunc_T **if_fp = loop == 1 ? ifcl->class_class_functions + : ifcl->class_obj_methods; + ufunc_T **cl_fp = (ufunc_T **)(loop == 1 + ? classfunctions.ga_data + : objmethods.ga_data); + int cl_count = loop == 1 ? classfunctions.ga_len + : objmethods.ga_len; + for (int if_i = 0; if_i < if_count; ++if_i) + { + char_u *if_name = if_fp[if_i]->uf_name; + int cl_i; + for (cl_i = 0; cl_i < cl_count; ++cl_i) + { + char_u *cl_name = cl_fp[cl_i]->uf_name; + if (STRCMP(if_name, cl_name) == 0) + { + // TODO: check return and argument types + break; + } + } + if (cl_i == cl_count) + { + semsg(_(e_function_str_of_interface_str_not_implemented), + if_name, impl); + success = FALSE; + break; + } + } + } + + clear_tv(&tv); + } + } + class_T *cl = NULL; if (success) { @@ -450,10 +593,23 @@ ex_class(exarg_T *eap) cl->class_flags = CLASS_INTERFACE; cl->class_refcount = 1; - cl->class_name = vim_strnsave(arg, name_end - arg); + cl->class_name = vim_strnsave(name_start, name_end - name_start); if (cl->class_name == NULL) goto cleanup; + if (ga_impl.ga_len > 0) + { + // Move the "implements" names into the class. + cl->class_interface_count = ga_impl.ga_len; + cl->class_interfaces = ALLOC_MULT(char_u *, ga_impl.ga_len); + if (cl->class_interfaces == NULL) + goto cleanup; + for (int i = 0; i < ga_impl.ga_len; ++i) + cl->class_interfaces[i] = ((char_u **)ga_impl.ga_data)[i]; + CLEAR_POINTER(ga_impl.ga_data); + ga_impl.ga_len = 0; + } + // Add class and object members to "cl". if (add_members_to_class(&classmembers, &cl->class_class_members, @@ -499,7 +655,7 @@ ex_class(exarg_T *eap) have_new = TRUE; break; } - if (!have_new) + if (is_class && !have_new) { // No new() method was defined, add the default constructor. garray_T fga; @@ -589,6 +745,7 @@ ex_class(exarg_T *eap) // - Fill hashtab with object members and methods ? // Add the class to the script-local variables. + // TODO: handle other context, e.g. in a function typval_T tv; tv.v_type = VAR_CLASS; tv.vval.v_class = cl; @@ -607,6 +764,8 @@ cleanup: vim_free(cl); } + ga_clear_strings(&ga_impl); + for (int round = 1; round <= 2; ++round) { garray_T *gap = round == 1 ? &classmembers : &objmembers; @@ -986,6 +1145,10 @@ class_unref(class_T *cl) // be freed. VIM_CLEAR(cl->class_name); + for (int i = 0; i < cl->class_interface_count; ++i) + vim_free(((char_u **)cl->class_interfaces)[i]); + vim_free(cl->class_interfaces); + for (int i = 0; i < cl->class_class_member_count; ++i) { ocmember_T *m = &cl->class_class_members[i];