Lua - 为什么允许函数调用后的字符串?

Lua - why is string after function call allowed?

我正在尝试实现一个简单的 C++ 函数,它检查 Lua 脚本的语法。为此,我正在使用 Lua 的编译器函数 luaL_loadbufferx() 并随后检查其 return 值。

最近,我 运行 遇到了一个问题,因为没有检测到 我认为应该标记为无效 的代码,脚本后来失败了运行时间(例如 lua_pcall())。

示例Lua代码(可以在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 ')'

目标显然是在编译时捕获所有语法错误。所以我的问题是:

  1. 调用字符串值到底是什么意思?
  2. 为什么首先允许这种语法?是我不知道的某些 Lua 功能,还是 luaL_loadbufferx() 在这个特定示例中有问题?
  3. 是否可以在不 运行ning 的情况下通过任何其他方法检测此类错误?不幸的是,我的函数在编译时无法访问全局变量,所以我不能直接通过 lua_pcall().
  4. 运行 代码

注意:我使用的是 Lua 版本 5.3.4 (manual here)。

非常感谢您的帮助。

myfunc() "!"myfunc(){1,2,3} 都是有效的 Lua 表达式。

Lua 允许 exp string 形式的调用。请参阅 Syntax of Lua 中的 functioncallprefixexp

所以 myfunc() "!" 是一个有效的函数调用,它调用任何 myfunc returns 并使用字符串 "!".

调用它

对于形式为 exp table-literal.

的调用也会发生同样的事情

我正在写我自己的问题的答案,以防其他人在未来偶然发现类似的问题并寻求解决方案。


手动

Lua manual (in its section 3.4.10 – Function Calls) 基本上指出,可以通过三种不同的方式为 Lua 函数提供参数。

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.


说明

lhf pointed out in 一样,myfunc()"!"myfunc(){1,2,3}都是有效的Lua表达式。这意味着 Lua 编译器没有做错,考虑到它在编译时 不知道 函数 return 值。

问题中给出的原始示例代码:

print("Hello " .. myfunc() "!")
然后可以重写为:
print("Hello " .. (myfunc()) ("!"))
其中(执行时)转化为:
print("Hello " .. ("everyone") ("!"))
从而导致运行时错误消息 attempt to call a string value(可以重写为:字符串 everyone 不是函数,因此您不能调用它)。


解决方案

据我所知,这两种提供参数的替代方法与标准 func(arg) 语法相比并没有真正的好处。这就是我最终修改 Lua 解析器文件的原因。保留这种替代语法的缺点太大了。这是我所做的(与 v5.3.4 相关):

  1. 在文件 lparser.c 中我搜索了函数:
    static void suffixedexp (LexState *ls, expdesc *v)
    
  2. 在这个函数中,我更改了 case 语句:
    case '(': case TK_STRING: case '{':
    
    case '(':
    

警告!通过这样做,我修改了 Lua 语言,所以正如 lhf 在他的评论中所说,它不能再被称为 pure Lua。如果你不确定它是否正是你想要的,我不推荐这种方法。

通过此轻微修改,编译器将上述两种替代语法检测为错误。当然,我不能再在 Lua 脚本中使用它们,但对于我的特定应用程序来说,这很好。

我需要做的就是在某处记录这个变化,以便在升级Lua到更高版本时找到它。

另一种方法是更改​​字符串的元表,使对字符串的调用有效。

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

现在,那些对字符串的有效语法调用将导致字符串连接,而不是运行时错误。