Почему следующий фрагмент кода Lua полностью действителен? - PullRequest
2 голосов
/ 14 апреля 2019

Из моего знания Lua (и согласно тому, что я прочитал в руководствах по Lua), у меня всегда было впечатление, что идентификатор в Lua ограничен только AZ & az & _ & digits (и не может начать использоватьцифра или зарезервированное ключевое слово, т.е. 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
ď ż ‹â € €‹Функция: встроенная # 44

Мне непонятно, почему этот набор символов допускается для идентификатора?
Другими словамиПочему это полностью действующая программа Lua?

Ответы [ 2 ]

5 голосов
/ 14 апреля 2019

В отличие от некоторых языков, Lua на самом деле не определяется формальной спецификацией, которая охватывает все непредвиденные обстоятельства и полностью объясняет все поведение Lua. Что-то простое, например, «какой набор символов представляет собой файл Lua, закодированный в нем», на самом деле не объясняется в документации Lua.

Все документы говорят об идентификаторах :

Имена (также называемые идентификаторами ) в 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. Для строк в Lua вы просто копируете последовательность байтов между начальным и конечным символами строки.

Итак, как вы относитесь к лексическим идентификаторам? Ну, вы могли бы задать вопрос выше. Или вы можете задать гораздо более простой вопрос: является ли символ пробелом, управляющим символом, цифрой или символом? «Письмо» - это просто нечто, что не является одним из тех .

Lua определяет, что он считает "символами". ASCII может сказать вам, что такое управляющий символ, пробел и цифра. В такой реализации любая кодовая единица UTF-8 со значением вне ASCII представляет собой букву . Даже если технически эти кодовые блоки декодируются во что-то, что Unicode воспринимает как «символ», ваш лексер просто угрожает ему как буква.

Эта простая форма лексирования UTF-8 обеспечивает высокую производительность и низкую нагрузку на память. Вам не нужно декодировать UTF-8 в кодовые точки Unicode, и вам не нужна гигантская таблица Unicode, чтобы сказать вам, является ли кодовая точка "символом" или "пробелом" или чем-то еще. И, конечно же, это то, что естественно выпадает из многих реализаций Lua на основе ASCII.

Так что большинство реализаций Lua сделают это таким образом, хотя бы случайно. Выполнение чего-то большего потребовало бы преднамеренных усилий.

Это также позволяет пользователю использовать последовательности символов Unicode в качестве идентификаторов. Это означает, что кто-то может легко написать код на своем родном языке (вне ключевых слов).

Но это также означает, что у обфускаторов есть много способов создать «идентификаторы», которые являются просто строками бессмысленных байтов. Действительно, поскольку в Юникоде есть несколько способов «записать» одну и ту же кажущуюся строку Юникода (если только вы не исследуете байты напрямую), обфускаторы могут создавать идентификаторы, которые появляются при визуализации в текстовом редакторе, чтобы все они были один и тот же текст, хотя на самом деле это разные строки.

0 голосов
/ 14 апреля 2019

Для уточнения есть только один идентификатор T

T.math - синтаксис сахара для T["math"], который также распространяется на запутанные строки. Вполне допустимо, чтобы key содержал любые символы или даже начинался с цифры.

Теперь возможность использовать . вместо [ ] не работает со строкой, которая не соответствует ограничениям идентификатора. См. Ответ Николаса Боласа о том, как преодолеть эти ограничения.

...