comparison src/vim9class.c @ 31653:ec76f9d2319e v9.0.1159

patch 9.0.1159: extends argument for class not implemented yet Commit: https://github.com/vim/vim/commit/8367716a6e9589d61a771e6c329da05c9b55e61a Author: Bram Moolenaar <Bram@vim.org> Date: Sun Jan 8 19:54:10 2023 +0000 patch 9.0.1159: extends argument for class not implemented yet Problem: Extends argument for class not implemented yet. Solution: Basic implementation of "extends".
author Bram Moolenaar <Bram@vim.org>
date Sun, 08 Jan 2023 21:00:05 +0100
parents 520857d1fda7
children 2f1af1b2f82d
comparison
equal deleted inserted replaced
31652:db9e6699d7b3 31653:ec76f9d2319e
158 } 158 }
159 159
160 /* 160 /*
161 * Move the class or object members found while parsing a class into the class. 161 * Move the class or object members found while parsing a class into the class.
162 * "gap" contains the found members. 162 * "gap" contains the found members.
163 * "parent_members" points to the members in the parent class (if any)
164 * "parent_count" is the number of members in the parent class
163 * "members" will be set to the newly allocated array of members and 165 * "members" will be set to the newly allocated array of members and
164 * "member_count" set to the number of members. 166 * "member_count" set to the number of members.
165 * Returns OK or FAIL. 167 * Returns OK or FAIL.
166 */ 168 */
167 static int 169 static int
168 add_members_to_class( 170 add_members_to_class(
169 garray_T *gap, 171 garray_T *gap,
172 ocmember_T *parent_members,
173 int parent_count,
170 ocmember_T **members, 174 ocmember_T **members,
171 int *member_count) 175 int *member_count)
172 { 176 {
173 *member_count = gap->ga_len; 177 *member_count = parent_count + gap->ga_len;
174 *members = gap->ga_len == 0 ? NULL : ALLOC_MULT(ocmember_T, gap->ga_len); 178 *members = *member_count == 0 ? NULL
175 if (gap->ga_len > 0 && *members == NULL) 179 : ALLOC_MULT(ocmember_T, *member_count);
180 if (*member_count > 0 && *members == NULL)
176 return FAIL; 181 return FAIL;
182 for (int i = 0; i < parent_count; ++i)
183 {
184 // parent members need to be copied
185 *members[i] = parent_members[i];
186 members[i]->ocm_name = vim_strsave(members[i]->ocm_name);
187 if (members[i]->ocm_init != NULL)
188 members[i]->ocm_init = vim_strsave(members[i]->ocm_init);
189 }
177 if (gap->ga_len > 0) 190 if (gap->ga_len > 0)
178 mch_memmove(*members, gap->ga_data, sizeof(ocmember_T) * gap->ga_len); 191 // new members are moved
192 mch_memmove(*members + parent_count,
193 gap->ga_data, sizeof(ocmember_T) * gap->ga_len);
179 VIM_CLEAR(gap->ga_data); 194 VIM_CLEAR(gap->ga_data);
180 return OK; 195 return OK;
181 } 196 }
182 197
183 /* 198 /*
231 246
232 // TODO: 247 // TODO:
233 // generics: <Tkey, Tentry> 248 // generics: <Tkey, Tentry>
234 // handle "is_export" if it is set 249 // handle "is_export" if it is set
235 250
251 // Name for "extends BaseClass"
252 char_u *extends = NULL;
253
236 // Names for "implements SomeInterface" 254 // Names for "implements SomeInterface"
237 garray_T ga_impl; 255 garray_T ga_impl;
238 ga_init2(&ga_impl, sizeof(char_u *), 5); 256 ga_init2(&ga_impl, sizeof(char_u *), 5);
239 257
240 arg = skipwhite(name_end); 258 arg = skipwhite(name_end);
241 while (*arg != NUL && *arg != '#' && *arg != '\n') 259 while (*arg != NUL && *arg != '#' && *arg != '\n')
242 { 260 {
243 // TODO: 261 // TODO:
244 // extends SomeClass
245 // specifies SomeInterface 262 // specifies SomeInterface
246 if (STRNCMP(arg, "implements", 10) == 0 && IS_WHITE_OR_NUL(arg[10])) 263 if (STRNCMP(arg, "extends", 7) == 0 && IS_WHITE_OR_NUL(arg[7]))
264 {
265 if (extends != NULL)
266 {
267 emsg(_(e_duplicate_extends));
268 goto early_ret;
269 }
270 arg = skipwhite(arg + 7);
271 char_u *end = find_name_end(arg, NULL, NULL, FNE_CHECK_START);
272 if (!IS_WHITE_OR_NUL(*end))
273 {
274 semsg(_(e_white_space_required_after_name_str), arg);
275 goto early_ret;
276 }
277 extends = vim_strnsave(arg, end - arg);
278 if (extends == NULL)
279 goto early_ret;
280
281 arg = skipwhite(end + 1);
282 }
283 else if (STRNCMP(arg, "implements", 10) == 0
284 && IS_WHITE_OR_NUL(arg[10]))
247 { 285 {
248 if (ga_impl.ga_len > 0) 286 if (ga_impl.ga_len > 0)
249 { 287 {
250 emsg(_(e_duplicate_implements)); 288 emsg(_(e_duplicate_implements));
251 goto early_ret; 289 goto early_ret;
287 } 325 }
288 else 326 else
289 { 327 {
290 semsg(_(e_trailing_characters_str), arg); 328 semsg(_(e_trailing_characters_str), arg);
291 early_ret: 329 early_ret:
330 vim_free(extends);
292 ga_clear_strings(&ga_impl); 331 ga_clear_strings(&ga_impl);
293 return; 332 return;
294 } 333 }
295 } 334 }
296 335
494 break; 533 break;
495 } 534 }
496 } 535 }
497 vim_free(theline); 536 vim_free(theline);
498 537
499 // Check a few things before defining the class. 538 class_T *extends_cl = NULL; // class from "extends" argument
539
540 /*
541 * Check a few things before defining the class.
542 */
543
544 // Check the "extends" class is valid.
545 if (success && extends != NULL)
546 {
547 typval_T tv;
548 tv.v_type = VAR_UNKNOWN;
549 if (eval_variable(extends, 0, 0, &tv, NULL, EVAL_VAR_IMPORT) == FAIL)
550 {
551 semsg(_(e_class_name_not_found_str), extends);
552 success = FALSE;
553 }
554 else
555 {
556 if (tv.v_type != VAR_CLASS
557 || tv.vval.v_class == NULL
558 || (tv.vval.v_class->class_flags & CLASS_INTERFACE) != 0)
559 {
560 semsg(_(e_cannot_extend_str), extends);
561 success = FALSE;
562 }
563 else
564 {
565 extends_cl = tv.vval.v_class;
566 ++extends_cl->class_refcount;
567 }
568 clear_tv(&tv);
569 }
570 }
571 VIM_CLEAR(extends);
572
573 // Check all "implements" entries are valid.
500 if (success && ga_impl.ga_len > 0) 574 if (success && ga_impl.ga_len > 0)
501 { 575 {
502 // Check all "implements" entries are valid and correct.
503 for (int i = 0; i < ga_impl.ga_len && success; ++i) 576 for (int i = 0; i < ga_impl.ga_len && success; ++i)
504 { 577 {
505 char_u *impl = ((char_u **)ga_impl.ga_data)[i]; 578 char_u *impl = ((char_u **)ga_impl.ga_data)[i];
506 typval_T tv; 579 typval_T tv;
507 tv.v_type = VAR_UNKNOWN; 580 tv.v_type = VAR_UNKNOWN;
508 if (eval_variable(impl, 0, 0, &tv, NULL, 581 if (eval_variable(impl, 0, 0, &tv, NULL, EVAL_VAR_IMPORT) == FAIL)
509 EVAL_VAR_VERBOSE + EVAL_VAR_IMPORT) == FAIL)
510 { 582 {
511 semsg(_(e_interface_name_not_found_str), impl); 583 semsg(_(e_interface_name_not_found_str), impl);
512 success = FALSE; 584 success = FALSE;
513 break; 585 break;
514 } 586 }
618 cl->class_refcount = 1; 690 cl->class_refcount = 1;
619 cl->class_name = vim_strnsave(name_start, name_end - name_start); 691 cl->class_name = vim_strnsave(name_start, name_end - name_start);
620 if (cl->class_name == NULL) 692 if (cl->class_name == NULL)
621 goto cleanup; 693 goto cleanup;
622 694
695 cl->class_extends = extends_cl;
696
623 if (ga_impl.ga_len > 0) 697 if (ga_impl.ga_len > 0)
624 { 698 {
625 // Move the "implements" names into the class. 699 // Move the "implements" names into the class.
626 cl->class_interface_count = ga_impl.ga_len; 700 cl->class_interface_count = ga_impl.ga_len;
627 cl->class_interfaces = ALLOC_MULT(char_u *, ga_impl.ga_len); 701 cl->class_interfaces = ALLOC_MULT(char_u *, ga_impl.ga_len);
633 ga_impl.ga_len = 0; 707 ga_impl.ga_len = 0;
634 } 708 }
635 709
636 // Add class and object members to "cl". 710 // Add class and object members to "cl".
637 if (add_members_to_class(&classmembers, 711 if (add_members_to_class(&classmembers,
638 &cl->class_class_members, 712 extends_cl == NULL ? NULL
639 &cl->class_class_member_count) == FAIL 713 : extends_cl->class_class_members,
714 extends_cl == NULL ? 0
715 : extends_cl->class_class_member_count,
716 &cl->class_class_members,
717 &cl->class_class_member_count) == FAIL
640 || add_members_to_class(&objmembers, 718 || add_members_to_class(&objmembers,
641 &cl->class_obj_members, 719 extends_cl == NULL ? NULL
642 &cl->class_obj_member_count) == FAIL) 720 : extends_cl->class_obj_members,
721 extends_cl == NULL ? 0
722 : extends_cl->class_obj_member_count,
723 &cl->class_obj_members,
724 &cl->class_obj_member_count) == FAIL)
643 goto cleanup; 725 goto cleanup;
644 726
645 if (is_class && cl->class_class_member_count > 0) 727 if (is_class && cl->class_class_member_count > 0)
646 { 728 {
647 // Allocate a typval for each class member and initialize it. 729 // Allocate a typval for each class member and initialize it.
733 int *fcount = loop == 1 ? &cl->class_class_function_count 815 int *fcount = loop == 1 ? &cl->class_class_function_count
734 : &cl->class_obj_method_count; 816 : &cl->class_obj_method_count;
735 ufunc_T ***fup = loop == 1 ? &cl->class_class_functions 817 ufunc_T ***fup = loop == 1 ? &cl->class_class_functions
736 : &cl->class_obj_methods; 818 : &cl->class_obj_methods;
737 819
738 *fcount = gap->ga_len; 820 int parent_count = 0;
739 if (gap->ga_len == 0) 821 if (extends_cl != NULL)
822 // Include functions from the parent.
823 parent_count = loop == 1
824 ? extends_cl->class_class_function_count
825 : extends_cl->class_obj_method_count;
826
827 *fcount = parent_count + gap->ga_len;
828 if (*fcount == 0)
740 { 829 {
741 *fup = NULL; 830 *fup = NULL;
742 continue; 831 continue;
743 } 832 }
744 *fup = ALLOC_MULT(ufunc_T *, gap->ga_len); 833 *fup = ALLOC_MULT(ufunc_T *, *fcount);
745 if (*fup == NULL) 834 if (*fup == NULL)
746 goto cleanup; 835 goto cleanup;
747 mch_memmove(*fup, gap->ga_data, sizeof(ufunc_T *) * gap->ga_len); 836
837 int skipped = 0;
838 for (int i = 0; i < parent_count; ++i)
839 {
840 // Copy functions from the parent. Can't use the same
841 // function, because "uf_class" is different and compilation
842 // will have a different result.
843 // Skip "new" functions. TODO: not all of them.
844 if (loop == 1 && STRNCMP(
845 extends_cl->class_class_functions[i]->uf_name,
846 "new", 3) == 0)
847 ++skipped;
848 else
849 *fup[i - skipped] = copy_function((loop == 1
850 ? extends_cl->class_class_functions
851 : extends_cl->class_obj_methods)[i]);
852 }
853
854 mch_memmove(*fup + parent_count - skipped, gap->ga_data,
855 sizeof(ufunc_T *) * gap->ga_len);
748 vim_free(gap->ga_data); 856 vim_free(gap->ga_data);
749 857 *fcount -= skipped;
750 // Set the class pointer on all the object methods. 858
751 for (int i = 0; i < gap->ga_len; ++i) 859 // Set the class pointer on all the functions and object methods.
860 for (int i = 0; i < *fcount; ++i)
752 { 861 {
753 ufunc_T *fp = (*fup)[i]; 862 ufunc_T *fp = (*fup)[i];
754 fp->uf_class = cl; 863 fp->uf_class = cl;
755 if (loop == 2) 864 if (loop == 2)
756 fp->uf_flags |= FC_OBJECT; 865 fp->uf_flags |= FC_OBJECT;
784 vim_free(cl->class_obj_members); 893 vim_free(cl->class_obj_members);
785 vim_free(cl->class_obj_methods); 894 vim_free(cl->class_obj_methods);
786 vim_free(cl); 895 vim_free(cl);
787 } 896 }
788 897
898 vim_free(extends);
899 class_unref(extends_cl);
789 ga_clear_strings(&ga_impl); 900 ga_clear_strings(&ga_impl);
790 901
791 for (int round = 1; round <= 2; ++round) 902 for (int round = 1; round <= 2; ++round)
792 { 903 {
793 garray_T *gap = round == 1 ? &classmembers : &objmembers; 904 garray_T *gap = round == 1 ? &classmembers : &objmembers;
1165 // Freeing what the class contains may recursively come back here. 1276 // Freeing what the class contains may recursively come back here.
1166 // Clear "class_name" first, if it is NULL the class does not need to 1277 // Clear "class_name" first, if it is NULL the class does not need to
1167 // be freed. 1278 // be freed.
1168 VIM_CLEAR(cl->class_name); 1279 VIM_CLEAR(cl->class_name);
1169 1280
1281 class_unref(cl->class_extends);
1282
1170 for (int i = 0; i < cl->class_interface_count; ++i) 1283 for (int i = 0; i < cl->class_interface_count; ++i)
1171 vim_free(((char_u **)cl->class_interfaces)[i]); 1284 vim_free(((char_u **)cl->class_interfaces)[i]);
1172 vim_free(cl->class_interfaces); 1285 vim_free(cl->class_interfaces);
1173 1286
1174 for (int i = 0; i < cl->class_class_member_count; ++i) 1287 for (int i = 0; i < cl->class_class_member_count; ++i)