Patch 9.0.1123
Problem: Class function not implemented yet.
Solution: Implement defining and calling a class function.
Files: src/vim9class.c, src/proto/vim9class.pro, src/structs.h,
src/vim9expr.c, src/testdir/test_vim9_class.vim
*** ../vim-9.0.1122/src/vim9class.c 2022-12-23 17:56:21.405511534 +0000
--- src/vim9class.c 2023-01-01 12:29:03.529703540 +0000
***************
*** 223,231 ****
garray_T classmembers;
ga_init2(&classmembers, sizeof(ocmember_T), 10);
! // Growarray with object methods declared in the class.
! garray_T classmethods;
! ga_init2(&classmethods, sizeof(ufunc_T *), 10);
// Growarray with object members declared in the class.
garray_T objmembers;
--- 223,231 ----
garray_T classmembers;
ga_init2(&classmembers, sizeof(ocmember_T), 10);
! // Growarray with functions declared in the class.
! garray_T classfunctions;
! ga_init2(&classfunctions, sizeof(ufunc_T *), 10);
// Growarray with object members declared in the class.
garray_T objmembers;
***************
*** 288,293 ****
--- 288,306 ----
}
}
+ int has_static = FALSE;
+ char_u *ps = p;
+ if (checkforcmd(&p, "static", 4))
+ {
+ if (STRNCMP(ps, "static", 6) != 0)
+ {
+ semsg(_(e_command_cannot_be_shortened_str), ps);
+ break;
+ }
+ has_static = TRUE;
+ p = skipwhite(ps + 6);
+ }
+
// object members (public, read access, private):
// "this._varname"
// "this.varname"
***************
*** 314,360 ****
}
}
- // class members and methods
- else if (checkforcmd(&p, "static", 6))
- {
- p = skipwhite(p);
- if (checkforcmd(&p, "def", 3))
- {
- // TODO: class method
- // static def someMethod()
- // enddef
- // static def <Tval> someMethod()
- // enddef
- }
- else
- {
- // class members (public, read access, private):
- // "static _varname"
- // "static varname"
- // "public static varname"
- char_u *varname = p;
- char_u *varname_end = NULL;
- type_T *type = NULL;
- char_u *init_expr = NULL;
- if (parse_member(eap, line, varname, has_public,
- &varname_end, &type_list, &type, &init_expr) == FAIL)
- break;
- if (add_member(&classmembers, varname, varname_end,
- has_public, type, init_expr) == FAIL)
- {
- vim_free(init_expr);
- break;
- }
- }
- }
-
// constructors:
// def new()
// enddef
// def newOther()
// enddef
! // methods:
! // def someMethod()
// enddef
// TODO:
// def <Tval> someMethod()
--- 327,341 ----
}
}
// constructors:
// def new()
// enddef
// def newOther()
// enddef
! // object methods and class functions:
! // def SomeMethod()
! // enddef
! // static def ClassFunction()
// enddef
// TODO:
// def <Tval> someMethod()
***************
*** 364,369 ****
--- 345,352 ----
exarg_T ea;
garray_T lines_to_free;
+ // TODO: error for "public static def Func()"?
+
CLEAR_FIELD(ea);
ea.cmd = line;
ea.arg = p;
***************
*** 376,388 ****
ga_clear_strings(&lines_to_free);
// TODO: how about errors?
! if (uf != NULL && ga_grow(&objmethods, 1) == OK)
{
! if (STRNCMP(uf->uf_name, "new", 3) == 0)
uf->uf_flags |= FC_NEW;
! ((ufunc_T **)objmethods.ga_data)[objmethods.ga_len] = uf;
! ++objmethods.ga_len;
}
}
--- 359,396 ----
ga_clear_strings(&lines_to_free);
// TODO: how about errors?
! int is_new = STRNCMP(uf->uf_name, "new", 3) == 0;
! garray_T *fgap = has_static || is_new
! ? &classfunctions : &objmethods;
! if (uf != NULL && ga_grow(fgap, 1) == OK)
{
! if (is_new)
uf->uf_flags |= FC_NEW;
! ((ufunc_T **)fgap->ga_data)[fgap->ga_len] = uf;
! ++fgap->ga_len;
! }
! }
!
! // class members
! else if (has_static)
! {
! // class members (public, read access, private):
! // "static _varname"
! // "static varname"
! // "public static varname"
! char_u *varname = p;
! char_u *varname_end = NULL;
! type_T *type = NULL;
! char_u *init_expr = NULL;
! if (parse_member(eap, line, varname, has_public,
! &varname_end, &type_list, &type, &init_expr) == FAIL)
! break;
! if (add_member(&classmembers, varname, varname_end,
! has_public, type, init_expr) == FAIL)
! {
! vim_free(init_expr);
! break;
}
}
***************
*** 445,452 ****
}
int have_new = FALSE;
! for (int i = 0; i < objmethods.ga_len; ++i)
! if (STRCMP(((ufunc_T **)objmethods.ga_data)[i]->uf_name,
"new") == 0)
{
have_new = TRUE;
--- 453,460 ----
}
int have_new = FALSE;
! for (int i = 0; i < classfunctions.ga_len; ++i)
! if (STRCMP(((ufunc_T **)classfunctions.ga_data)[i]->uf_name,
"new") == 0)
{
have_new = TRUE;
***************
*** 483,492 ****
ga_clear_strings(&lines_to_free);
vim_free(fga.ga_data);
! if (nf != NULL && ga_grow(&objmethods, 1) == OK)
{
! ((ufunc_T **)objmethods.ga_data)[objmethods.ga_len] = nf;
! ++objmethods.ga_len;
nf->uf_flags |= FC_NEW;
nf->uf_ret_type = get_type_ptr(&type_list);
--- 491,500 ----
ga_clear_strings(&lines_to_free);
vim_free(fga.ga_data);
! if (nf != NULL && ga_grow(&classfunctions, 1) == OK)
{
! ((ufunc_T **)classfunctions.ga_data)[classfunctions.ga_len] =
nf;
! ++classfunctions.ga_len;
nf->uf_flags |= FC_NEW;
nf->uf_ret_type = get_type_ptr(&type_list);
***************
*** 500,520 ****
}
}
! // TODO: class methods
! cl->class_obj_method_count = objmethods.ga_len;
! cl->class_obj_methods = ALLOC_MULT(ufunc_T *, objmethods.ga_len);
! if (cl->class_obj_methods == NULL)
! goto cleanup;
! mch_memmove(cl->class_obj_methods, objmethods.ga_data,
! sizeof(ufunc_T *) * objmethods.ga_len);
! vim_free(objmethods.ga_data);
!
! // Set the class pointer on all the object methods.
! for (int i = 0; i < objmethods.ga_len; ++i)
! {
! ufunc_T *fp = cl->class_obj_methods[i];
! fp->uf_class = cl;
! fp->uf_flags |= FC_OBJECT; // TODO: not for class method
}
cl->class_type.tt_type = VAR_CLASS;
--- 508,542 ----
}
}
! // loop 1: class functions, loop 2: object methods
! for (int loop = 1; loop <= 2; ++loop)
! {
! garray_T *gap = loop == 1 ? &classfunctions : &objmethods;
! int *fcount = loop == 1 ? &cl->class_class_function_count
! : &cl->class_obj_method_count;
! ufunc_T ***fup = loop == 1 ? &cl->class_class_functions
! : &cl->class_obj_methods;
!
! *fcount = gap->ga_len;
! if (gap->ga_len == 0)
! {
! *fup = NULL;
! continue;
! }
! *fup = ALLOC_MULT(ufunc_T *, gap->ga_len);
! if (*fup == NULL)
! goto cleanup;
! mch_memmove(*fup, gap->ga_data, sizeof(ufunc_T *) * gap->ga_len);
! vim_free(gap->ga_data);
!
! // Set the class pointer on all the object methods.
! for (int i = 0; i < gap->ga_len; ++i)
! {
! ufunc_T *fp = (*fup)[i];
! fp->uf_class = cl;
! if (loop == 2)
! fp->uf_flags |= FC_OBJECT;
! }
}
cl->class_type.tt_type = VAR_CLASS;
***************
*** 539,544 ****
--- 561,567 ----
if (cl != NULL)
{
vim_free(cl->class_name);
+ vim_free(cl->class_class_functions);
vim_free(cl->class_obj_members);
vim_free(cl->class_obj_methods);
vim_free(cl);
***************
*** 565,570 ****
--- 588,601 ----
func_clear_free(uf, FALSE);
}
ga_clear(&objmethods);
+
+ for (int i = 0; i < classfunctions.ga_len; ++i)
+ {
+ ufunc_T *uf = ((ufunc_T **)classfunctions.ga_data)[i];
+ func_clear_free(uf, FALSE);
+ }
+ ga_clear(&classfunctions);
+
clear_type_list(&type_list);
}
***************
*** 627,633 ****
/*
* Evaluate what comes after a class:
* - class member: SomeClass.varname
! * - class method: SomeClass.SomeMethod()
* - class constructor: SomeClass.new()
* - object member: someObject.varname
* - object method: someObject.SomeMethod()
--- 658,664 ----
/*
* Evaluate what comes after a class:
* - class member: SomeClass.varname
! * - class function: SomeClass.SomeMethod()
* - class constructor: SomeClass.new()
* - object member: someObject.varname
* - object method: someObject.SomeMethod()
***************
*** 664,672 ****
: rettv->vval.v_object->obj_class;
if (*name_end == '(')
{
! for (int i = 0; i < cl->class_obj_method_count; ++i)
{
! ufunc_T *fp = cl->class_obj_methods[i];
// Use a separate pointer to avoid that ASAN complains about
// uf_name[] only being 4 characters.
char_u *ufname = (char_u *)fp->uf_name;
--- 695,707 ----
: rettv->vval.v_object->obj_class;
if (*name_end == '(')
{
! int on_class = rettv->v_type == VAR_CLASS;
! int count = on_class ? cl->class_class_function_count
! : cl->class_obj_method_count;
! for (int i = 0; i < count; ++i)
{
! ufunc_T *fp = on_class ? cl->class_class_functions[i]
! : cl->class_obj_methods[i];
// Use a separate pointer to avoid that ASAN complains about
// uf_name[] only being 4 characters.
char_u *ufname = (char_u *)fp->uf_name;
***************
*** 805,813 ****
goto fail_after_eval;
len = fname_end - fname;
! for (int i = 0; i < cl->class_obj_method_count; ++i)
{
! ufunc_T *fp = cl->class_obj_methods[i];
// Use a separate pointer to avoid that ASAN complains about
// uf_name[] only being 4 characters.
char_u *ufname = (char_u *)fp->uf_name;
--- 840,852 ----
goto fail_after_eval;
len = fname_end - fname;
! int count = tv.v_type == VAR_CLASS ? cl->class_class_function_count
! : cl->class_obj_method_count;
! ufunc_T **funcs = tv.v_type == VAR_CLASS ? cl->class_class_functions
! : cl->class_obj_methods;
! for (int i = 0; i < count; ++i)
{
! ufunc_T *fp = funcs[i];
// Use a separate pointer to avoid that ASAN complains about
// uf_name[] only being 4 characters.
char_u *ufname = (char_u *)fp->uf_name;
***************
*** 824,829 ****
--- 863,897 ----
}
/*
+ * If "cctx->ctx_ufunc" indicates we are in a class, check if "name" is a
class
+ * member. If it is then return TRUE and set "cl_ret" and "idx_ret".
+ */
+ int
+ class_member_exists(
+ char_u *name,
+ class_T **cl_ret,
+ int *idx_ret,
+ cctx_T *cctx)
+ {
+ if (cctx->ctx_ufunc == NULL || cctx->ctx_ufunc->uf_class == NULL)
+ return FALSE;
+ class_T *cl = cctx->ctx_ufunc->uf_class;
+
+ for (int idx = 0; idx < cl->class_class_member_count; ++idx)
+ {
+ ocmember_T *m = &cl->class_class_members[idx];
+ if (STRCMP(m->ocm_name, name) == 0)
+ {
+ *cl_ret = cl;
+ *idx_ret = idx;
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+ }
+
+ /*
* Make a copy of an object.
*/
void
*** ../vim-9.0.1122/src/proto/vim9class.pro 2022-12-10 18:42:09.090378817
+0000
--- src/proto/vim9class.pro 2023-01-01 12:28:33.305662665 +0000
***************
*** 6,11 ****
--- 6,12 ----
void ex_type(exarg_T *eap);
int class_object_index(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int
verbose);
ufunc_T *find_class_func(char_u **arg);
+ int class_member_exists(char_u *name, class_T **cl_ret, int *idx_ret, cctx_T
*cctx);
void copy_object(typval_T *from, typval_T *to);
void object_unref(object_T *obj);
void copy_class(typval_T *from, typval_T *to);
*** ../vim-9.0.1122/src/structs.h 2022-12-29 20:56:20.021538298 +0000
--- src/structs.h 2022-12-31 20:59:34.665726295 +0000
***************
*** 1493,1501 ****
ocmember_T *class_class_members; // allocated
typval_T *class_members_tv; // allocated array of class member vals
! // class methods: "static def SomeMethod()"
! int class_class_method_count;
! ufunc_T **class_class_methods; // allocated
// object members: "this.varname"
int class_obj_member_count;
--- 1493,1501 ----
ocmember_T *class_class_members; // allocated
typval_T *class_members_tv; // allocated array of class member vals
! // class functions: "static def SomeMethod()"
! int class_class_function_count;
! ufunc_T **class_class_functions; // allocated
// object members: "this.varname"
int class_obj_member_count;
*** ../vim-9.0.1122/src/vim9expr.c 2022-12-29 20:56:20.025538293 +0000
--- src/vim9expr.c 2023-01-01 12:47:25.878330043 +0000
***************
*** 587,593 ****
}
else
{
! lvar_T lvar;
if (lookup_local(*arg, len, &lvar, cctx) == OK)
{
--- 587,594 ----
}
else
{
! lvar_T lvar;
! class_T *cl = NULL;
if (lookup_local(*arg, len, &lvar, cctx) == OK)
{
***************
*** 602,607 ****
--- 603,612 ----
else
gen_load = TRUE;
}
+ else if (class_member_exists(name, &cl, &idx, cctx))
+ {
+ res = generate_CLASSMEMBER(cctx, TRUE, cl, idx);
+ }
else
{
// "var" can be script-local even without using "s:" if it
*** ../vim-9.0.1122/src/testdir/test_vim9_class.vim 2022-12-31
19:00:58.845477088 +0000
--- src/testdir/test_vim9_class.vim 2023-01-01 12:56:33.794406510 +0000
***************
*** 377,383 ****
static _secret = 7
public static anybody = 42
! def AddToCounter(nr: number)
counter += nr
enddef
endclass
--- 377,383 ----
static _secret = 7
public static anybody = 42
! static def AddToCounter(nr: number)
counter += nr
enddef
endclass
***************
*** 402,407 ****
--- 402,433 ----
END
v9.CheckScriptSuccess(lines)
enddef
+
+ def Test_class_function()
+ var lines =<< trim END
+ vim9script
+ class Value
+ this.value = 0
+ static objects = 0
+
+ def new(v: number)
+ this.value = v
+ ++objects
+ enddef
+
+ static def GetCount(): number
+ return objects
+ enddef
+ endclass
+
+ assert_equal(0, Value.GetCount())
+ var v1 = Value.new(2)
+ assert_equal(1, Value.GetCount())
+ var v2 = Value.new(7)
+ assert_equal(2, Value.GetCount())
+ END
+ v9.CheckScriptSuccess(lines)
+ enddef
def Test_class_object_to_string()
var lines =<< trim END
*** ../vim-9.0.1122/src/version.c 2022-12-31 19:00:58.845477088 +0000
--- src/version.c 2023-01-01 12:57:07.734409940 +0000
***************
*** 697,698 ****
--- 697,700 ----
{ /* Add new patch number below this line */
+ /**/
+ 1123,
/**/
--
If you only have a hammer, you tend to see every problem as a nail.
If you only have MS-Windows, you tend to solve every problem by rebooting.
/// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net \\\
/// \\\
\\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ ///
\\\ help me help AIDS victims -- http://ICCF-Holland.org ///
--
--
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php
---
You received this message because you are subscribed to the Google Groups
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/vim_dev/20230101125909.B86881C0AA3%40moolenaar.net.