【问题标题】:Why is the following piece of Lua code, completely valid?为什么下面这段 Lua 代码完全有效?
【发布时间】:2019-04-14 03:31:26
【问题描述】:

根据我的 Lua 知识(以及根据我在 Lua 手册中阅读的内容),我一直认为 Lua 中的标识符仅限于 A-Z & a-z & _ & 数字(并且不能开始使用digit 也不是保留关键字,即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
â¬â€‹â€â«â€â€â€¬ 功能:内置#45
​â®â€‹âŞâ®â€‹ď»żâ€Śâ€âŽ function: builtin#44

我不清楚,为什么标识符允许使用这组字符?
换句话说,为什么它是一个完全有效的 Lua 程序?

【问题讨论】:

  • 显示任何在线 Lua 示例。
  • @MikeV。在线 Lua 示例是什么意思?
  • 无论如何,我发现任何 ASCII 码高于 127 的 Unicode 字符都是完全有效的标识符(仍然不知道为什么在 Lua 中允许这样)。这很有趣,我正在考虑在某个时候做我自己的 Lua 混淆器。
  • 从 0x80 一直到 0xFFFF(检查 Unicode 表)。该范围包含各种字符,并且所有这些字符都可以用作标识符的一部分。我简直不敢相信。谁能解释一下?
  • 例如:tutorialspoint - 无效。任何在线 Lua 编译器也是如此。

标签: lua luajit


【解决方案1】:

与某些语言不同,Lua 并没有真正由正式的规范定义,它涵盖了所有意外情况并完全解释了 Lua 的所有行为。 Lua 的文档中并没有真正解释“编码的 Lua 文件是什么字符集”这样简单的事情。

所有the docs say about identifiers 是:

Lua 中的

名称(也称为标识符)可以是任何由字母、数字和下划线组成的字符串,不以数字开头,也不是保留字。

但没有什么能真正说明“字母”是什么。甚至没有关于 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 字符串(除非您直接检查字节),所以混淆器可以将在文本编辑器中呈现时 出现 的标识符全部设置为相同的文本,但实际上是不同的字符串。

【讨论】:

    【解决方案2】:

    澄清只有一个标识符T

    T.mathT["math"] 的糖语法,这也扩展到混淆字符串。让key 包含任何字符甚至以数字开头都是完全有效的。

    现在能够使用. 而不是[ ] 不适用于不符合标识符限制的字符串。请参阅 Nicol Bolas 的回答,详细了解这些限制。

    【讨论】:

    • "不符合标识符的限制" 是什么让您认为这是真的?我没有看到任何不符合文档内容的内容。
    • @NicolBolas 我指的是T.1(无效)而不是T["1"](有效)
    • 我的问题是什么让你认为 OP 的字符串不符合这些限制?
    • 字符串中有<符号
    • 不,它没有。它有一个字节序列,当通过 UTF-8 解释时会产生一个 Unicode 代码点,该代码点在您的浏览器/文本编辑器中呈现为 似乎是<符号的字形。 < 不是一回事。
    猜你喜欢
    • 2014-02-02
    • 2022-12-01
    • 1970-01-01
    • 2012-12-03
    • 1970-01-01
    • 2022-09-23
    • 1970-01-01
    • 2012-11-04
    • 2023-04-01
    相关资源
    最近更新 更多