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

时间:2017-07-07 17:52:42

标签: lua luac

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

最近,我遇到了一个问题,因为我认为我认为应该标记为无效的代码未被检测到,而是脚本在运行时稍后失败(例如{{1} }})。

示例Lua代码(可在official Lua demo上测试):

lua_pcall()

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

  1. 调用字符串值究竟是什么意思
  2. 为什么首先允许这种语法?它是一些我不知道的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 ')' 在这个特定的例子中是否有问题?
  3. 是否可以通过任何其他方法检测此类错误而无需运行它?不幸的是,我的函数在编译时无法访问全局变量,因此我不能直接通过luaL_loadbufferx()运行代码。
  4. 注意:我使用的是Lua版本5.3.4(manual here)。

    非常感谢你的帮助。

3 个答案:

答案 0 :(得分:5)

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

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

因此myfunc() "!"是一个有效的函数调用,它调用myfunc返回的任何内容并使用字符串"!"调用它。

调用 exp table-literal 形式的事情也是如此。

答案 1 :(得分:2)

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

手册

Lua manual (in its section 3.4.10 – Function Calls)基本上说,有三种不同的方式为Lua函数提供参数。

  

参数具有以下语法:

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

解释

lhf中指出his answermyfunc()"!"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 '(':
  3. 警告!通过这样做,我修改了Lua语言,正如他在评论中所述,它不再被称为 pure Lua。如果您不确定它是否正是您想要的,我不推荐这种方法。

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

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

答案 2 :(得分:2)

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

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

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