Parenless calls for function literals

"'TopchetoEU' via lua-l" <[email protected]> Thu, 28 May 2026 17:41:31 +0000
Newsgroups gmane.comp.lang.lua.general
Message-ID <CdBAYNQgpj1uPWQzg0HKplvAgP1X0NrYOGXDK7oL1vyBzKcClWibJWsM7O_r9PziqO0wzsq99Kxzaa9OA1IUAd5r9EZGHDQUMJDuZDYMMbo=@proton.me>
Hello list,

Lua allows calls with table and string literals to omit the call parens, which enables some very elegant and concise DSL-styled syntax. I have been experimenting with extending that idea to function literals, too. This is the core of the syntax:

    called_function begin
        body_of_function_literal
    end

I chose to introduce `begin` as a keyword, as opposed to using the existing `function` keyword, as syntax like this:

    print("Hello, world")
    function test() end

Would be difficult to parse (not impossible, but would require a look ahead, which would complicate needlessly the parser for such a small feature).

I'm not terribly attached to the `begin` keyword, and I even fear that it could break existing code, as `begin` could be a common variable/field name, so I'm open to suggestions.

Otherwise, I think this would make DSLs much cleaner in some cases, where you need to pass functions, instead of tables. For example, a testing framework could declare its cases like this:

    describe "My example test" begin
        it "Should be true" begin
            assert.truthy(true)
        end
    end

I have attached a patch file, so you can experiment with it. Note that I haven't spent much time testing it, but nothing *should* break, as it changes about 15 LoC.

Comments and criticism are welcome.

-- 
You received this message because you are subscribed to the Google Groups "lua-l" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].
To view this discussion visit https://groups.google.com/d/msgid/lua-l/CdBAYNQgpj1uPWQzg0HKplvAgP1X0NrYOGXDK7oL1vyBzKcClWibJWsM7O_r9PziqO0wzsq99Kxzaa9OA1IUAd5r9EZGHDQUMJDuZDYMMbo%3D%40proton.me.
lua-begin.patch (text/x-patch, 2.6 KB)
diff --git a/llex.c b/llex.c
index 5fc39a5c..898ac8d7 100644
--- a/llex.c
+++ b/llex.c
@@ -38,7 +38,7 @@
 
 /* ORDER RESERVED */
 static const char *const luaX_tokens [] = {
-    "and", "break", "do", "else", "elseif",
+    "and", "begin", "break", "do", "else", "elseif",
     "end", "false", "for", "function", "goto", "if",
     "in", "local", "nil", "not", "or", "repeat",
     "return", "then", "true", "until", "while",
diff --git a/llex.h b/llex.h
index 389d2f86..6d625e3e 100644
--- a/llex.h
+++ b/llex.h
@@ -31,7 +31,7 @@
 */
 enum RESERVED {
   /* terminal symbols denoted by reserved words */
-  TK_AND = FIRST_RESERVED, TK_BREAK,
+  TK_AND = FIRST_RESERVED, TK_BEGIN, TK_BREAK,
   TK_DO, TK_ELSE, TK_ELSEIF, TK_END, TK_FALSE, TK_FOR, TK_FUNCTION,
   TK_GOTO, TK_IF, TK_IN, TK_LOCAL, TK_NIL, TK_NOT, TK_OR, TK_REPEAT,
   TK_RETURN, TK_THEN, TK_TRUE, TK_UNTIL, TK_WHILE,
diff --git a/lparser.c b/lparser.c
index f4bfc963..ac406d26 100644
--- a/lparser.c
+++ b/lparser.c
@@ -988,23 +988,28 @@ static void parlist (LexState *ls) {
 }
 
 
-static void body (LexState *ls, expdesc *e, int ismethod, int line) {
+static void body (LexState *ls, expdesc *e, int kind, int line) {
   /* body ->  '(' parlist ')' block END */
   FuncState new_fs;
   BlockCnt bl;
   new_fs.f = addprototype(ls);
   new_fs.f->linedefined = line;
   open_func(ls, &new_fs, &bl);
-  checknext(ls, '(');
-  if (ismethod) {
-    new_localvarliteral(ls, "self");  /* create 'self' parameter */
-    adjustlocalvars(ls, 1);
+  if (kind == 2) {
+    setvararg(ls->fs, 0);
+  }
+  else {
+    checknext(ls, '(');
+    if (kind == 1) {
+      new_localvarliteral(ls, "self");  /* create 'self' parameter */
+      adjustlocalvars(ls, 1);
+    }
+    parlist(ls);
+    checknext(ls, ')');
   }
-  parlist(ls);
-  checknext(ls, ')');
   statlist(ls);
   new_fs.f->lastlinedefined = ls->linenumber;
-  check_match(ls, TK_END, TK_FUNCTION, line);
+  check_match(ls, TK_END, kind == 2 ? TK_BEGIN : TK_FUNCTION, line);
   codeclosure(ls, e);
   close_func(ls);
 }
@@ -1050,6 +1055,11 @@ static void funcargs (LexState *ls, expdesc *f) {
       luaX_next(ls);  /* must use 'seminfo' before 'next' */
       break;
     }
+    case TK_BEGIN: {
+      luaX_next(ls);
+      body(ls, &args, 2, line);
+      break;
+    }
     default: {
       luaX_syntaxerror(ls, "function arguments expected");
     }
@@ -1127,7 +1137,7 @@ static void suffixedexp (LexState *ls, expdesc *v) {
         funcargs(ls, v);
         break;
       }
-      case '(': case TK_STRING: case '{': {  /* funcargs */
+      case '(': case TK_STRING: case '{': case TK_BEGIN: {  /* funcargs */
         luaK_exp2nextreg(fs, v);
         funcargs(ls, v);
         break;