5

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

最近,我遇到了一个问题,因为我认为应该标记为 invalid的代码没有被检测到,而是脚本稍后在运行时失败(例如 in lua_pcall())。

示例 Lua 代码(可以在官方 Lua 演示中测试):

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. 是否可以通过任何其他方法检测此类错误而不运行它?不幸的是,我的函数在编译时无法访问全局变量,所以我不能直接通过lua_pcall().

注意:我使用的是 Lua 版本 5.3.4(此处为手册)。

非常感谢您的帮助。

4

3 回答 3

5

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

Lua 允许以exp string形式调用。参见Lua的语法中的functioncalland 。prefixexp

一个有效的函数调用也是如此myfunc() "!",它调用任何myfunc返回并使用 string 调用它"!"

exp table-literal形式的调用也会发生同样的事情。

于 2017-07-07T18:03:38.373 回答
2

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


手动的

Lua 手册(在其第 3.4.10 节 – 函数调用)基本上指出,有三种不同的方式为 Lua 函数提供参数。

参数具有以下语法:

  args ::= ‘(’ [explist] ‘)’
  args ::= tableconstructor
  args ::= LiteralString
所有参数表达式在调用之前进行评估。f{fields} 形式的调用是 f({fields}); 的语法糖;也就是说,参数列表是一个新表。f'string'(或 f"string" 或 f[[string]])形式的调用是 f('string') 的语法糖;也就是说,参数列表是单个文字字符串。


解释

正如lhf在他的回答中指出的那样,myfunc()"!"myfunc(){1,2,3}都是有效的 Lua 表达式。这意味着 Lua 编译器没有做错任何事情,因为它在编译时不知道函数返回值。

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

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 在他的评论中所说,它不再可以称为Lua。如果您不确定它是否正是您想要的,我不推荐这种方法。

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

我需要做的就是在某处记录此更改,以便在将 Lua 升级到更高版本时找到它。

于 2017-07-07T22:31:54.623 回答
2

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

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

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

于 2017-07-08T08:34:58.653 回答