From 3d0cdb536646b9c20175f44d9c8c6ed2a27b2792 Mon Sep 17 00:00:00 2001 From: hyung-hwan Date: Wed, 2 Oct 2024 00:33:34 +0900 Subject: [PATCH] rewrote compile_fun() to support attribute list for a function --- lib/comp.c | 566 +++++++++++++++++++++++++++-------------------- lib/hcl-prv.h | 15 +- lib/read.c | 28 ++- t/class-5001.err | 30 ++- t/feed-5001.err | 5 + t/fun-01.hcl | 19 +- t/insta-01.hcl | 10 +- t/insta-02.hcl | 20 +- t/retvar-01.hcl | 10 +- t/run.sh | 5 + t/var-01.hcl | 2 +- t/var-5001.err | 6 +- t/var-5004.err | 64 ++++++ 13 files changed, 496 insertions(+), 284 deletions(-) diff --git a/lib/comp.c b/lib/comp.c index e738364..18da41e 100644 --- a/lib/comp.c +++ b/lib/comp.c @@ -473,7 +473,8 @@ static int check_block_expression_as_body (hcl_t* hcl, hcl_cnode_t* c, const hcl { no_block: hcl_setsynerrbfmt ( - hcl, HCL_SYNERR_BLOCK, (car? HCL_CNODE_GET_LOC(car): c? HCL_CNODE_GET_LOC(c): HCL_CNODE_GET_LOC(ctx)), HCL_NULL, + hcl, HCL_SYNERR_BLOCK, + (car? HCL_CNODE_GET_LOC(car): c? HCL_CNODE_GET_LOC(c): HCL_CNODE_GET_LOC(ctx)), HCL_NULL, "block expression expected as '%.*js' body", HCL_CNODE_GET_TOKLEN(ctx), HCL_CNODE_GET_TOKPTR(ctx) ); return -1; @@ -534,7 +535,8 @@ static int check_block_expression_as_body (hcl_t* hcl, hcl_cnode_t* c, const hcl hcl_setsynerrbfmt ( hcl, HCL_SYNERR_BANNED, HCL_CNODE_GET_LOC(cdr), HCL_NULL, - "redundant expression prohibited after '%.*js' body", HCL_CNODE_GET_TOKLEN(ctx), HCL_CNODE_GET_TOKPTR(ctx) + "redundant expression prohibited after '%.*js' body", + HCL_CNODE_GET_TOKLEN(ctx), HCL_CNODE_GET_TOKPTR(ctx) ); return -1; } @@ -2639,7 +2641,8 @@ static int compile_class (hcl_t* hcl, hcl_cnode_t* src, int defclass) } else { - if (emit_byte_instruction(hcl, HCL_CODE_PUSH_NIL,HCL_CNODE_GET_LOC(cmd)) <= -1) return -1; /* push nil for class name of an anonymous class */ + /* push nil for class name of an anonymous class */ + if (emit_byte_instruction(hcl, HCL_CODE_PUSH_NIL, HCL_CNODE_GET_LOC(cmd)) <= -1) return -1; } POP_CFRAME (hcl); @@ -2814,35 +2817,109 @@ static HCL_INLINE int compile_class_p2 (hcl_t* hcl) /* ========================================================================= */ +static int check_fun_attr_list (hcl_t* hcl, hcl_cnode_t* attr_list, unsigned int* fun_type) +{ + unsigned int ft; + + ft = 0; + + HCL_ASSERT (hcl, attr_list != HCL_NULL); + HCL_ASSERT (hcl, HCL_CNODE_IS_CONS_CONCODED(attr_list, HCL_CONCODE_XLIST) || + HCL_CNODE_IS_ELIST_CONCODED(attr_list, HCL_CONCODE_XLIST)); + + if (HCL_CNODE_IS_CONS(attr_list)) + { + hcl_cnode_t* c, * a; + const hcl_ooch_t* tokptr; + hcl_oow_t toklen; + + c = attr_list; + while (c) + { + a = HCL_CNODE_CONS_CAR(c); + + tokptr = HCL_CNODE_GET_TOKPTR(a); + toklen = HCL_CNODE_GET_TOKLEN(a); + + if (!HCL_CNODE_IS_TYPED(a, HCL_CNODE_SYMLIT)) + { + hcl_setsynerrbfmt ( + hcl, HCL_SYNERR_FUN, HCL_CNODE_GET_LOC(a), HCL_NULL, + "invalid function attribute name '%.*js'", toklen, tokptr); + return -1; + } + + if (hcl_comp_oochars_bcstr(tokptr, toklen, "class") == 0 || + hcl_comp_oochars_bcstr(tokptr, toklen, "c") == 0) + { + if (ft != 0) + { + conflicting: + hcl_setsynerrbfmt ( + hcl, HCL_SYNERR_FUN, HCL_CNODE_GET_LOC(a), HCL_NULL, + "conflicting function attribute name '%.*js'", toklen, tokptr); + return -1; + } + ft = FUN_CM; + } + else if (hcl_comp_oochars_bcstr(tokptr, toklen, "classinst") == 0 || + hcl_comp_oochars_bcstr(tokptr, toklen, "ci") == 0) + { + if (ft != 0) goto conflicting; + ft = FUN_CIM; + } + else + { + hcl_setsynerrbfmt ( + hcl, HCL_SYNERR_FUN, HCL_CNODE_GET_LOC(a), HCL_NULL, + "unrecognized function attribute name '%.*js'", toklen, tokptr); + return -1; + } + + c = HCL_CNODE_CONS_CDR(c); + } + } + + *fun_type = ft; + return 0; +} + static int compile_fun (hcl_t* hcl, hcl_cnode_t* src) { - hcl_cnode_t* cmd, * obj, * args; + hcl_cnode_t* cmd, * next; hcl_oow_t va, nargs, nrvars, nlvars; hcl_ooi_t jump_inst_pos, lfbase_pos, lfsize_pos; hcl_oow_t saved_tv_wcount, tv_dup_start; hcl_cnode_t* fun_name; hcl_cnode_t* class_name; + hcl_cnode_t* arg_list; + hcl_cnode_t* fun_body; hcl_cframe_t* cf; - unsigned int fun_type = FUN_PLAIN; - int named = 0; + unsigned int fun_type; HCL_ASSERT (hcl, HCL_CNODE_IS_CONS(src)); saved_tv_wcount = hcl->c->tv.wcount; cmd = HCL_CNODE_CONS_CAR(src); - obj = HCL_CNODE_CONS_CDR(src); + next = HCL_CNODE_CONS_CDR(src); + fun_name = HCL_NULL; class_name = HCL_NULL; + arg_list = HCL_NULL; + fun_body = HCL_NULL; + fun_type = FUN_PLAIN; HCL_ASSERT (hcl, HCL_CNODE_IS_SYMBOL_SYNCODED(cmd, HCL_SYNCODE_FUN) || HCL_CNODE_IS_TYPED(cmd, HCL_CNODE_FUN)); - if (obj) + if (next) { - hcl_cnode_t* tmp, * next; + hcl_cnode_t* tmp; + hcl_cnode_t* attr_list; /* the reader ensures that the cdr field of a cons cell points to the next cell. * and only the field of the last cons cell is NULL. */ - HCL_ASSERT (hcl, HCL_CNODE_IS_CONS(obj)); + HCL_ASSERT (hcl, HCL_CNODE_IS_CONS(next)); + attr_list = HCL_NULL; /* fun (arg..) * fun name (arg..) @@ -2850,12 +2927,12 @@ static int compile_fun (hcl_t* hcl, hcl_cnode_t* src) * fun(#attr..) (arg..) * fun(#attr..) class:name(arg..) */ - next = obj; + tmp = HCL_CNODE_CONS_CAR(next); if (HCL_CNODE_IS_SYMBOL_PLAIN(tmp)) { /* 'fun' followed by name */ - got_name: + fun_got_name: /* name must be followed by argument list */ fun_name = tmp; @@ -2863,10 +2940,10 @@ static int compile_fun (hcl_t* hcl, hcl_cnode_t* src) if (!next) { hcl_setsynerrbfmt ( - hcl, HCL_SYNERR_FUN, HCL_CNODE_GET_LOC(cmd), HCL_NULL, - "'%.*js' name '%.*js' not followed by ( or :", - HCL_CNODE_GET_TOKLEN(cmd), HCL_CNODE_GET_TOKPTR(cmd), - HCL_CNODE_GET_TOKLEN(fun_name), HCL_CNODE_GET_TOKPTR(fun_name)); + hcl, HCL_SYNERR_FUN, HCL_CNODE_GET_LOC(fun_name), HCL_NULL, + "function name '%.*js' not followed by ( or : for '%.*js'", + HCL_CNODE_GET_TOKLEN(fun_name), HCL_CNODE_GET_TOKPTR(fun_name), + HCL_CNODE_GET_TOKLEN(cmd), HCL_CNODE_GET_TOKPTR(cmd)); return -1; } @@ -2881,10 +2958,10 @@ static int compile_fun (hcl_t* hcl, hcl_cnode_t* src) if (!next) { hcl_setsynerrbfmt ( - hcl, HCL_SYNERR_FUN, HCL_CNODE_GET_LOC(cmd), HCL_NULL, - "no '%.*js' name after class name '%.*js' and :", - HCL_CNODE_GET_TOKLEN(cmd), HCL_CNODE_GET_TOKPTR(cmd), - HCL_CNODE_GET_TOKLEN(class_name), HCL_CNODE_GET_TOKPTR(class_name)); + hcl, HCL_SYNERR_FUN, HCL_CNODE_GET_LOC(class_name), HCL_NULL, + "no function name after class name '%.*js:' for '%.*js'", + HCL_CNODE_GET_TOKLEN(class_name), HCL_CNODE_GET_TOKPTR(class_name), + HCL_CNODE_GET_TOKLEN(cmd), HCL_CNODE_GET_TOKPTR(cmd)); return -1; } @@ -2892,54 +2969,187 @@ static int compile_fun (hcl_t* hcl, hcl_cnode_t* src) if (!HCL_CNODE_IS_SYMBOL_PLAIN(tmp)) { hcl_setsynerrbfmt ( - hcl, HCL_SYNERR_FUN, HCL_CNODE_GET_LOC(cmd), HCL_NULL, - "invalid '%.*js' name '%.*js' after class name and :", - HCL_CNODE_GET_TOKLEN(cmd), HCL_CNODE_GET_TOKPTR(cmd), - HCL_CNODE_GET_TOKLEN(tmp), HCL_CNODE_GET_TOKPTR(tmp)); + hcl, HCL_SYNERR_FUN, HCL_CNODE_GET_LOC(tmp), HCL_NULL, + "invalid function name '%.*js' after '%.*js:' for '%.*js'", + HCL_CNODE_GET_TOKLEN(tmp), HCL_CNODE_GET_TOKPTR(tmp), + HCL_CNODE_GET_TOKLEN(class_name), HCL_CNODE_GET_TOKPTR(class_name), + HCL_CNODE_GET_TOKLEN(cmd), HCL_CNODE_GET_TOKPTR(cmd)); return -1; } fun_name = tmp; + fun_type = FUN_IM; + if (attr_list && check_fun_attr_list(hcl, attr_list, &fun_type) <= -1) return -1; + fun_type |= 0x100; /* indicate that the function was defined in 'fun class:name()' style */ + + next = HCL_CNODE_CONS_CDR(next); + if (!next) + { + hcl_setsynerrbfmt ( + hcl, HCL_SYNERR_FUN, HCL_CNODE_GET_LOC(fun_name), HCL_NULL, + "function name '%.*js:%.*js' not followed by ( for '%.*js'", + HCL_CNODE_GET_TOKLEN(class_name), HCL_CNODE_GET_TOKPTR(class_name), + HCL_CNODE_GET_TOKLEN(fun_name), HCL_CNODE_GET_TOKPTR(fun_name), + HCL_CNODE_GET_TOKLEN(cmd), HCL_CNODE_GET_TOKPTR(cmd)); + return -1; + } + + tmp = HCL_CNODE_CONS_CAR(next); /* pointing to argument list */ + + if (is_in_class_init_scope(hcl)) + { + hcl_setsynerrbfmt ( + hcl, HCL_SYNERR_FUN, HCL_CNODE_GET_LOC(fun_name), HCL_NULL, + "class name '%.*js' before :'%.*js' prohibited in class initialization context", + HCL_CNODE_GET_TOKLEN(class_name), HCL_CNODE_GET_TOKPTR(class_name), + HCL_CNODE_GET_TOKLEN(fun_name), HCL_CNODE_GET_TOKPTR(fun_name)); + return -1; + } + } + else + { + /* no 'class:' part after 'fun' */ + if (is_in_class_init_scope(hcl)) + { + /* TODO:THIS IS ALSO WRONG. + * class X { + * a := (fun x(){}) ## this context is also class_init_scope. so the check above isn't good enough + * } */ + fun_type = FUN_IM; + if (attr_list && check_fun_attr_list(hcl, attr_list, &fun_type) <= -1) return -1; + } + else + { + /* as of now, the plain function doesn't support attribute list. + * this can change in the future. */ + if (attr_list) + { + hcl_setsynerrbfmt ( + hcl, HCL_SYNERR_FUN, HCL_CNODE_GET_LOC(attr_list), HCL_NULL, + "unsupported attribute list for plain function '%.*js'", + HCL_CNODE_GET_TOKLEN(fun_name), HCL_CNODE_GET_TOKPTR(fun_name)); + return -1; + } + } } } - else if (HCL_CNODE_IS_CONS(tmp)) + + if (HCL_CNODE_IS_CONS_CONCODED(tmp, HCL_CONCODE_XLIST) || + HCL_CNODE_IS_ELIST_CONCODED(tmp, HCL_CONCODE_XLIST)) { /* 'fun' followed by attribute or argument list */ - next = HCL_CNODE_CONS_CDR(next); - if (!next) + arg_list = tmp; + if (fun_name) { + /* argument list for sure + * fun class:name() + * fun name () + * ^ */ + next = HCL_CNODE_CONS_CDR(next); /* point past argument list */ + if (!next) + { + hcl_setsynerrbfmt ( + hcl, HCL_SYNERR_FUN, HCL_CNODE_GET_LOC(cmd), HCL_NULL, + "no function body after argument list of function '%.*js' for '%.*js'", + HCL_CNODE_GET_TOKLEN(fun_name), HCL_CNODE_GET_TOKPTR(fun_name), + HCL_CNODE_GET_TOKLEN(cmd), HCL_CNODE_GET_TOKPTR(cmd)); + return -1; + } + fun_body = next; + } + else + { + /* not clear if it is attribute list or argument list */ + next = HCL_CNODE_CONS_CDR(next); /* point past attribute/argument list */ + if (!next) + { + /* TODO: guess if the current list looks like attribute list or + * not by inspecting elements and produce better error mesage. + * another hack is to disallow ELIST as attribute list? */ + hcl_setsynerrbfmt ( + hcl, HCL_SYNERR_FUN, HCL_CNODE_GET_LOC(cmd), HCL_NULL, + "unamed function not followed by function body for '%.*js'", + HCL_CNODE_GET_TOKLEN(cmd), HCL_CNODE_GET_TOKPTR(cmd)); + return -1; + } + + tmp = HCL_CNODE_CONS_CAR(next); + if (HCL_CNODE_IS_SYMBOL_PLAIN(tmp)) + { + /* it is attribute list for sure. fun(#attr..) name */ + attr_list = arg_list; + arg_list = HCL_NULL; + goto fun_got_name; + } + else if (HCL_CNODE_IS_CONS_CONCODED(tmp, HCL_CONCODE_XLIST) || + HCL_CNODE_IS_ELIST_CONCODED(tmp, HCL_CONCODE_XLIST)) + { + /* fun(#attr..) (arg..) .. */ + attr_list = arg_list; + arg_list = tmp; + + next = HCL_CNODE_CONS_CDR(next); /* point past argument list */ + if (!next) + { + hcl_setsynerrbfmt ( + hcl, HCL_SYNERR_FUN, HCL_CNODE_GET_LOC(cmd), HCL_NULL, + "no function body after attribute list and argument list of unamed function for '%.*js'", + HCL_CNODE_GET_TOKLEN(cmd), HCL_CNODE_GET_TOKPTR(cmd)); + return -1; + } + fun_body = next; + } + else + { + /* fun(arg..) .. */ + fun_body = next; + } + } + + if (!fun_name && is_in_class_init_scope(hcl)) + { + /* TODO: it must allow as rvalue.. + * class X { + * b := (fun() {}) ## this is allowed <- TODO: not supported yet + * fun() {} ## this is prohibited + * } + */ hcl_setsynerrbfmt ( hcl, HCL_SYNERR_FUN, HCL_CNODE_GET_LOC(cmd), HCL_NULL, - "'%.*js' not defined with body", - HCL_CNODE_GET_TOKLEN(cmd), HCL_CNODE_GET_TOKPTR(cmd), - HCL_CNODE_GET_TOKLEN(tmp), HCL_CNODE_GET_TOKPTR(tmp)); + "unnamed function defined with '%.*js' prohibited in class initialziation context", + HCL_CNODE_GET_TOKLEN(cmd), HCL_CNODE_GET_TOKPTR(cmd)); return -1; } - - tmp = HCL_CNODE_CONS_CAR(next); - if (HCL_CNODE_IS_SYMBOL_PLAIN(tmp)) - { - /* fun(#attr..) name */ - goto got_name; - } - else if (HCL_CNODE_IS_CONS(tmp)) - { - /* fun(#attr..) (arg..) */ - } - - /* fall down to handle the function body */ } else { - hcl_setsynerrbfmt ( - hcl, HCL_SYNERR_FUN, HCL_CNODE_GET_LOC(cmd), HCL_NULL, - "'%.*js' not followed by name or (, but followed by '%.*js'", - HCL_CNODE_GET_TOKLEN(cmd), HCL_CNODE_GET_TOKPTR(cmd), - HCL_CNODE_GET_TOKLEN(tmp), HCL_CNODE_GET_TOKPTR(tmp)); + /* not valid argument list or attribute list + * fun if ## cmd is 'fun', fun_name is nil + * fun a if ## cmd is 'fun', fun_name is 'a' + * fun a:b if ## cmd is 'fun', fun_name is 'b' + * fun self.a ## cmd is 'fun', fun_name is nil + */ + if (fun_name) + { + hcl_setsynerrbfmt ( + hcl, HCL_SYNERR_FUN, HCL_CNODE_GET_LOC(tmp), HCL_NULL, + "'%.*js' not followed by ( but followed by '%.*js'", + HCL_CNODE_GET_TOKLEN(fun_name), HCL_CNODE_GET_TOKPTR(fun_name), + HCL_CNODE_GET_TOKLEN(tmp), HCL_CNODE_GET_TOKPTR(tmp)); + } + else + { + hcl_setsynerrbfmt ( + hcl, HCL_SYNERR_FUN, HCL_CNODE_GET_LOC(tmp), HCL_NULL, + "invalid function name '%.*js' for '%.*js'", + HCL_CNODE_GET_TOKLEN(tmp), HCL_CNODE_GET_TOKPTR(tmp), + HCL_CNODE_GET_TOKLEN(cmd), HCL_CNODE_GET_TOKPTR(cmd)); + } return -1; } } else { + /* nothing after 'fun' (e.g. fun ) */ hcl_setsynerrbfmt ( hcl, HCL_SYNERR_FUN, HCL_CNODE_GET_LOC(cmd), HCL_NULL, "'%.*js' not followed by name or (", @@ -2947,180 +3157,17 @@ static int compile_fun (hcl_t* hcl, hcl_cnode_t* src) return -1; } - if (obj && HCL_CNODE_IS_CONS(obj)) - { - /* inaccurate pre-check if 'fun' is followed by an argument list - * without a function name. */ - args = HCL_CNODE_CONS_CAR(obj); - if (!HCL_CNODE_IS_ELIST_CONCODED(args, HCL_CONCODE_XLIST) && - !HCL_CNODE_IS_CONS_CONCODED(args, HCL_CONCODE_XLIST)) - { - /* not followed by an argument list */ - named = 1; - goto named_function; - } - } - - if (named) - { - named_function: - if (!obj) - { - hcl_setsynerrbfmt (hcl, HCL_SYNERR_ARGNAMELIST, HCL_CNODE_GET_LOC(src), HCL_NULL, "no name in %.*js", HCL_CNODE_GET_TOKLEN(cmd), HCL_CNODE_GET_TOKPTR(cmd)); - return -1; - } - else if (!HCL_CNODE_IS_CONS(obj)) - { - hcl_setsynerrbfmt (hcl, HCL_SYNERR_DOTBANNED, HCL_CNODE_GET_LOC(obj), HCL_CNODE_GET_TOK(obj), "redundant cdr in %.*js", HCL_CNODE_GET_TOKLEN(cmd), HCL_CNODE_GET_TOKPTR(cmd)); - return -1; - } - - fun_name = HCL_CNODE_CONS_CAR(obj); - if (is_in_class_init_scope(hcl)) - { - if ((HCL_CNODE_IS_DBLCOLONS(fun_name) || HCL_CNODE_IS_COLONSTAR(fun_name))) - { - /* class method - (fun ::xxxx () ...) inside class definition */ - /* class instantiation method - (fun :*xxxx() ...) inside class definition */ - obj = HCL_CNODE_CONS_CDR(obj); - if (!obj) - { - hcl_setsynerrbfmt (hcl, HCL_SYNERR_ARGNAMELIST, HCL_CNODE_GET_LOC(src), HCL_NULL, "no name in %.*js", HCL_CNODE_GET_TOKLEN(cmd), HCL_CNODE_GET_TOKPTR(cmd)); - return -1; - } - - fun_type = HCL_CNODE_IS_DBLCOLONS(fun_name)? FUN_CM: FUN_CIM; - fun_name = HCL_CNODE_CONS_CAR(obj); /* advance to the actual name */ - } - else - { - if (HCL_CNODE_IS_SYMBOL_PLAIN(fun_name)) - { - /* probably this form - fun XXX:yyy () ... - * the class name must not be specified in the class initialization scope */ - hcl_cnode_t* tmp; - tmp = HCL_CNODE_CONS_CDR(obj); - if (tmp && HCL_CNODE_IS_CONS(tmp)) - { - tmp = HCL_CNODE_CONS_CAR(tmp); - if (HCL_CNODE_IS_COLON(tmp) || HCL_CNODE_IS_DBLCOLONS(tmp) || HCL_CNODE_IS_COLONSTAR(tmp)/*(HCL_CNODE_IS_SYMBOL_PLAIN(tmp)*/) - { - hcl_setsynerrbfmt ( - hcl, HCL_SYNERR_VARNAME, - HCL_CNODE_GET_LOC(fun_name), HCL_CNODE_GET_TOK(fun_name), - "function name not valid followed by %.*js", - HCL_CNODE_GET_TOKLEN(tmp), HCL_CNODE_GET_TOKPTR(tmp)); - return -1; - } - } - } - - fun_type = FUN_IM; - } - } - else if (HCL_CNODE_IS_SYMBOL_PLAIN(fun_name)) - { - hcl_cnode_t* tmp, marker; - tmp = HCL_CNODE_CONS_CDR(obj); - if (tmp && HCL_CNODE_IS_CONS(tmp)) - { - hcl_cnode_t* marker; - marker = HCL_CNODE_CONS_CAR(tmp); - if (HCL_CNODE_IS_COLON(marker) || HCL_CNODE_IS_DBLCOLONS(marker) || HCL_CNODE_IS_COLONSTAR(marker)) - { - /* fun A:aaa A::aaa A:*aaa */ - - tmp = HCL_CNODE_CONS_CDR(tmp); - if (tmp && HCL_CNODE_IS_CONS(tmp)) - { - hcl_cnode_t* cand; - cand = HCL_CNODE_CONS_CAR(tmp); - if (HCL_CNODE_IS_SYMBOL_PLAIN(cand)) - { - /* out-of-class method definition - * for fun String:length() { ... }, - * class_name is String, fun_name is length. */ - fun_type = HCL_CNODE_IS_DBLCOLONS(marker)? FUN_CM: - HCL_CNODE_IS_COLONSTAR(marker)? FUN_CIM: FUN_IM; - - /* indicates that this method is defined using the AAA:bbb syntax. - * the form of method defintion can still be inside a class if this - * form is place inside another normal method. - * class X { - * fun x() { - * fun J:q() { .... } ## this defintion - * } - * } - * */ - fun_type |= 0x100; - - class_name = fun_name; - fun_name = HCL_CNODE_CONS_CAR(tmp); - obj = tmp; - } - } - } - } - } - - if (!HCL_CNODE_IS_SYMBOL(fun_name)) - { - hcl_setsynerrbfmt (hcl, HCL_SYNERR_VARNAME, - HCL_CNODE_GET_LOC(fun_name), HCL_NULL, - "invalid function name '%.*js' for '%.*js'", - HCL_CNODE_GET_TOKLEN(fun_name), HCL_CNODE_GET_TOKPTR(fun_name), - HCL_CNODE_GET_TOKLEN(cmd), HCL_CNODE_GET_TOKPTR(cmd)); - return -1; - } - - if (HCL_CNODE_SYMBOL_SYNCODE(fun_name)) /*|| HCL_OBJ_GET_FLAGS_KERNEL(fun_name) >= 1) */ - { - hcl_setsynerrbfmt (hcl, HCL_SYNERR_BANNEDVARNAME, - HCL_CNODE_GET_LOC(fun_name), HCL_NULL, - "special symbol '%.*js' not to be used as function name", - HCL_CNODE_GET_TOKLEN(fun_name), HCL_CNODE_GET_TOKPTR(fun_name)); - return -1; - } - - obj = HCL_CNODE_CONS_CDR(obj); - } - else - { - HCL_ASSERT (hcl, HCL_CNODE_IS_SYMBOL_SYNCODED(cmd, HCL_SYNCODE_FUN) || - HCL_CNODE_IS_TYPED(cmd, HCL_CNODE_FUN)); - fun_name = HCL_NULL; - } - - if (!obj) - { - no_arg_list: - hcl_setsynerrbfmt (hcl, HCL_SYNERR_ARGNAMELIST, HCL_CNODE_GET_LOC(src), HCL_NULL, - "argument list missing in %.*js", - HCL_CNODE_GET_TOKLEN(cmd), HCL_CNODE_GET_TOKPTR(cmd)); - return -1; - } - else if (!HCL_CNODE_IS_CONS(obj)) - { - redundant_cdr: - hcl_setsynerrbfmt (hcl, HCL_SYNERR_DOTBANNED, HCL_CNODE_GET_LOC(obj), HCL_CNODE_GET_TOK(obj), "redundant cdr in argument list in %.*js", HCL_CNODE_GET_TOKLEN(cmd), HCL_CNODE_GET_TOKPTR(cmd)); - return -1; - } - /* process the argument list */ va = 0; nargs = 0; nrvars = 0; - args = HCL_CNODE_CONS_CAR(obj); - HCL_ASSERT (hcl, args != HCL_NULL); - if (HCL_CNODE_IS_ELIST_CONCODED(args, HCL_CONCODE_XLIST)) + HCL_ASSERT (hcl, HCL_CNODE_IS_ELIST_CONCODED(arg_list, HCL_CONCODE_XLIST) || + HCL_CNODE_IS_CONS_CONCODED(arg_list, HCL_CONCODE_XLIST)); + if (HCL_CNODE_IS_ELIST_CONCODED(arg_list, HCL_CONCODE_XLIST)) { - /* empty list - no argument - (fun () (+ 10 20)) */ - } - else if (!HCL_CNODE_IS_CONS_CONCODED(args, HCL_CONCODE_XLIST)) - { - hcl_setsynerrbfmt (hcl, HCL_SYNERR_ARGNAMELIST, HCL_CNODE_GET_LOC(args), HCL_CNODE_GET_TOK(args), "no argument list in %.*js", HCL_CNODE_GET_TOKLEN(cmd), HCL_CNODE_GET_TOKPTR(cmd)); - return -1; + /* empty list - no argument - fun () {+ 10 20} */ + /* do nothing */ } else { @@ -3128,22 +3175,33 @@ static int compile_fun (hcl_t* hcl, hcl_cnode_t* src) int in_ret_args = 0; tv_dup_start = hcl->c->tv.s.len; - dcl = args; + dcl = arg_list; do { arg = HCL_CNODE_CONS_CAR(dcl); if (in_ret_args) { - if (!HCL_CNODE_IS_SYMBOL(arg)) + if (!HCL_CNODE_IS_SYMBOL_PLAIN_IDENT(arg)) { - hcl_setsynerrbfmt (hcl, HCL_SYNERR_VARNAME, HCL_CNODE_GET_LOC(arg), HCL_CNODE_GET_TOK(arg), "return variable not symbol in %.*js", HCL_CNODE_GET_TOKLEN(cmd), HCL_CNODE_GET_TOKPTR(cmd)); + /* in 'fun x (x :: 20) { }', '20' is not a valid return variable name. + * in 'fun x (x :: if) { }', 'if' is not a valid return variable name. */ + hcl_setsynerrbfmt ( + hcl, HCL_SYNERR_VARNAME, HCL_CNODE_GET_LOC(arg), HCL_NULL, + "invalid return variable '%.*js' for '%.*js'", + HCL_CNODE_GET_TOKLEN(arg), HCL_CNODE_GET_TOKPTR(arg), + HCL_CNODE_GET_TOKLEN(cmd), HCL_CNODE_GET_TOKPTR(cmd)); return -1; } + /* TODO: delete this error check once SYNCODE stuffs are all deleted */ if (HCL_CNODE_IS_SYMBOL(arg) && HCL_CNODE_SYMBOL_SYNCODE(arg) /* || HCL_OBJ_GET_FLAGS_KERNEL(arg) >= 2 */) { - hcl_setsynerrbfmt (hcl, HCL_SYNERR_BANNEDVARNAME, HCL_CNODE_GET_LOC(arg), HCL_CNODE_GET_TOK(arg), "special symbol not to be declared as return variable in %.*js", HCL_CNODE_GET_TOKLEN(cmd), HCL_CNODE_GET_TOKPTR(cmd)); + hcl_setsynerrbfmt ( + hcl, HCL_SYNERR_BANNEDVARNAME, HCL_CNODE_GET_LOC(arg), HCL_NULL, + "special symbol '%.*js' used as return variable for '%.*js'", + HCL_CNODE_GET_TOKLEN(arg), HCL_CNODE_GET_TOKPTR(arg), + HCL_CNODE_GET_TOKLEN(cmd), HCL_CNODE_GET_TOKPTR(cmd)); return -1; } @@ -3151,7 +3209,12 @@ static int compile_fun (hcl_t* hcl, hcl_cnode_t* src) { if (hcl->errnum == HCL_EEXIST) { - hcl_setsynerrbfmt (hcl, HCL_SYNERR_VARNAMEDUP, HCL_CNODE_GET_LOC(arg), HCL_CNODE_GET_TOK(arg), "return variable duplicate in %.*js", HCL_CNODE_GET_TOKLEN(cmd), HCL_CNODE_GET_TOKPTR(cmd)); + /* in 'fun x (y :: z z) {}', the second 'z' is duplicate */ + hcl_setsynerrbfmt ( + hcl, HCL_SYNERR_VARNAMEDUP, HCL_CNODE_GET_LOC(arg), HCL_NULL, + "duplicate return variable '%.*js' for '%.*js'", + HCL_CNODE_GET_TOKLEN(arg), HCL_CNODE_GET_TOKPTR(arg), + HCL_CNODE_GET_TOKLEN(cmd), HCL_CNODE_GET_TOKPTR(cmd)); } return -1; } @@ -3165,7 +3228,13 @@ static int compile_fun (hcl_t* hcl, hcl_cnode_t* src) } else { - hcl_setsynerrbfmt (hcl, HCL_SYNERR_CNODE, HCL_CNODE_GET_LOC(arg), HCL_CNODE_GET_TOK(arg), "unexpected element in %.*js", HCL_CNODE_GET_TOKLEN(cmd), HCL_CNODE_GET_TOKPTR(cmd)); + /* in 'fun x (... a) {}', 'a' is an unexpected token. + * only ')' or '::' can follow ... */ + hcl_setsynerrbfmt ( + hcl, HCL_SYNERR_CNODE, HCL_CNODE_GET_LOC(arg), HCL_NULL, + "unexpected token '%.*js' after '...' for '%.*js'", + HCL_CNODE_GET_TOKLEN(arg), HCL_CNODE_GET_TOKPTR(arg), + HCL_CNODE_GET_TOKLEN(cmd), HCL_CNODE_GET_TOKPTR(cmd)); return -1; } } @@ -3179,16 +3248,25 @@ static int compile_fun (hcl_t* hcl, hcl_cnode_t* src) { va = 1; } - else if (!HCL_CNODE_IS_SYMBOL(arg)) + else if (!HCL_CNODE_IS_SYMBOL_PLAIN_IDENT(arg)) { - hcl_setsynerrbfmt (hcl, HCL_SYNERR_ARGNAME, HCL_CNODE_GET_LOC(arg), HCL_CNODE_GET_TOK(arg), "argument not symbol in %.*js", HCL_CNODE_GET_TOKLEN(cmd), HCL_CNODE_GET_TOKPTR(cmd)); + hcl_setsynerrbfmt ( + hcl, HCL_SYNERR_ARGNAME, HCL_CNODE_GET_LOC(arg), HCL_NULL, + "invalid argument name '%.*js' for '%.*js'", + HCL_CNODE_GET_TOKLEN(arg), HCL_CNODE_GET_TOKPTR(arg), + HCL_CNODE_GET_TOKLEN(cmd), HCL_CNODE_GET_TOKPTR(cmd)); return -1; } else { + /* TODO: delete this error check once SYNCODE stuffs are all deleted */ if (HCL_CNODE_IS_SYMBOL(arg) && HCL_CNODE_SYMBOL_SYNCODE(arg) /* || HCL_OBJ_GET_FLAGS_KERNEL(arg) >= 2 */) { - hcl_setsynerrbfmt (hcl, HCL_SYNERR_BANNEDARGNAME, HCL_CNODE_GET_LOC(arg), HCL_CNODE_GET_TOK(arg), "special symbol not to be declared as argument in %.*js", HCL_CNODE_GET_TOKLEN(cmd), HCL_CNODE_GET_TOKPTR(cmd)); + hcl_setsynerrbfmt ( + hcl, HCL_SYNERR_BANNEDARGNAME, HCL_CNODE_GET_LOC(arg), HCL_NULL, + "special symbol '%.*js' used as argument name for '%.*js'", + HCL_CNODE_GET_TOKLEN(arg), HCL_CNODE_GET_TOKPTR(arg), + HCL_CNODE_GET_TOKLEN(cmd), HCL_CNODE_GET_TOKPTR(cmd)); return -1; } @@ -3196,7 +3274,11 @@ static int compile_fun (hcl_t* hcl, hcl_cnode_t* src) { if (hcl->errnum == HCL_EEXIST) { - hcl_setsynerrbfmt (hcl, HCL_SYNERR_ARGNAMEDUP, HCL_CNODE_GET_LOC(arg), HCL_CNODE_GET_TOK(arg), "argument duplicate in %.*js", HCL_CNODE_GET_TOKLEN(cmd), HCL_CNODE_GET_TOKPTR(cmd)); + hcl_setsynerrbfmt ( + hcl, HCL_SYNERR_ARGNAMEDUP, HCL_CNODE_GET_LOC(arg), HCL_NULL, + "duplicate argument name '%.*js' for '%.*js'", + HCL_CNODE_GET_TOKLEN(arg), HCL_CNODE_GET_TOKPTR(arg), + HCL_CNODE_GET_TOKLEN(cmd), HCL_CNODE_GET_TOKPTR(cmd)); } return -1; } @@ -3209,7 +3291,8 @@ static int compile_fun (hcl_t* hcl, hcl_cnode_t* src) if (!HCL_CNODE_IS_CONS(dcl)) { - hcl_setsynerrbfmt (hcl, HCL_SYNERR_DOTBANNED, HCL_CNODE_GET_LOC(dcl), HCL_CNODE_GET_TOK(dcl), "redundant cdr in argument list in %.*js", HCL_CNODE_GET_TOKLEN(cmd), HCL_CNODE_GET_TOKPTR(cmd)); + hcl_setsynerrbfmt (hcl, HCL_SYNERR_DOTBANNED, HCL_CNODE_GET_LOC(dcl), HCL_CNODE_GET_TOK(dcl), + "redundant cdr in argument list in %.*js", HCL_CNODE_GET_TOKLEN(cmd), HCL_CNODE_GET_TOKPTR(cmd)); return -1; } } @@ -3222,18 +3305,23 @@ static int compile_fun (hcl_t* hcl, hcl_cnode_t* src) * block arguments, evaluation which is done by message passing * limits the number of arguments that can be passed. so the * check is implemented */ - hcl_setsynerrbfmt (hcl, HCL_SYNERR_ARGFLOOD, HCL_CNODE_GET_LOC(args), HCL_NULL, "too many(%zu) arguments in %.*js", nargs, HCL_CNODE_GET_TOKLEN(cmd), HCL_CNODE_GET_TOKPTR(cmd)); + hcl_setsynerrbfmt ( + hcl, HCL_SYNERR_ARGFLOOD, HCL_CNODE_GET_LOC(arg_list), HCL_NULL, + "too many(%zu) arguments in %.*js", nargs, + HCL_CNODE_GET_TOKLEN(cmd), HCL_CNODE_GET_TOKPTR(cmd)); return -1; } if (nrvars > MAX_CODE_NBLKLVARS) { - hcl_setsynerrbfmt (hcl, HCL_SYNERR_VARFLOOD, HCL_CNODE_GET_LOC(args), HCL_NULL, "too many(%zu) return variables in %.*js", nrvars, HCL_CNODE_GET_TOKLEN(cmd), HCL_CNODE_GET_TOKPTR(cmd)); + hcl_setsynerrbfmt ( + hcl, HCL_SYNERR_VARFLOOD, HCL_CNODE_GET_LOC(arg_list), HCL_NULL, + "too many(%zu) return variables in %.*js", nrvars, + HCL_CNODE_GET_TOKLEN(cmd), HCL_CNODE_GET_TOKPTR(cmd)); return -1; } HCL_ASSERT (hcl, nargs + nrvars == hcl->c->tv.wcount - saved_tv_wcount); - /* * fun aa(a b) { ... }; * (fun aa(a b) { ... }) @@ -3242,10 +3330,9 @@ static int compile_fun (hcl_t* hcl, hcl_cnode_t* src) * the variable declaration can't be placed before the block expression. * it is supported inside the block expression itself. */ - hcl_cnode_t* blk; - blk = HCL_CNODE_CONS_CDR(obj); - if (check_block_expression_as_body(hcl, blk, cmd, FOR_NONE) <= -1) return -1; - obj = blk; + if (check_block_expression_as_body(hcl, fun_body, cmd, FOR_NONE) <= -1) return -1; + + HCL_ASSERT (hcl, fun_body != HCL_NULL); nlvars = 0; /* no known local variables until the actual block is processed */ HCL_ASSERT (hcl, nargs + nrvars + nlvars == hcl->c->tv.wcount - saved_tv_wcount); @@ -3273,7 +3360,7 @@ static int compile_fun (hcl_t* hcl, hcl_cnode_t* src) * produce the long jump instruction (HCL_CODE_JUMP_FORWARD_X) */ if (emit_single_param_instruction(hcl, HCL_CODE_JUMP_FORWARD_0, MAX_CODE_JUMP, HCL_CNODE_GET_LOC(cmd)) <= -1) return -1; - SWITCH_TOP_CFRAME (hcl, COP_COMPILE_OBJECT_LIST, obj); /* 1 */ + SWITCH_TOP_CFRAME (hcl, COP_COMPILE_OBJECT_LIST, fun_body); /* 1 */ PUSH_SUBCFRAME (hcl, COP_POST_FUN, fun_name); /* 3*/ cf = GET_SUBCFRAME(hcl); cf->u.fun.fun_type = fun_type; @@ -4640,7 +4727,7 @@ static HCL_INLINE int compile_dsymbol (hcl_t* hcl, hcl_cnode_t* obj) /* if defined using A::xxx syntax, it's not possible to know the instance position of an instance variable. * class X [ a b ] { * fun a() { - * fun J::t() { + * fun J:t() { * ## J has nothing to to with X in priciple even if J may point to X when a() is executed. * ## it's not meaningful to look up the variable `a` in the context of class X. * ## it must be prohibited to access instance variables using the self or super prefix @@ -4659,7 +4746,7 @@ static HCL_INLINE int compile_dsymbol (hcl_t* hcl, hcl_cnode_t* obj) } else if (hcl_comp_oochars_bcstr(name.ptr, sep - (const hcl_ooch_t*)name.ptr, "super") == 0) { - if (fbi->fun_type >> 8) /* if defined using A::xxx syntax */ + if (fbi->fun_type >> 8) /* if defined using A:xxx syntax */ { hcl_setsynerrbfmt (hcl, HCL_SYNERR_VARNAME, HCL_CNODE_GET_LOC(obj), HCL_CNODE_GET_TOK(obj), "not allowed to prefix with super in out-of-class method context"); return -1; @@ -6107,7 +6194,10 @@ static HCL_INLINE int post_fun (hcl_t* hcl) { /* something wrong - this must not happen because the reader must prevent this * but if it happens, it is a syntax error */ - hcl_setsynerrbfmt(hcl, HCL_SYNERR_BANNED, HCL_CNODE_GET_LOC(class_name), HCL_CNODE_GET_TOK(class_name), "class name prohibited"); + hcl_setsynerrbfmt ( + hcl, HCL_SYNERR_BANNED, HCL_CNODE_GET_LOC(class_name), HCL_NULL, + "class name '%.js' prohibited class initialization context", + HCL_CNODE_GET_TOKLEN(class_name), HCL_CNODE_GET_TOKPTR(class_name)); return -1; } diff --git a/lib/hcl-prv.h b/lib/hcl-prv.h index 837c531..b83a2a6 100644 --- a/lib/hcl-prv.h +++ b/lib/hcl-prv.h @@ -400,6 +400,16 @@ enum hcl_cnode_type_t HCL_CNODE_SELF, HCL_CNODE_SUPER, + HCL_CNODE_CONS, + HCL_CNODE_ELIST, /* empty list */ + HCL_CNODE_SHELL, /* pseudo-node to hold another actual node */ + + /* If HCL_CNODE_SHELL is not the last item before the horizontal line, + * HCL_CNDOE_IS_FOR_DATA(x) must be revised */ + /* ------------------------------------------------------------------ */ + + /* the cnode types from here don't represent actual data. + * these represent syntactical elements of the language only. */ HCL_CNODE_CLASS, HCL_CNODE_FUN, HCL_CNODE_DO, @@ -423,10 +433,6 @@ enum hcl_cnode_type_t HCL_CNODE_COLONGT, /* :> */ HCL_CNODE_COLONLT, /* :< */ HCL_CNODE_COLONSTAR, /* :* */ - - HCL_CNODE_CONS, - HCL_CNODE_ELIST, /* empty list */ - HCL_CNODE_SHELL /* pseudo-node to hold another actual node */ }; typedef enum hcl_cnode_type_t hcl_cnode_type_t; @@ -444,6 +450,7 @@ typedef enum hcl_cnode_flag_t hcl_cnode_flag_t; #define HCL_CNODE_GET_TOKLEN(x) ((x)->cn_tok.len) #define HCL_CNODE_IS_TYPED(x, _type) ((x)->cn_type == _type) +#define HCL_CNODE_IS_FOR_DATA(x) ((x)->cn_type <= HCL_CNODE_SHELL) #define HCL_CNODE_IS_ELLIPSIS(x) ((x)->cn_type == HCL_CNODE_ELLIPSIS) #define HCL_CNODE_IS_TRPCOLONS(x) ((x)->cn_type == HCL_CNODE_TRPCOLONS) diff --git a/lib/read.c b/lib/read.c index ca92566..8bbb2aa 100644 --- a/lib/read.c +++ b/lib/read.c @@ -896,7 +896,27 @@ static HCL_INLINE int can_colon_list (hcl_t* hcl) if (HCL_CNODE_IS_SYMBOL_SYNCODED(HCL_CNODE_CONS_CAR(rstl->head), HCL_SYNCODE_FUN) || HCL_CNODE_IS_TYPED(HCL_CNODE_CONS_CAR(rstl->head), HCL_CNODE_FUN)) { - if (rstl->count == 2) return 2; + hcl_cnode_t* tmp, * next; + next = HCL_CNODE_CONS_CDR(rstl->head); + HCL_ASSERT (hcl, next != HCL_NULL); + tmp = HCL_CNODE_CONS_CAR(next); /* second item */ + if (rstl->count == 2) + { + /* fun class:name() *... */ + if (HCL_CNODE_IS_SYMBOL_PLAIN(tmp)) return 2; + } + else if (rstl->count == 3) + { + /* fun(#c) class:name() ... */ + if (HCL_CNODE_IS_CONS_CONCODED(tmp, HCL_CONCODE_XLIST) || + HCL_CNODE_IS_ELIST_CONCODED(tmp, HCL_CONCODE_XLIST)) + { + next = HCL_CNODE_CONS_CDR(next); + HCL_ASSERT (hcl, next != HCL_NULL); + tmp = HCL_CNODE_CONS_CAR(next); /* third item */ + if (HCL_CNODE_IS_SYMBOL_PLAIN(tmp)) return 2; + } + } } return 0; /* the first key is not colon-delimited. so not allowed to colon-delimit other keys */ @@ -908,11 +928,17 @@ static HCL_INLINE int can_colon_list (hcl_t* hcl) cc = (hcl_concode_t)LIST_FLAG_GET_CONCODE(rstl->flagv); if (cc == HCL_CONCODE_XLIST) { + hcl_cnode_t* tmp; + /* method defintion with fun - e.g. fun String:length() * ugly that this reader must know about the meaning of fun */ if (rstl->count > 1) return 0; + /* ugly dual use of a colon sign. switch to MLIST if the first element * is delimited by a colon. e.g. (obj:new 10 20 30) */ + tmp = HCL_CNODE_CONS_CAR(rstl->head); + if (!HCL_CNODE_IS_FOR_DATA(tmp)) return 0; + LIST_FLAG_SET_CONCODE(rstl->flagv, HCL_CONCODE_MLIST); rstl->flagv &= ~JSON; } diff --git a/t/class-5001.err b/t/class-5001.err index e17ff9e..4fc314a 100644 --- a/t/class-5001.err +++ b/t/class-5001.err @@ -18,11 +18,11 @@ class B + ##ERROR: syntax error - prohibited binary selector '+' J := 11 class B { if (== J 10) { - fun :*newA() { + fun(#ci) newA() { return self } } else { - fun :*newB() { + fun(#ci) newB() { return self } } @@ -45,7 +45,7 @@ class B [ x y ] { }; class X :: B [ a b ] { - fun :* new(t) { + fun(#ci) new(t) { | a | set self.a t; set a 100; @@ -67,24 +67,25 @@ class X :: B [ a b ] { --- class X { - fun :* xxx() { + fun(#ci) xxx() { return X; } - fun :* qqq() { + + fun(#ci) qqq() { return "hello" } - fun String:length() { ##ERROR: syntax error - function name not valid - return (str.length self) + fun String:length() { ##ERROR: syntax error - class name 'String' before :'length' prohibited in class initialization context + return (core.basicSize self) } } --- class X { - fun :* xxx() { + fun(#ci) xxx() { return X; } - fun :* qqq() { + fun(#ci) qqq() { return "hello" } } @@ -109,7 +110,7 @@ fun X:xxx() { ##ERROR: exception not handled - "not class" ## and the clase instantiation method can't specify the size ## you can't place an item in the arrya at all. -fun Array:*boom() { +fun(#ci) Array:boom() { core.basicAtPut self 0 10 ##ERROR: exception not handled - "position(0) out of range - negative or greater than or equal to 0" printf "%O" self return self @@ -119,7 +120,7 @@ Array:boom --- class X [ a b c ] { - fun :* new () { + fun(#ci) new () { self.a := 20 return self } @@ -186,3 +187,10 @@ F := (class F { ##ERROR: exception not handle - "prohibited redefintion of F" ##F := 30 ##class F { ##ERROR: exception not handled - "prohibited redefintion of F" ##} + + +--- +class a { + fun() { ##ERROR: syntax error - unnamed function defined with 'fun' prohibited in class initialziation context + } +} diff --git a/t/feed-5001.err b/t/feed-5001.err index 326698e..1685032 100644 --- a/t/feed-5001.err +++ b/t/feed-5001.err @@ -146,6 +146,11 @@ fun :* fun1() { ##ERROR: syntax error - invalid function name ':*' for 'fun' --- +fun(#ci) fun1() { ##ERROR: syntax error - unsupported attribute list for plain function 'fun1' +} + +--- + (10 + 20 30) ##ERROR: syntax error - redundant operand '30' --- diff --git a/t/fun-01.hcl b/t/fun-01.hcl index 9ca1aec..2c13af2 100644 --- a/t/fun-01.hcl +++ b/t/fun-01.hcl @@ -97,7 +97,7 @@ if (== y 29) { ## -------------------------------------- defclass A [ a b c ] { - fun :* newInstance(x y z) { + fun(#ci) newInstance(x y z) { set a x set b y set c z @@ -113,8 +113,15 @@ k := (A:newInstance 11 22 33); ##set k (A:newInstance 11 22 33); set v (k:get-a); -if (== v 11) { - printf "OK - %d\n" v; -} else { - printf "ERROR - %d\n" v; -}; +if (== v 11) { printf "OK - %d\n" v; } else { printf "ERROR - %d, ot 11\n" v; }; + +## -------------------------------------- + +k := (fun (x) { + x 20 }) ## (+ x 20) would be syntax error, must be { + x 20 } +v := (k 10) +if (== v 30) { printf "OK - %d\n" v } else { printf "ERROR - %d, not 30\n" v }; + +## -------------------------------------- +fun k(x) (+ x 30) ## (+ x 30) is valid function body +v := (k 10) +if (== v 40) { printf "OK - %d\n" v } else { printf "ERROR - %d, not 40\n" v }; diff --git a/t/insta-01.hcl b/t/insta-01.hcl index 3a1c43f..b2be6f9 100644 --- a/t/insta-01.hcl +++ b/t/insta-01.hcl @@ -14,7 +14,7 @@ fun Number: ~= (oprnd) { return (~= self oprnd) } class A [ a b c ] { - fun :*newInstance(x y z) { + fun(#ci) newInstance(x y z) { set a x; set b y; set c z; @@ -28,7 +28,7 @@ class A [ a b c ] { class B :: A [ d e f ] { - fun :*newInstance(x y z) { + fun(#ci) newInstance(x y z) { super:newInstance (* x 2) (* y 2) (* z 2); set d x; set e y; @@ -36,9 +36,9 @@ class B :: A [ d e f ] { return self; }; - fun :: getSuper() { return super; }; - ###fun :: getSuperclass() { return (self:superclass); }; - fun :: getSelf() { return self; }; + fun(#c) getSuper() { return super; }; + ###fun(#c) getSuperclass() { return (self:superclass); }; + fun(#c) getSelf() { return self; }; fun sum() { return (+ (super:get-a) (super:get-b) (super:get-c) self.d self.e self.f); diff --git a/t/insta-02.hcl b/t/insta-02.hcl index f547487..91e4d41 100644 --- a/t/insta-02.hcl +++ b/t/insta-02.hcl @@ -12,7 +12,7 @@ fun Number: ~= (oprnd) { return (~= self oprnd) } ## -------------------------------------------------------------- set t ( class [ x ] { - fun :* make() { x := 1234; return self; }; + fun(#ci) make() { x := 1234; return self; }; fun get-x() { return x }; } ); @@ -40,7 +40,7 @@ else { printf "OK: value is %d\n" v }; ## -------------------------------------------------------------- class X0 [ a b c d ] { - fun :*new() { + fun(#ci) new() { return self; } @@ -67,7 +67,7 @@ else { printf "OK: value is %d\n" v } ## -------------------------------------------------------------- class X1 [ a b c ] { - fun :* new () { + fun(#classinst) new () { self.a := 20 return self } @@ -108,11 +108,11 @@ class F [ j t ] { } class X2 [ a b c ] { - fun :* new () { + fun(#classinst) new () { | j | self.a := 20 j := (self.a * 2) - fun F::get_x() { return (j * j) } + fun(#class) F:get_x() { return (j * j) } return self } } @@ -124,7 +124,7 @@ else { printf "OK: value is %d\n" v } ## -------------------------------------------------------------- class X3 { - fun :* new (a b) { + fun(#ci) new (a b) { fun X3:sum() { return (fun(j) { return (j + (a + b)) }) } return self; } @@ -136,15 +136,15 @@ else { printf "OK: value is %d\n" v } ## -------------------------------------------------------------- class X4 { - fun :: t() { + fun(#class) t() { | X5 | class X5 { ## this X5 isn't the local variable X4 - fun :: t() { + fun(#class) t() { X6 := (class { - fun :: t() { + fun(#class) t() { | X7 | X7 := (class { ## this X4 is the local variable X4 - fun :: t() { return 60 } + fun(#class) t() { return 60 } }) return 40 } diff --git a/t/retvar-01.hcl b/t/retvar-01.hcl index 65c2fb8..9e9a4a1 100644 --- a/t/retvar-01.hcl +++ b/t/retvar-01.hcl @@ -36,12 +36,12 @@ set X1 999; set X2 888; - fun :: get ( :: x y) { + fun(#class) get ( :: x y) { set x X1; set y X2; }; - fun :: get2 (inc :: x y) { + fun(#class) get2 (inc :: x y) { set x (+ X1 inc); set y (+ X2 inc); }; @@ -60,9 +60,9 @@ else { printf "OK: d=%d\n" d } class X [ x, y ] { - fun ::f(a :: b c) { b := (+ a 10); c := (+ a 20) } + fun(#class) f(a :: b c) { b := (+ a 10); c := (+ a 20) } - fun :*new(z) { + fun(#classinst) new(z) { ## multi-variable assignment with return variables to member variables [self.x, self.y] := (X:f z) return self; @@ -83,7 +83,7 @@ ## create a new binary operator message returning two output values -fun Number: // (x :: quo rem) { +fun Number:// (x :: quo rem) { quo := (/ self x) rem := (- self (* quo x)) } diff --git a/t/run.sh b/t/run.sh index bcc5e83..8beba21 100644 --- a/t/run.sh +++ b/t/run.sh @@ -3,4 +3,9 @@ ## program crashes or exits without an error message. echo RUN "[$@]" ($@ 2>&1 || echo "ERROR: exited with $?") | grep -E '^ERROR:' && exit 1 +##[ "x$MEMCHECK" = "xyes" ] && { +## [ -x /usr/bin/valgrind ] && { +## valgrind --leak-check=full --show-reachable=yes --track-fds=yes --log-file=/tmp/x "$@" 2>&1 +## } +##} exit 0 diff --git a/t/var-01.hcl b/t/var-01.hcl index ea2b9b0..21aa01a 100644 --- a/t/var-01.hcl +++ b/t/var-01.hcl @@ -58,7 +58,7 @@ x class T [ j ] { - fun :* new() { + fun(#classinst) new() { set j 99 return self } diff --git a/t/var-5001.err b/t/var-5001.err index 39353dc..6421519 100644 --- a/t/var-5001.err +++ b/t/var-5001.err @@ -1,5 +1,5 @@ defclass A [ a ] { - fun :* init1() { + fun(#ci) init1() { | b | set b (+ 1 2); set a b; @@ -15,7 +15,7 @@ defclass A [ a ] { printf ">>> %d\n" j; } - fun :* init2() { + fun(#ci) init2() { | b | set b (+ 10 20); set a b; @@ -26,7 +26,7 @@ defclass A [ a ] { --- -fun String length() { ##ERROR: syntax error - no argument list +fun String length() { ##ERROR: syntax error - 'String' not followed by ( but followed by 'length' } --- diff --git a/t/var-5004.err b/t/var-5004.err index 464a1f7..6db00fe 100644 --- a/t/var-5004.err +++ b/t/var-5004.err @@ -1,2 +1,66 @@ fun self.x() { ##ERROR: syntax error - invalid function name 'self.x' for 'fun' }; + +--- + +fun if() { ##ERROR: syntax error - invalid function name 'if' for 'fun' +}; + +--- + +fun a if() { ##ERROR: syntax error - 'a' not followed by ( but followed by 'if' +}; + +--- + +fun a:b if() { ##ERROR: syntax error - 'b' not followed by ( but followed by 'if' +}; + +--- + +fun x (x :: 20) { ##ERROR: syntax error - invalid return variable '20' for 'fun' +} + +--- +fun x (x :: if) { ##ERROR: syntax error - invalid return variable 'if' for 'fun' +} + +--- + +fun x (x :: self.y) { ##ERROR: syntax error - invalid return variable 'self.y' for 'fun' +} + +--- + +fun x (x :: z z) { ##ERROR: syntax error - duplicate return variable 'z' for 'fun' +} + +--- + +fun x (x 20) { ##ERROR: syntax error - invalid argument name '20' for 'fun' +} + +--- + +fun x (+) { ##ERROR: syntax error - invalid argument name '+' for 'fun' +} + +--- + +fun x (a while) { ##ERROR: syntax error - invalid argument name 'while' for 'fun' +} + +--- + +fun x (a b a) { ##ERROR: syntax error - duplicate argument name 'a' for 'fun' +} + +--- + +fun x (... a) { ##ERROR: syntax error - unexpected token 'a' after '...' for 'fun' +} + +--- + +fun x (... : a) { ##ERROR: syntax error - : disallowed +}