5
votes

I'm trying to implement a simple C++ function, which checks a syntax of Lua script. For that I'm using Lua's compiler function luaL_loadbufferx() and checking its return value afterwards.

Recently, I have ran into a problem, because the code, that I thought should be marked invalid, was not detected and instead the script failed later at a runtime (eg. in lua_pcall()).

Example Lua code (can be tested on official Lua demo):

function myfunc()
   return "everyone"
end

-- Examples of unexpected behaviour:
-- The following lines pass the compile time check without errors.
print("Hello " .. myfunc() "!") -- Runtime error: attempt to call a string value
print("Hello " .. myfunc() {1,2,3}) -- Runtime error: attempt to call a string value

-- Other examples:
-- The following lines contain examples of invalid syntax, which IS detected by compiler.
print("Hello " myfunc() .. "!") -- Compile error: ')' expected near 'myfunc'
print("Hello " .. myfunc() 5) -- Compile error: ')' expected near '5'
print("Hello " .. myfunc() .. ) -- Compile error: unexpected symbol near ')'

The goal is obviously to catch all syntax errors at compile time. So my questions are:

  1. What exactly is meant by calling a string value?
  2. Why is this syntax allowed in the first place? Is it some Lua feature I'm unaware of, or the luaL_loadbufferx() is faulty in this particular example?
  3. Is it possible to detect such errors by any other method without running it? Unfortunately, my function doesn't have access to global variables at compile time, so I can't just just run the code directly via lua_pcall().

Note: I'm using Lua version 5.3.4 (manual here).

Thank you very much for your help.

3

3 Answers

5
votes

Both myfunc() "!" and myfunc(){1,2,3} are valid Lua expressions.

Lua allows calls of the form exp string. See functioncall and prefixexp in the Syntax of Lua.

So myfunc() "!" is a valid function call that calls whatever myfunc returns and call it with the string "!".

The same thing happens for a call of the form exp table-literal.

2
votes

Another approach is to change string's metatable making a call to a string valid.

local mt = getmetatable ""
mt.__call = function (self, args) return self .. args end
print(("x") "y") -- outputs `xy`

Now those valid syntax calls to a string will result in string concatenation instead of runtime errors.

2
votes

I'm writing answer to my own question just in case anyone else stumbles upon the similar problem in the future and also looks for solution.


Manual

Lua manual (in its section 3.4.10 – Function Calls) basically states, that there are three different ways of providing arguments to Lua function.

Arguments have the following syntax:

  args ::= ‘(’ [explist] ‘)’
  args ::= tableconstructor
  args ::= LiteralString
All argument expressions are evaluated before the call. A call of the form f{fields} is syntactic sugar for f({fields}); that is, the argument list is a single new table. A call of the form f'string' (or f"string" or f[[string]]) is syntactic sugar for f('string'); that is, the argument list is a single literal string.

Explanation

As lhf pointed out in his answer, both myfunc()"!" and myfunc(){1,2,3} are valid Lua expressions. It means the Lua compiler is doing nothing wrong, considering it doesn't know the function return value at a compile time.

The original example code given in the question:

print("Hello " .. myfunc() "!")
print("Hello " .. (myfunc()) ("!"))
print("Hello " .. ("everyone") ("!"))
attempt to call a string valueeveryone

Solution

As far as I understand, these two alternative ways of supplying arguments have no real benefit over the standard func(arg) syntax. That's why I ended up modyfing the Lua parser files. The disadventage of keeping this alternative syntax was too big. Here is what I've done (relevant for v5.3.4):

  1. In file lparser.c i searched for function:
    static void suffixedexp (LexState *ls, expdesc *v)
  2. Inside this function i changed the case statement:
    case '(': case TK_STRING: case '{':
    to
    case '(':

Warning! By doing this I have modified the Lua language, so as lhf stated in his comment, it can no longer be called pure Lua. If you are unsure whether it is exactly what you want, I can't recommend this approach.

With this slight modification compiler detects the two above mentioned alternative syntaxes as errors. Of course, I can no longer use them inside Lua scripts, but for my specific application it's just fine.

All I need to do is to note this change somewhere to find it in case of upgrading Lua to higher version.