|
Consistency with table constructors. Especially when implementing
DSLs it is inconsistent that you can do
f{ 1, 2, }
but not
f( 1, 2, )
For documentation purposes, I like to split parameters across
lines:
function f( a, -- a comment for a b -- a comment for b ) ... end
which would benefit from a trailing comma (which should also be added for consistency with the trailing comma in arglists):
function f( a, -- a comment for a b, -- a comment for b ) ... end
This can not be extended to explists in general, since doing so
would introduce an ambiguity with function calls:
a, b = 1, print("hello world")
However, it should be possible to extend this to everything else
(return statements, for explists) since those are delimited.
Additionally, the left hand side of assignments could also get
trailing comma support; I don't think these extensions would be
worthwhile though.
$ ./lua Lua 5.4.5 Copyright (C) 1994-2022 Lua.org, PUC-Rio > function f(a,b,c,) print(a,b,c,) end > f(1,2,3) 1 2 3
The diff got somewhat messy due to the addition of the arglist
parameter to explist, which is somewhat dirty.
diff --git a/lparser.c b/lparser.c index 24668c24..5837f9d6 100644 --- a/lparser.c +++ b/lparser.c @@ -957,28 +957,31 @@ static void setvararg (FuncState *fs, int nparams) { static void parlist (LexState *ls) { - /* parlist -> [ {NAME ','} (NAME | '...') ] */ + /* parlist -> [ {NAME ','} [ (NAME | '...') ',' ] ] */ FuncState *fs = ls->fs; Proto *f = fs->f; int nparams = 0; int isvararg = 0; - if (ls->t.token != ')') { /* is 'parlist' not empty? */ - do { - switch (ls->t.token) { - case TK_NAME: { - new_localvar(ls, str_checkname(ls)); - nparams++; - break; - } - case TK_DOTS: { - luaX_next(ls); - isvararg = 1; - break; - } - default: luaX_syntaxerror(ls, "<name> or '...' expected"); + int closed = 0; + do { + switch (ls->t.token) { + case TK_NAME: { + new_localvar(ls, str_checkname(ls)); + nparams++; + break; } - } while (!isvararg && testnext(ls, ',')); - } + case TK_DOTS: { + luaX_next(ls); + isvararg = 1; + break; + } + case ')': { + closed = 1; + break; + } + default: luaX_syntaxerror(ls, "<name> or '...' expected"); + } + } while (!closed && !isvararg && testnext(ls, ',')); adjustlocalvars(ls, nparams); f->numparams = cast_byte(fs->nactvar); if (isvararg) @@ -1009,11 +1012,13 @@ static void body (LexState *ls, expdesc *e, int ismethod, int line) { } -static int explist (LexState *ls, expdesc *v) { +static int explist (LexState *ls, expdesc *v, int arglist) { /* explist -> expr { ',' expr } */ int n = 1; /* at least one _expression_ */ expr(ls, v); while (testnext(ls, ',')) { + if (arglist && ls->t.token == ')') /* allow trailing comma in an arglist */ + return n; luaK_exp2nextreg(ls->fs, v); expr(ls, v); n++; @@ -1027,12 +1032,12 @@ static void funcargs (LexState *ls, expdesc *f, int line) { expdesc args; int base, nparams; switch (ls->t.token) { - case '(': { /* funcargs -> '(' [ explist ] ')' */ + case '(': { /* funcargs -> '(' [ explist ] [ ',' ] ')' */ luaX_next(ls); if (ls->t.token == ')') /* arg list is empty? */ args.k = VVOID; else { - explist(ls, &args); + explist(ls, &args, 1); if (hasmultret(args.k)) luaK_setmultret(fs, &args); } @@ -1389,7 +1394,7 @@ static void restassign (LexState *ls, struct LHS_assign *lh, int nvars) { else { /* restassign -> '=' explist */ int nexps; checknext(ls, '='); - nexps = explist(ls, &e); + nexps = explist(ls, &e, 0); if (nexps != nvars) adjust_assign(ls, nvars, nexps, &e); else { @@ -1609,7 +1614,7 @@ static void forlist (LexState *ls, TString *indexname) { } checknext(ls, TK_IN); line = ls->linenumber; - adjust_assign(ls, 4, explist(ls, &e), &e); + adjust_assign(ls, 4, explist(ls, &e, 0), &e); adjustlocalvars(ls, 4); /* control variables */ marktobeclosed(fs); /* last control var. must be closed */ luaK_checkstack(fs, 3); /* extra space to call generator */ @@ -1744,7 +1749,7 @@ static void localstat (LexState *ls) { nvars++; } while (testnext(ls, ',')); if (testnext(ls, '=')) - nexps = explist(ls, &e); + nexps = explist(ls, &e, 0); else { e.k = VVOID; nexps = 0; @@ -1819,7 +1824,7 @@ static void retstat (LexState *ls) { if (block_follow(ls, 1) || ls->t.token == ';') nret = 0; /* return no values */ else { - nret = explist(ls, &e); /* optional return values */ + nret = explist(ls, &e, 0); /* optional return values */ if (hasmultret(e.k)) { luaK_setmultret(fs, &e); if (e.k == VCALL && nret == 1 && !fs->bl->insidetbc) { /* tail call? */