根据我对Lua的了解(并根据我在Lua手册中所读的内容),我一直给人的印象是Lua中的标识符仅限于AZ&az&_&digits(并且不能开始使用a数字也不是保留关键字,即local local = 123
)。
现在,我遇到了一些(混淆的)Lua程序,该程序使用各种奇怪的字符作为标识符:
https://i.imgur.com/HPLKMxp.png
-- Most likely, copy+paste won't work. Download the file from https://tknk.io/7HHZ
print(_VERSION .. " " .. (jit and "JIT" or "non-JIT"))
local T = {}
T.math = T.math or {}
T.math.​â®â€‹âŞâ®â€‹ď»żâ€Śâ€âŽ = math.sin
T.math.â¬â€‹ââ¬ââ«â®â€â€¬ = math.cos
for k, v in pairs(T.math) do print(k, v) end
输出:
Lua 5.1 JIT
​â€â€ââ€â€â€â€œfunction:builtin#45
»żâ€‹®â€‹Şâ€ď»żâ€ŚŽ函数:Builtin#44
我不清楚,为什么标识符允许使用这组字符?
换句话说,为什么它是一个完全有效的Lua程序?
答案 0 :(得分:5)
与某些语言不同,Lua并不是由正式规范真正定义的,它涵盖了所有偶然性并完全解释了Lua的所有行为。 Lua的文档中并没有真正解释诸如“ Lua文件编码了什么字符集”这样的简单内容。
所有the docs say about identifiers是:
Lua中的名称(也称为标识符)可以是字母,数字和下划线组成的任意字符串,不是以数字开头也不是保留字。< / p>
但是没有人真正说出“字母”是什么。 Lua所使用的字符集甚至都没有定义。因此,它本质上是依赖于实现的。一个“字母”就是...无论实现如何实现。
因此,假设您正在编写Lua实现。并且您希望用户能够提供Unicode编码的字符串(即Lua文本 之内的字符串)。 Lua 5.3要求这样做。但是,您也不希望他们的文件使用UTF-16编码(也是因为lua_load
获得字节序列,而不是短裤序列)。因此,您的Lua实现假定在lua_load
中获得的字节序列采用UTF-8编码,以便用户可以编写使用Unicode字符的字符串。
在编写此实现的词法分析器/解析器部分时,如何处理?处理UTF-8的最简单,最简单的方法是... 不处理UTF-8 。确实,这就是编码的重点。由于Lua用特定符号定义的所有内容均以ASCII编码,并且ASCII文本也是具有相同含义的UTF-8文本,因此您基本上可以将UTF-8字符串视为ASCII字符串。对于In-Lua字符串,您只需在字符串的开始和结束字符之间复制字节序列即可。
那么您如何处理词法标识符?好吧,你可以问上面的问题。或者您可以问一个更简单的问题:字符是空格,控制字符,数字还是符号? “字母”只是不是其中的一个。
Lua定义它认为什么是“符号”。 ASCII可以告诉您什么是控制字符,空格和数字。在这样的实现中,任何具有ASCII以外的值的UTF-8代码单元都是一个字母。即使从技术上讲,这些代码单元都会解码成Unicode认为是“符号”的东西,但您的词法分析器只是将其威胁为字母。
这种简单的UTF-8词法形式为您提供了快速的性能和较低的内存开销。您不必将UTF-8解码为Unicode代码点,也不需要庞大的Unicode表来告诉您代码点是“符号”还是“空格”或其他。当然,这也是许多基于ASCII的Lua实现自然所不具备的。
因此,即使只是偶然,大多数Lua实现都会以这种方式进行操作。做更多的事情需要刻意的努力。
它还允许用户使用Unicode字符序列作为标识符。这意味着某人可以轻松地以其母语(关键字之外)编写代码。
但是也意味着混淆器有很多方法可以创建“标识符”,这些标识符只是无意义的字节串。确实,由于Unicode有多种方式来“拼写”相同的表观Unicode字符串(除非您直接检查字节),混淆器可以将在文本编辑器中呈现的 标识符识别为相同的文本,而实际上是不同的字符串。
答案 1 :(得分:0)
为了澄清,只有一个标识符T
T.math
是T["math"]
的糖语法,它也扩展到模糊字符串。让key
包含任何字符甚至以数字开头是完全有效的。
现在能够使用.
而不是[ ]
不适用于不符合标识符限制的字符串。有关这些限制的详细信息,请参见Nicol Bolas的答案。