Why is a line allowed after a function call?

I am trying to implement a simple C ++ function that checks the syntax of a Lua script. To do this, I use the Lua compiler function luaL_loadbufferx() and then check its return value.

Recently, I ran into a problem because code that I thought should be flagged as invalid was not detected, but instead the script exited later at runtime (e.g. in lua_pcall() ).

Sample Lua code (you can check out the 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 to catch all syntax errors at compile time. So my questions are:

  • What exactly is meant when calling a string value?
  • Why is this syntax allowed first? Is this some Lua function that I don't know about, or is luaL_loadbufferx() broken in this particular example?
  • Is it possible to detect such errors in any other way without running it? Unfortunately, my function does not have access to global variables at compile time, so I just can't just run the code directly through lua_pcall() .

Note. I am using Lua version 5.3.4 ( here, here).

Many thanks for your help.

+5
source share
3 answers

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

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

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

The same thing happens when the exp-literal form is called.

+5
source

I am writing the answer to my question just in case when someone else is faced with a similar problem in the future and is also looking for a solution.


Guide

The Lua manual (in section 3.4.10 - Functional Calls) basically states that there are three different ways to provide arguments to a Lua function.

Arguments have the following syntax:

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

Explanation

As lhf pointed out in his answer , both myfunc()"!" and myfunc(){1,2,3} valid Lua expressions. This means that the Lua compiler does nothing, given that it does not know the return value of the function at compile time.

Source sample code asked in question:

 print("Hello " .. myfunc() "!") 
It can then be rewritten as:
 print("Hello " .. (myfunc()) ("!")) 
Which (when executed) translates into:
 print("Hello " .. ("everyone") ("!")) 
And thus, the result is a run-time error message attempt to call a string value (which can be rewritten as: everyone string is not a function, so you cannot call it).

Decision

As far as I understand, these two alternative methods of providing arguments do not really benefit from the standard syntax func(arg) . This is why I ended up working on Lua parser files. The failure to save this alternative syntax was too big. Here is what I did (applies to v5.3.4):

  • In the lparser.c file lparser.c I was looking for a function:
     static void suffixedexp (LexState *ls, expdesc *v) 
  • Inside this function, I changed the case statement:
     case '(': case TK_STRING: case '{': 
    before
     case '(': 

Attention! Having done this, I changed the Lua language, as its comment says lhf, it can no longer be called pure Lua. If you do not know what exactly you want, I can not recommend this approach.

Using this small modifier, the compiler detects the two above-mentioned alternative syntaxes as errors. Of course, I can no longer use them in Lua scripts, but for my specific application this is just fine.

All I have to do is mark this change somewhere, to find it when upgrading Lua to a higher version.

+2
source

Another approach is to change the string meta tag that makes the call to the string valid.

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

Now, these valid syntax calls for a string will result in string concatenation instead of runtime errors.

+2
source

Source: https://habr.com/ru/post/1269614/


All Articles