Mercurial > vim
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); |