Lua - почему разрешена строка после вызова функции?

Я пытаюсь реализовать простую функцию C ++, которая проверяет синтаксис сценария Lua. Для этого я использую функцию компилятора Lua luaL_loadbufferx() и потом проверяю возвращаемое значение.

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

Спасибо большое за помощь.


person Electrix    schedule 07.07.2017    source источник


Ответы (3)


Оба myfunc() "!" и myfunc(){1,2,3} являются допустимыми выражениями Lua.

Lua допускает вызовы формы exp строка. См. functioncall и prefixexp в Синтаксисе Lua.

Итак, myfunc() "!" - это допустимый вызов функции, который вызывает все, что возвращает myfunc, и вызывает его со строкой "!".

То же самое происходит при вызове формы exp table-literal.

person lhf    schedule 07.07.2017
comment
Большое спасибо за объяснение. Я попытаюсь выяснить, можно ли отключить эту функцию Lua, потому что отсутствует ... очень распространенная ошибка при написании сценария с большим количеством текста. Кроме этого, я не вижу другого способа добиться того, чего хочу. - person Electrix; 07.07.2017
comment
@Electrix, Нет возможности отключить эту функцию Lua, потому что она есть в грамматике. Вы можете сменить парсер, но тогда это уже не будет Lua. - person lhf; 07.07.2017

Другой подход - изменить метатаблицу строки, сделав вызов допустимой строки.

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

Теперь эти допустимые синтаксические вызовы строки приведут к объединению строк вместо ошибок времени выполнения.

person Tymur Gubayev    schedule 08.07.2017
comment
+1, спасибо за отличную идею. Хотя это не поможет обнаружить нарушенный синтаксис, оно исправляет его на лету. Я обязательно рассмотрю этот подход. - person Electrix; 08.07.2017

Я пишу ответ на свой вопрос на тот случай, если кто-то еще наткнется на аналогичную проблему в будущем и также будет искать решение.


Руководство

Руководство по Lua (в его разделе 3.4.10 - Вызов функций) в основном заявляет, что есть три разных способа предоставления аргументов функции Lua.

Аргументы имеют следующий синтаксис:

  args ::= ‘(’ [explist] ‘)’
  args ::= tableconstructor
  args ::= LiteralString
Все выражения аргументов оцениваются перед вызовом. Вызов формы f {fields} является синтаксическим сахаром для f ({fields}); то есть список аргументов - это одна новая таблица. Вызов формы f'string '(или f «строка» или f [[строка]]) является синтаксическим сахаром для f (' строка '); то есть список аргументов представляет собой одну буквальную строку.


Объяснение

Как указано в его ответе 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 '{':
    to
    case '(':

Внимание! Сделав это, я изменил язык Lua, поэтому, как сказал lhf в своем комментарии, он больше не может называться чистым Lua. Если вы не уверены, что это именно то, что вам нужно, я не могу рекомендовать этот подход.

С помощью этой небольшой модификации компилятор обнаруживает два вышеупомянутых альтернативных синтаксиса как ошибки. Конечно, я больше не могу использовать их в сценариях Lua, но для моего конкретного приложения это нормально.

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

person Electrix    schedule 07.07.2017
comment
Вы нарушите общий код, такой как require"foo". Но вы об этом знаете. - person lhf; 08.07.2017
comment
these two alternative ways of supplying arguments have no real benefit Эта функция активно используется при создании различных DSL. Отказ от этого только подрывает вашу свободу в построении синтаксиса - person Vlad; 08.07.2017
comment
@Vlad, в моем случае Lua используется в большом игровом проекте для написания простых квестов (часто меняется качество авторов). Хотя иногда эти квесты содержат расширенный синтаксис, большая часть кода связана со словами, предложениями, заголовками и т. Д. Эти квесты затем обрабатываются внутри игрового ядра, и хотя их немного special (что-то вроде конфигураций для других квестов), они сохраняют тот же простой синтаксис, и я, честно говоря, не думаю, что этот альтернативный синтаксис аргументов когда-либо понадобится. Тем не менее, если у вас есть другие идеи, как подойти к этой проблеме, я обязательно ее рассмотрю. - person Electrix; 08.07.2017