diff --git a/README.md b/README.md index 8a26bba7..08aabb9e 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ - [Control Strucutres](#control-strucutres) - [Function](#function) - [Variable](#variable) + - [Built-in Variable](#built-in-variable) - [Pipes](#pipes) - [Garbage Collection](#garbage-collection) - [Modules](#modules) @@ -296,7 +297,7 @@ You can also pass arguments to the entry point function by defining it with para ```awk @pragma entry main; function main(arg1, arg2) { - print "Arguments:", arg1, arg2 + print "Arguments:", arg1, arg2 } ``` @@ -315,6 +316,25 @@ This flexibility in specifying the entry point can be useful in various scenario - Testing and Debugging: When working on specific parts of your script, you can temporarily set the entry point to a different function, making it easier to test and debug that particular functionality. - Integration with Other Systems: If you need to embed Hawk scripts within a larger application or system, you can use the `@pragma entry` feature to specify the function that should be executed as the entry point, enabling better integration and control over the script execution flow. +If you don't know the number of arguments in advance, you can use the ellipsis `...` in the parameter list and access the varidic arguments using `@argv()` and `@argc()`. + +```awk +@pragma entry main; +function main(...) { + @local i + for (i = 0; i < @argc(); i++) printf("%d:", @argv(i)) + print "" +} +``` + +In this example, the `main` function can accept variable number of arguments. + +```sh +$ hawk -f main.hawk 10 20 30 40 50 +``` + +The expected output of the above command is `10:20:30:40:50:`. + It's important to note that if you don't define an entry point function using `@pragma entry`, Hawk will default to the standard awk behavior and execute the `BEGIN` block first, followed by the pattern-action blocks, and finally the `END` block. Overall, the @pragma entry feature in Hawk provides you with greater flexibility and control over the execution flow of your scripts, allowing you to tailor the entry point to your specific needs and requirements. diff --git a/lib/hawk.h b/lib/hawk.h index ab1e026b..d065f73c 100644 --- a/lib/hawk.h +++ b/lib/hawk.h @@ -433,6 +433,7 @@ enum hawk_nde_type_t HAWK_NDE_MBS, HAWK_NDE_REX, HAWK_NDE_XNIL, + HAWK_NDE_XARG, HAWK_NDE_FUN, /* keep this order for the following items otherwise, you may have @@ -483,6 +484,7 @@ struct hawk_fun_t hawk_oow_t nargs; hawk_ooch_t* argspec; /* similar to the argument spec of hawk_fnc_arg_t. supports v & r only. neither R nor x is set internally. */ hawk_oow_t argspeclen; /* the length of argspec. it can be different from nargs if there are call-by-value parameters after the last call-by-reference parameter or variadic arguments are supported */ + int variadic; hawk_nde_t* body; }; typedef struct hawk_fun_t hawk_fun_t; diff --git a/lib/mod-hawk.c b/lib/mod-hawk.c index a516fd94..c50d36c0 100644 --- a/lib/mod-hawk.c +++ b/lib/mod-hawk.c @@ -498,6 +498,7 @@ static int fnc_typename (hawk_rtx_t* rtx, const hawk_fnc_info_t* fi) } /* -------------------------------------------------------------------------- */ + static int fnc_hash (hawk_rtx_t* rtx, const hawk_fnc_info_t* fi) { hawk_val_t* a0; diff --git a/lib/parse-prv.h b/lib/parse-prv.h index d626c17a..e783ad28 100644 --- a/lib/parse-prv.h +++ b/lib/parse-prv.h @@ -29,6 +29,8 @@ enum hawk_kwid_t { HAWK_KWID_XABORT, + HAWK_KWID_XARGC, + HAWK_KWID_XARGV, HAWK_KWID_XGLOBAL, HAWK_KWID_XINCLUDE, HAWK_KWID_XINCLUDE_ONCE, diff --git a/lib/parse.c b/lib/parse.c index c1513258..386c074b 100644 --- a/lib/parse.c +++ b/lib/parse.c @@ -114,15 +114,20 @@ enum tok_t TOK_COLON, TOK_DBLCOLON, TOK_QUEST, + TOK_ELLIPSIS, + TOK_DBLPERIOD, /* not used yet */ + TOK_PERIOD, /* not used yet */ /* == begin reserved words == */ /* === extended reserved words === */ + TOK_XARGC, + TOK_XARGV, + TOK_XABORT, TOK_XGLOBAL, - TOK_XLOCAL, TOK_XINCLUDE, TOK_XINCLUDE_ONCE, + TOK_XLOCAL, TOK_XPRAGMA, - TOK_XABORT, TOK_XRESET, /* === normal reserved words === */ @@ -264,6 +269,8 @@ static kwent_t kwtab[] = /* keep this table in sync with the kw_t enums in "parse-prv.h". * also keep it sorted by the first field for binary search */ { { HAWK_T("@abort"), 6 }, TOK_XABORT, 0 }, + { { HAWK_T("@argc"), 5 }, TOK_XARGC, 0 }, + { { HAWK_T("@argv"), 5 }, TOK_XARGV, 0 }, { { HAWK_T("@global"), 7 }, TOK_XGLOBAL, 0 }, { { HAWK_T("@include"), 8 }, TOK_XINCLUDE, 0 }, { { HAWK_T("@include_once"), 13 }, TOK_XINCLUDE_ONCE, 0 }, @@ -628,7 +635,7 @@ hawk_ooch_t* hawk_addsionamewithuchars (hawk_t* hawk, const hawk_uch_t* ptr, haw #if defined(HAWK_OOCH_IS_UCH) link = (hawk_link_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*link) + HAWK_SIZEOF(hawk_uch_t) * (len + 1)); - if (!link) return HAWK_NULL; + if (HAWK_UNLIKELY(!link)) return HAWK_NULL; hawk_copy_uchars_to_ucstr_unlimited ((hawk_uch_t*)(link + 1), ptr, len); #else @@ -638,7 +645,7 @@ hawk_ooch_t* hawk_addsionamewithuchars (hawk_t* hawk, const hawk_uch_t* ptr, haw if (hawk_convutobchars(hawk, ptr, &ucslen, HAWK_NULL, &bcslen) <= -1) return HAWK_NULL; link = (hawk_link_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*link) + HAWK_SIZEOF(hawk_bch_t) * (bcslen + 1)); - if (!link) return HAWK_NULL; + if (HAWK_UNLIKELY(!link)) return HAWK_NULL; ucslen = len; bcslen = bcslen + 1; @@ -665,7 +672,7 @@ hawk_ooch_t* hawk_addsionamewithbchars (hawk_t* hawk, const hawk_bch_t* ptr, haw if (hawk_convbtouchars (hawk, ptr, &bcslen, HAWK_NULL, &ucslen, 0) <= -1) return HAWK_NULL; link = (hawk_link_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*link) + HAWK_SIZEOF(hawk_uch_t) * (ucslen + 1)); - if (!link) return HAWK_NULL; + if (HAWK_UNLIKELY(!link)) return HAWK_NULL; bcslen = len; ucslen = ucslen + 1; @@ -674,7 +681,7 @@ hawk_ooch_t* hawk_addsionamewithbchars (hawk_t* hawk, const hawk_bch_t* ptr, haw #else link = (hawk_link_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*link) + HAWK_SIZEOF(hawk_bch_t) * (len + 1)); - if (!link) return HAWK_NULL; + if (HAWK_UNLIKELY(!link)) return HAWK_NULL; hawk_copy_bchars_to_bcstr_unlimited ((hawk_bch_t*)(link + 1), ptr, len); #endif @@ -829,14 +836,14 @@ static int begin_include (hawk_t* hawk, int once) * and this list is not deleted after hawk_parse. * the errinfo.loc.file can point to the file name here. */ sio_name = hawk_addsionamewithoochars(hawk, HAWK_OOECS_PTR(hawk->tok.name), HAWK_OOECS_LEN(hawk->tok.name)); - if (!sio_name) + if (HAWK_UNLIKELY(!sio_name)) { ADJERR_LOC (hawk, &hawk->ptok.loc); goto oops; } arg = (hawk_sio_arg_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*arg)); - if (!arg) + if (HAWK_UNLIKELY(!arg)) { ADJERR_LOC (hawk, &hawk->ptok.loc); goto oops; @@ -1304,6 +1311,7 @@ static hawk_nde_t* parse_function (hawk_t* hawk) hawk_oow_t argspeccapa = 0; hawk_oow_t argspeclen; hawk_oow_t nargs, g; + int variadic = 0; hawk_htb_pair_t* pair; hawk_loc_t xloc; int rederr; @@ -1377,6 +1385,25 @@ static hawk_nde_t* parse_function (hawk_t* hawk) hawk_ooch_t* pa; hawk_oow_t pal; + if (MATCH(hawk, TOK_ELLIPSIS)) + { + /* this must be the last parameter. the variadic part doesn't support reference */ + /* this must be the last token before the parenthesis if given. + * function xxx (...) + * function xxx (a ...) + * function xxx (a, b ...) */ + if (get_token(hawk) <= -1) goto oops; + + if (!MATCH(hawk,TOK_RPAREN)) + { + hawk_seterrfmt (hawk, &hawk->tok.loc, HAWK_ERPAREN, FMT_ERPAREN, HAWK_OOECS_LEN(hawk->tok.name), HAWK_OOECS_PTR(hawk->tok.name)); + goto oops; + } + + variadic = 1; + break; + } + if (MATCH(hawk, TOK_BAND)) /* &arg */ { /* pass-by-reference argument */ @@ -1435,6 +1462,7 @@ static hawk_nde_t* parse_function (hawk_t* hawk) if (get_token(hawk) <= -1) goto oops; + /* no ... present after the variable name */ if (MATCH(hawk,TOK_RPAREN)) break; if (!MATCH(hawk,TOK_COMMA)) @@ -1508,6 +1536,7 @@ static hawk_nde_t* parse_function (hawk_t* hawk) /*fun->name.ptr = HAWK_NULL;*/ /* function name is set below */ fun->name.len = 0; fun->nargs = nargs; + fun->variadic = variadic; fun->argspec = argspec; fun->argspeclen = argspeclen; fun->body = body; @@ -2475,7 +2504,7 @@ static hawk_nde_t* parse_if (hawk_t* hawk, const hawk_loc_t* xloc) } nde = (hawk_nde_if_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*nde)); - if (nde == HAWK_NULL) + if (HAWK_UNLIKELY(!nde)) { ADJERR_LOC (hawk, xloc); goto oops; @@ -5087,6 +5116,61 @@ oops: } +static hawk_nde_t* parse_primary_xarg (hawk_t* hawk, const hawk_loc_t* xloc) +{ + hawk_nde_xarg_t* nde = HAWK_NULL; + hawk_nde_t* pos = HAWK_NULL; + int opcode; + + HAWK_ASSERT (hawk->tok.type == TOK_XARGV || hawk->tok.type == TOK_XARGC); + + opcode = (hawk->tok.type == TOK_XARGC); /* @argv: 1 @argv: 2 */ + + if (get_token(hawk) <= -1) goto oops; + if (!MATCH(hawk,TOK_LPAREN)) + { + hawk_seterrfmt (hawk, &hawk->tok.loc, HAWK_ELPAREN, FMT_ELPAREN, HAWK_OOECS_LEN(hawk->tok.name), HAWK_OOECS_PTR(hawk->tok.name)); + goto oops; + } + if (get_token(hawk) <= -1) goto oops; + + if (opcode == 0) + { + /* @argv */ + hawk_loc_t eloc; + eloc = hawk->tok.loc; + pos = parse_expr_withdc(hawk, &eloc); + if (HAWK_UNLIKELY(!pos)) goto oops; + } + + if (!MATCH(hawk,TOK_RPAREN)) + { + hawk_seterrfmt (hawk, &hawk->tok.loc, HAWK_ERPAREN, FMT_ERPAREN, HAWK_OOECS_LEN(hawk->tok.name), HAWK_OOECS_PTR(hawk->tok.name)); + goto oops; + } + + if (get_token(hawk) <= -1) goto oops; + + nde = (hawk_nde_xarg_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*nde)); + if (HAWK_UNLIKELY(!nde)) + { + ADJERR_LOC (hawk, xloc); + return HAWK_NULL; + } + + nde->type = HAWK_NDE_XARG; + nde->loc = *xloc; + nde->opcode = opcode; + nde->pos = pos; + + return (hawk_nde_t*)nde; + +oops: + if (nde) hawk_freemem (hawk, nde); /* tricky to call hawk_clrpt() on nde due to var and pos */ + if (pos) hawk_clrpt (hawk, pos); + return HAWK_NULL; +} + static hawk_nde_t* parse_primary_nopipe (hawk_t* hawk, const hawk_loc_t* xloc) { switch (hawk->tok.type) @@ -5131,6 +5215,9 @@ static hawk_nde_t* parse_primary_nopipe (hawk_t* hawk, const hawk_loc_t* xloc) case TOK_XNIL: return parse_primary_xnil(hawk, xloc); + case TOK_XARGV: + case TOK_XARGC: + return parse_primary_xarg(hawk, xloc); default: { @@ -5298,7 +5385,7 @@ static hawk_nde_t* parse_variable (hawk_t* hawk, const hawk_loc_t* xloc, hawk_nd } nde = (hawk_nde_var_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*nde)); - if (nde == HAWK_NULL) + if (HAWK_UNLIKELY(!nde)) { ADJERR_LOC (hawk, xloc); return HAWK_NULL; @@ -5380,7 +5467,7 @@ static hawk_nde_t* parse_fun_as_value (hawk_t* hawk, const hawk_oocs_t* name, c /* create the node for the literal */ nde = (hawk_nde_fun_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*nde)); - if (nde == HAWK_NULL) + if (HAWK_UNLIKELY(!nde)) { ADJERR_LOC (hawk, xloc); return HAWK_NULL; @@ -5531,7 +5618,7 @@ static hawk_nde_t* parse_primary_ident_noseg (hawk_t* hawk, const hawk_loc_t* xl * as concatention by blanks. so we handle the name as a named * variable. */ tmp = (hawk_nde_var_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*tmp)); - if (tmp == HAWK_NULL) ADJERR_LOC (hawk, xloc); + if (HAWK_UNLIKELY(!tmp)) ADJERR_LOC (hawk, xloc); else { /* collect unique instances of a named variable @@ -5938,7 +6025,7 @@ static hawk_nde_t* parse_fncall (hawk_t* hawk, const hawk_oocs_t* name, hawk_fnc make_node: call = (hawk_nde_fncall_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*call)); - if (!call) + if (HAWK_UNLIKELY(!call)) { ADJERR_LOC (hawk, xloc); goto oops; @@ -6588,6 +6675,9 @@ static int get_symbols (hawk_t* hawk, hawk_ooci_t c, hawk_tok_t* tok) { HAWK_T("::"), 2, TOK_DBLCOLON, 0 }, { HAWK_T(":"), 1, TOK_COLON, 0 }, { HAWK_T("?"), 1, TOK_QUEST, 0 }, + { HAWK_T("..."), 3, TOK_ELLIPSIS, 0 }, + { HAWK_T(".."), 2, TOK_DBLPERIOD, 0 }, + { HAWK_T("."), 1, TOK_PERIOD, 0 }, { HAWK_NULL, 0, 0, 0 } }; @@ -7253,6 +7343,8 @@ static hawk_htb_walk_t deparse_func (hawk_htb_t* map, hawk_htb_pair_t* pair, voi PUT_S (df, HAWK_T(", ")); } + if (fun->variadic) PUT_S(df, HAWK_T(" ...")); + PUT_S (df, HAWK_T(")")); if (df->hawk->opt.trait & HAWK_CRLF) PUT_C (df, HAWK_T('\r')); diff --git a/lib/run.c b/lib/run.c index 5155f0f9..357e51c6 100644 --- a/lib/run.c +++ b/lib/run.c @@ -163,6 +163,7 @@ static hawk_val_t* eval_str (hawk_rtx_t* rtx, hawk_nde_t* nde); static hawk_val_t* eval_mbs (hawk_rtx_t* rtx, hawk_nde_t* nde); static hawk_val_t* eval_rex (hawk_rtx_t* rtx, hawk_nde_t* nde); static hawk_val_t* eval_xnil (hawk_rtx_t* rtx, hawk_nde_t* nde); +static hawk_val_t* eval_xarg (hawk_rtx_t* rtx, hawk_nde_t* nde); static hawk_val_t* eval_fun (hawk_rtx_t* rtx, hawk_nde_t* nde); static hawk_val_t* eval_named (hawk_rtx_t* rtx, hawk_nde_t* nde); static hawk_val_t* eval_gbl (hawk_rtx_t* rtx, hawk_nde_t* nde); @@ -1788,7 +1789,7 @@ hawk_val_t* hawk_rtx_callfun (hawk_rtx_t* rtx, hawk_fun_t* fun, hawk_val_t* args /* keep HAWK_NULL in call.args so that hawk_rtx_evalcall() knows it's a fake call structure */ /* check if the number of arguments given is more than expected */ - if (nargs > fun->nargs) + if (nargs > fun->nargs && !fun->variadic) { /* TODO: is this correct? what if i want to * allow arbitrary numbers of arguments? */ @@ -3707,6 +3708,7 @@ static hawk_val_t* eval_expression0 (hawk_rtx_t* rtx, hawk_nde_t* nde) eval_mbs, eval_rex, eval_xnil, + eval_xarg, eval_fun, eval_named, eval_gbl, @@ -6667,11 +6669,11 @@ static HAWK_INLINE hawk_val_t* eval_fncall_fun (hawk_rtx_t* rtx, hawk_nde_t* nde fun = call->u.fun.fun; } - if (call->nargs > fun->nargs) + if (call->nargs > fun->nargs && !fun->variadic) { /* TODO: is this correct? what if i want to * allow arbitarary numbers of arguments? */ - hawk_rtx_seterrnum (rtx, &nde->loc, HAWK_EARGTM); + hawk_rtx_seterrfmt (rtx, &nde->loc, HAWK_EARGTM, HAWK_T("too many arguments to '%.*js'"), fun->name.len, fun->name.ptr); return HAWK_NULL; } @@ -6705,11 +6707,11 @@ static hawk_val_t* eval_fncall_var (hawk_rtx_t* rtx, hawk_nde_t* nde) ADJERR_LOC (rtx, &nde->loc); rv = HAWK_NULL; } - else if (call->nargs > fun->nargs) + else if (call->nargs > fun->nargs && !fun->variadic) { /* TODO: is this correct? what if i want to * allow arbitarary numbers of arguments? */ - hawk_rtx_seterrnum (rtx, &nde->loc, HAWK_EARGTM); + hawk_rtx_seterrfmt (rtx, &nde->loc, HAWK_EARGTM, HAWK_T("too many arguments to '%.*js'"), fun->name.len, fun->name.ptr); rv = HAWK_NULL; } else @@ -7144,11 +7146,25 @@ static hawk_oow_t push_arg_from_nde (hawk_rtx_t* rtx, hawk_nde_fncall_t* call, v break; } + case 'V': + /* the variadic argument marked with ... in the function parameter */ + if (p->type == HAWK_NDE_LCL) + { + hawk_nde_var_t* var = (hawk_nde_var_t*)p; + v = hawk_rtx_makeintval(rtx, ((hawk_nde_var_t*)p)->id.idxa); + } + else + { + /* THIS IS THE RUNTIME ERROR */ + /* TODO: */ + } + case 'x': /* a regular expression is passed to the function as it is */ v = eval_expression0(rtx, p); break; + default: normal_arg: v = eval_expression(rtx, p); @@ -7454,10 +7470,41 @@ static hawk_val_t* eval_rex (hawk_rtx_t* rtx, hawk_nde_t* nde) static hawk_val_t* eval_xnil (hawk_rtx_t* rtx, hawk_nde_t* nde) { - hawk_val_t* val; - val = hawk_rtx_makenilval(rtx); - if (HAWK_UNLIKELY(!val)) ADJERR_LOC (rtx, &nde->loc); - return val; + return hawk_rtx_makenilval(rtx); /* this never fails */ +} + +static hawk_val_t* eval_xarg (hawk_rtx_t* rtx, hawk_nde_t* nde) +{ + hawk_nde_xarg_t* xarg; + + xarg = (hawk_nde_xarg_t*)nde; + if (xarg->opcode == 0) + { + /* @argv */ + hawk_val_t* v; + hawk_int_t pos; + int n; + + v = eval_expression(rtx, xarg->pos); + if (HAWK_UNLIKELY(!v)) return HAWK_NULL; + + hawk_rtx_refupval (rtx, v); + n = hawk_rtx_valtoint(rtx, v, &pos); + hawk_rtx_refdownval (rtx, v); + if (n <= -1) + { + hawk_rtx_seterrnum (rtx, &xarg->pos->loc, HAWK_EPOSIDX); + return HAWK_NULL; + } + + return (pos < 0 || pos >= hawk_rtx_getnargs(rtx))? hawk_rtx_makenilval(rtx): hawk_rtx_getarg(rtx, pos); + } + else + { + /* @argc */ + hawk_int_t nargs = (hawk_int_t)hawk_rtx_getnargs(rtx); + return hawk_rtx_makeintval(rtx, nargs); + } } static hawk_val_t* eval_fun (hawk_rtx_t* rtx, hawk_nde_t* nde) diff --git a/lib/tree-prv.h b/lib/tree-prv.h index 406bb5b0..a37453f6 100644 --- a/lib/tree-prv.h +++ b/lib/tree-prv.h @@ -68,6 +68,7 @@ typedef struct hawk_nde_mbs_t hawk_nde_mbs_t; typedef struct hawk_nde_rex_t hawk_nde_rex_t; typedef struct hawk_nde_fun_t hawk_nde_fun_t; typedef struct hawk_nde_xnil_t hawk_nde_xnil_t; +typedef struct hawk_nde_xarg_t hawk_nde_xarg_t; typedef struct hawk_nde_var_t hawk_nde_var_t; typedef struct hawk_nde_fncall_t hawk_nde_fncall_t; @@ -201,6 +202,13 @@ struct hawk_nde_xnil_t HAWK_NDE_HDR; }; +struct hawk_nde_xarg_t +{ + HAWK_NDE_HDR; + int opcode; /* 0: @argc, 1: @argv */ + hawk_nde_t* pos; +}; + /* HAWK_NDE_FUN - function as a value */ struct hawk_nde_fun_t { diff --git a/lib/tree.c b/lib/tree.c index 7b813927..b37f4996 100644 --- a/lib/tree.c +++ b/lib/tree.c @@ -554,6 +554,19 @@ static int print_expr (hawk_t* hawk, hawk_nde_t* nde) break; } + case HAWK_NDE_XARG: + { + static hawk_ooch_t* xarg_str[] = + { + HAWK_T("@argv("), + HAWK_T("@argc("), + }; + PUT_SRCSTR (hawk, xarg_str[((hawk_nde_xarg_t*)nde)->opcode % 2]); + if (((hawk_nde_xarg_t*)nde)->pos) PRINT_EXPR (hawk, ((hawk_nde_xarg_t*)nde)->pos); + PUT_SRCSTR (hawk, HAWK_T(")")); + break; + } + case HAWK_NDE_FUN: { PUT_SRCSTRN (hawk, @@ -1454,6 +1467,14 @@ void hawk_clrpt (hawk_t* hawk, hawk_nde_t* tree) break; } + case HAWK_NDE_XARG: + { + if (((hawk_nde_xarg_t*)p)->pos) /* pos is null for @argc */ + hawk_clrpt (hawk, ((hawk_nde_xarg_t*)p)->pos); + hawk_freemem (hawk, p); + break; + } + case HAWK_NDE_FUN: { hawk_freemem (hawk, ((hawk_nde_fun_t*)p)->name.ptr); diff --git a/t/h-002.hawk b/t/h-002.hawk index 3f32f17d..47879d82 100644 --- a/t/h-002.hawk +++ b/t/h-002.hawk @@ -641,6 +641,10 @@ function main() tap_ensure (str::match(b @b"what is this", /10/, 1, a), 5, @SCRIPTNAME, @SCRIPTLINE); tap_ensure (RSTART, 5, @SCRIPTNAME, @SCRIPTLINE); tap_ensure (RLENGTH, 2, @SCRIPTNAME, @SCRIPTLINE); + + tap_ensure (test5(10, 20, 30), 10, @SCRIPTNAME, @SCRIPTLINE); + tap_ensure (test6(1, 2, 3, 4, 5, 6, 7, 8, 9, 10), 54, @SCRIPTNAME, @SCRIPTLINE); + tap_ensure (test7(1, 2, 3, 4, 5, 6, 7, 8, 9, 10), 55, @SCRIPTNAME, @SCRIPTLINE); } tap_end (); } @@ -649,3 +653,21 @@ function test1(&foo) { test2(foo) } function test2(&bar) { bar[1] = 1 } function test3(foo) { test2(foo) } function test4(bar) { bar[1] = 1 } + +function test5(a, b, ...) { + return a; +} + +function test6(a, ...) { + @local i, x + x = 0 + for (i = 0; i < @argc(); i++) x += @argv(i); + return x - a; +} + +function test7(...) { + @local i, x + x = 0 + for (i = 0; i < @argc(); i++) x += @argv(i); + return x; +}