comparison 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
comparison
equal deleted inserted replaced
31638:75be0502f95c 31639:62237ea155d9
225 if (!IS_WHITE_OR_NUL(*name_end)) 225 if (!IS_WHITE_OR_NUL(*name_end))
226 { 226 {
227 semsg(_(e_white_space_required_after_name_str), arg); 227 semsg(_(e_white_space_required_after_name_str), arg);
228 return; 228 return;
229 } 229 }
230 char_u *name_start = arg;
230 231
231 // TODO: 232 // TODO:
232 // generics: <Tkey, Tentry> 233 // generics: <Tkey, Tentry>
233 // extends SomeClass
234 // implements SomeInterface
235 // specifies SomeInterface
236 // check that nothing follows
237 // handle "is_export" if it is set 234 // handle "is_export" if it is set
235
236 // Names for "implements SomeInterface"
237 garray_T ga_impl;
238 ga_init2(&ga_impl, sizeof(char_u *), 5);
239
240 arg = skipwhite(name_end);
241 while (*arg != NUL && *arg != '#' && *arg != '\n')
242 {
243 // TODO:
244 // extends SomeClass
245 // specifies SomeInterface
246 if (STRNCMP(arg, "implements", 10) == 0 && IS_WHITE_OR_NUL(arg[10]))
247 {
248 arg = skipwhite(arg + 10);
249 char_u *impl_end = find_name_end(arg, NULL, NULL, FNE_CHECK_START);
250 if (!IS_WHITE_OR_NUL(*impl_end))
251 {
252 semsg(_(e_white_space_required_after_name_str), arg);
253 goto early_ret;
254 }
255 char_u *iname = vim_strnsave(arg, impl_end - arg);
256 if (iname == NULL)
257 goto early_ret;
258 if (ga_add_string(&ga_impl, iname) == FAIL)
259 {
260 vim_free(iname);
261 goto early_ret;
262 }
263 arg = skipwhite(impl_end);
264 }
265 else
266 {
267 semsg(_(e_trailing_characters_str), arg);
268 early_ret:
269 ga_clear_strings(&ga_impl);
270 return;
271 }
272 }
238 273
239 garray_T type_list; // list of pointers to allocated types 274 garray_T type_list; // list of pointers to allocated types
240 ga_init2(&type_list, sizeof(type_T *), 10); 275 ga_init2(&type_list, sizeof(type_T *), 10);
241 276
242 // Growarray with class members declared in the class. 277 // Growarray with class members declared in the class.
436 break; 471 break;
437 } 472 }
438 } 473 }
439 vim_free(theline); 474 vim_free(theline);
440 475
476 // Check a few things before defining the class.
477 if (success && ga_impl.ga_len > 0)
478 {
479 // Check all "implements" entries are valid and correct.
480 for (int i = 0; i < ga_impl.ga_len && success; ++i)
481 {
482 char_u *impl = ((char_u **)ga_impl.ga_data)[i];
483 typval_T tv;
484 tv.v_type = VAR_UNKNOWN;
485 if (eval_variable(impl, 0, 0, &tv, NULL,
486 EVAL_VAR_VERBOSE + EVAL_VAR_IMPORT) == FAIL)
487 {
488 semsg(_(e_interface_name_not_found_str), impl);
489 success = FALSE;
490 break;
491 }
492
493 if (tv.v_type != VAR_CLASS
494 || tv.vval.v_class == NULL
495 || (tv.vval.v_class->class_flags & CLASS_INTERFACE) == 0)
496 {
497 semsg(_(e_not_valid_interface_str), impl);
498 success = FALSE;
499 }
500
501 // check the members of the interface match the members of the class
502 class_T *ifcl = tv.vval.v_class;
503 for (int loop = 1; loop <= 2 && success; ++loop)
504 {
505 // loop == 1: check class members
506 // loop == 2: check object members
507 int if_count = loop == 1 ? ifcl->class_class_member_count
508 : ifcl->class_obj_member_count;
509 if (if_count == 0)
510 continue;
511 ocmember_T *if_ms = loop == 1 ? ifcl->class_class_members
512 : ifcl->class_obj_members;
513 ocmember_T *cl_ms = (ocmember_T *)(loop == 1
514 ? classmembers.ga_data
515 : objmembers.ga_data);
516 int cl_count = loop == 1 ? classmembers.ga_len
517 : objmembers.ga_len;
518 for (int if_i = 0; if_i < if_count; ++if_i)
519 {
520 int cl_i;
521 for (cl_i = 0; cl_i < cl_count; ++cl_i)
522 {
523 ocmember_T *m = &cl_ms[cl_i];
524 if (STRCMP(if_ms[if_i].ocm_name, m->ocm_name) == 0)
525 {
526 // TODO: check type
527 break;
528 }
529 }
530 if (cl_i == cl_count)
531 {
532 semsg(_(e_member_str_of_interface_str_not_implemented),
533 if_ms[if_i].ocm_name, impl);
534 success = FALSE;
535 break;
536 }
537 }
538 }
539
540 // check the functions/methods of the interface match the
541 // functions/methods of the class
542 for (int loop = 1; loop <= 2 && success; ++loop)
543 {
544 // loop == 1: check class functions
545 // loop == 2: check object methods
546 int if_count = loop == 1 ? ifcl->class_class_function_count
547 : ifcl->class_obj_method_count;
548 if (if_count == 0)
549 continue;
550 ufunc_T **if_fp = loop == 1 ? ifcl->class_class_functions
551 : ifcl->class_obj_methods;
552 ufunc_T **cl_fp = (ufunc_T **)(loop == 1
553 ? classfunctions.ga_data
554 : objmethods.ga_data);
555 int cl_count = loop == 1 ? classfunctions.ga_len
556 : objmethods.ga_len;
557 for (int if_i = 0; if_i < if_count; ++if_i)
558 {
559 char_u *if_name = if_fp[if_i]->uf_name;
560 int cl_i;
561 for (cl_i = 0; cl_i < cl_count; ++cl_i)
562 {
563 char_u *cl_name = cl_fp[cl_i]->uf_name;
564 if (STRCMP(if_name, cl_name) == 0)
565 {
566 // TODO: check return and argument types
567 break;
568 }
569 }
570 if (cl_i == cl_count)
571 {
572 semsg(_(e_function_str_of_interface_str_not_implemented),
573 if_name, impl);
574 success = FALSE;
575 break;
576 }
577 }
578 }
579
580 clear_tv(&tv);
581 }
582 }
583
441 class_T *cl = NULL; 584 class_T *cl = NULL;
442 if (success) 585 if (success)
443 { 586 {
444 // "endclass" encountered without failures: Create the class. 587 // "endclass" encountered without failures: Create the class.
445 588
448 goto cleanup; 591 goto cleanup;
449 if (!is_class) 592 if (!is_class)
450 cl->class_flags = CLASS_INTERFACE; 593 cl->class_flags = CLASS_INTERFACE;
451 594
452 cl->class_refcount = 1; 595 cl->class_refcount = 1;
453 cl->class_name = vim_strnsave(arg, name_end - arg); 596 cl->class_name = vim_strnsave(name_start, name_end - name_start);
454 if (cl->class_name == NULL) 597 if (cl->class_name == NULL)
455 goto cleanup; 598 goto cleanup;
599
600 if (ga_impl.ga_len > 0)
601 {
602 // Move the "implements" names into the class.
603 cl->class_interface_count = ga_impl.ga_len;
604 cl->class_interfaces = ALLOC_MULT(char_u *, ga_impl.ga_len);
605 if (cl->class_interfaces == NULL)
606 goto cleanup;
607 for (int i = 0; i < ga_impl.ga_len; ++i)
608 cl->class_interfaces[i] = ((char_u **)ga_impl.ga_data)[i];
609 CLEAR_POINTER(ga_impl.ga_data);
610 ga_impl.ga_len = 0;
611 }
456 612
457 // Add class and object members to "cl". 613 // Add class and object members to "cl".
458 if (add_members_to_class(&classmembers, 614 if (add_members_to_class(&classmembers,
459 &cl->class_class_members, 615 &cl->class_class_members,
460 &cl->class_class_member_count) == FAIL 616 &cl->class_class_member_count) == FAIL
497 "new") == 0) 653 "new") == 0)
498 { 654 {
499 have_new = TRUE; 655 have_new = TRUE;
500 break; 656 break;
501 } 657 }
502 if (!have_new) 658 if (is_class && !have_new)
503 { 659 {
504 // No new() method was defined, add the default constructor. 660 // No new() method was defined, add the default constructor.
505 garray_T fga; 661 garray_T fga;
506 ga_init2(&fga, 1, 1000); 662 ga_init2(&fga, 1, 1000);
507 ga_concat(&fga, (char_u *)"new("); 663 ga_concat(&fga, (char_u *)"new(");
587 743
588 // TODO: 744 // TODO:
589 // - Fill hashtab with object members and methods ? 745 // - Fill hashtab with object members and methods ?
590 746
591 // Add the class to the script-local variables. 747 // Add the class to the script-local variables.
748 // TODO: handle other context, e.g. in a function
592 typval_T tv; 749 typval_T tv;
593 tv.v_type = VAR_CLASS; 750 tv.v_type = VAR_CLASS;
594 tv.vval.v_class = cl; 751 tv.vval.v_class = cl;
595 set_var_const(cl->class_name, current_sctx.sc_sid, 752 set_var_const(cl->class_name, current_sctx.sc_sid,
596 NULL, &tv, FALSE, ASSIGN_DECL, 0); 753 NULL, &tv, FALSE, ASSIGN_DECL, 0);
604 vim_free(cl->class_class_functions); 761 vim_free(cl->class_class_functions);
605 vim_free(cl->class_obj_members); 762 vim_free(cl->class_obj_members);
606 vim_free(cl->class_obj_methods); 763 vim_free(cl->class_obj_methods);
607 vim_free(cl); 764 vim_free(cl);
608 } 765 }
766
767 ga_clear_strings(&ga_impl);
609 768
610 for (int round = 1; round <= 2; ++round) 769 for (int round = 1; round <= 2; ++round)
611 { 770 {
612 garray_T *gap = round == 1 ? &classmembers : &objmembers; 771 garray_T *gap = round == 1 ? &classmembers : &objmembers;
613 if (gap->ga_len == 0 || gap->ga_data == NULL) 772 if (gap->ga_len == 0 || gap->ga_data == NULL)
984 // Freeing what the class contains may recursively come back here. 1143 // Freeing what the class contains may recursively come back here.
985 // Clear "class_name" first, if it is NULL the class does not need to 1144 // Clear "class_name" first, if it is NULL the class does not need to
986 // be freed. 1145 // be freed.
987 VIM_CLEAR(cl->class_name); 1146 VIM_CLEAR(cl->class_name);
988 1147
1148 for (int i = 0; i < cl->class_interface_count; ++i)
1149 vim_free(((char_u **)cl->class_interfaces)[i]);
1150 vim_free(cl->class_interfaces);
1151
989 for (int i = 0; i < cl->class_class_member_count; ++i) 1152 for (int i = 0; i < cl->class_class_member_count; ++i)
990 { 1153 {
991 ocmember_T *m = &cl->class_class_members[i]; 1154 ocmember_T *m = &cl->class_class_members[i];
992 vim_free(m->ocm_name); 1155 vim_free(m->ocm_name);
993 vim_free(m->ocm_init); 1156 vim_free(m->ocm_init);