Function without brackets lua c++

203 views Asked by At

I am creating a application that plays music based off the functions called in a Lua script. I call it musica. The problem is that I need a function that doesn't need brackets. Like this:

play note("A")

This is my full code:

#include <iostream>
#include <string>

extern "C"
{
#include "../lua/include/lua.h"
#include "../lua/include/lauxlib.h"
#include "../lua/include/lualib.h"
}
/*
Message codes

M8I5H: Information
MSKIE: Syntax error
M3UET: Unknown error

*/
enum class MUSICA_NOTE_ENUM
{
A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z
};

int musica_play(lua_State *L)
{
// Not added yet
return 1;
}

int musica_note(lua_State *L)
{
  std::string note = lua_tostring(L, 1);

  lua_pushnumber(L, (lua_Number)((MUSICA_NOTE_ENUM)(note.at(0) & 31)));
  return 1;
}

int main(){
std::string music = R"(a = 75
play note("A")
play note("B")
play note("C")
-- play melody(melody_piono_tune)
)";
std::string m = "play note(\"A\")";

lua_State *L = luaL_newstate();

lua_register(L, "note", musica_note);
lua_register(L, "play", musica_play);

int r = luaL_dostring(L, m.c_str());


if(r == LUA_OK)
{

}else{
printf("[Line: %d, File: %s, MessageCode: MSKIE] MUSICA: There was a problem interperting the file:\n%s\n\n", __LINE__, __FILE__, lua_tostring(L, -1));
}

}

How do make the play function bracketless?

Thank you in advance

1

There are 1 answers

0
Joseph Sible-Reinstate Monica On BEST ANSWER

Standard Lua only supports calling functions without parentheses when the function is called with a table constructor or string literal as its only argument. Since you want to be able to call it with other values, your only option to make this work is to patch the Lua parser. Here's how you could do it by hacking at Lua's source code (this patch was prepared against the Lua 5.4.1 release):

diff --git a/src/lparser.c b/src/lparser.c
index bc7d9a4..d917687 100644
--- a/src/lparser.c
+++ b/src/lparser.c
@@ -1795,6 +1795,44 @@ static void localstat (LexState *ls) {
 }


+static void playstat (LexState *ls) {
+  FuncState *fs = ls->fs;
+  struct LHS_assign v;
+  expdesc *f = &v.v;
+  int line = ls->linenumber;
+  expdesc args;
+  int base, nparams;
+  Instruction *inst;
+
+  /* get the function ready to call like suffixedexp does */
+  singlevar(ls, f);
+  luaK_exp2nextreg(fs, f);
+
+  /* this section taken from the else inside case '(' in funcargs */
+  explist(ls, &args);
+  if (hasmultret(args.k))
+    luaK_setmultret(fs, &args);
+
+  /* this section taken from after the switch statement in funcargs */
+  lua_assert(f->k == VNONRELOC);
+  base = f->u.info;  /* base register for call */
+  if (hasmultret(args.k))
+    nparams = LUA_MULTRET;  /* open call */
+  else {
+    luaK_exp2nextreg(fs, &args);  /* close last argument */
+    nparams = fs->freereg - (base+1);
+  }
+  init_exp(f, VCALL, luaK_codeABC(fs, OP_CALL, base, nparams+1, 2));
+  luaK_fixline(fs, line);
+  fs->freereg = base+1;  /* call remove function and arguments and leaves
+                            (unless changed) one result */
+
+  /* this section taken from the else block in exprstat */
+  inst = &getinstruction(fs, &v.v);
+  SETARG_C(*inst, 1);  /* call statement uses no results */
+}
+
+
 static int funcname (LexState *ls, expdesc *v) {
   /* funcname -> NAME {fieldsel} [':' NAME] */
   int ismethod = 0;
@@ -1932,6 +1970,13 @@ static void statement (LexState *ls) {
       gotostat(ls);
       break;
     }
+    case TK_NAME: {
+      if (!strcmp(getstr(ls->t.seminfo.ts), "play")) {
+        playstat(ls);
+        break;
+      }
+    }
+    /* FALLTHROUGH */
     default: {  /* stat -> func | assignment */
       exprstat(ls);
       break;

This makes play act like a keyword when at the beginning of a statement. It captures expressions after it similar to how local foo, bar, baz = does. I must emphasize that this is really quite a hack. Your other option is to do the same sort of thing with Metalua, but that restricts you to Lua 5.1, which has been EOL for 8 years and has known security issues.