Почему этот код компилируется при вставке, но в противном случае происходит сбой?
Друг заставил меня взглянуть на эту страницу и заметил странный кусок кода в подписи одного из пользователей форума.
Код является однострочным и выглядит следующим образом:
On Local Error Resume Next: If Not Empty Is Nothing Then Do While Null: ReDim i(True To False) As Currency: Loop: Else Debug.Assert CCur(CLng(CInt(CBool(False Imp True Xor False Eqv True)))): Stop: On Local Error GoTo 0
Прокрутка удалена:
При локальной ошибке возобновить следующее: если не пусто, то ничего не делать, тогда делать пока Null: ReDim i(True To False) в качестве валюты: Loop: Else Debug.Assert CCur(CLng(CInt(CBool (False Imp True True или False Eqv True)))): Stop: при локальной ошибке GoTo 0
Потрясающе, этот код компилируется (я не пытался его запускать, но это не имеет значения), если вы просто вставляете его как есть (а затем не трогаете его снова!) В допустимую область действия уровня процедуры.
Некоторые наблюдения:
- Если разделители команд / двоеточия заменены новыми строками, он больше не компилируется
On Local Error
можно упростить доOn Error
- На первый взгляд, вложенные преобразования не представляют особого интереса, но оказывается, что заменить эту серию преобразований и сравнений простым
Debug.Assert True
делает код постоянно компилируемым, поэтому что-то там портит компилятор. - Если код вставлен, он компилируется; если он изменен каким-либо образом (даже просто удалив
Local
) после того, как VBE проверил строку, она перестает компилироваться, и кажется, что VBA больше ничего не понимает, если строка не удалена и не вставлена повторно. - Последняя грамматика / синтаксический анализатор rubberduck, тесно связанная с реальными спецификациями VBA, анализирует и разрешает очень хорошо(что, честно говоря, поражает воображение)
- Если известно, что строка не компилируется, а затем вырезается / повторно вставляется, она не компилируется... но повторно вставляет ее снова из-за пределов VBE, она внезапно компилируется.
Вопрос в том, как этот код компилируется в соответствии со спецификациями языка VB? Это ошибка в реализации VB[6|A|E]? Другими словами,почему / как это работает?
Ядумаю, что это как-то связано с разделителем инструкций (:
) и встроенный синтаксис - если нетEnd If
Утверждение, что это одна строка, а не блок.
Но тогда, что делает этот конкретный код кодом Шредингера? Что делает его одновременно и легальным, и нелегальным?
Если код правильно анализируется синтаксическим анализатором, сгенерированным с использованием формального определения грамматики (ANTLR), то это должна быть допустимая конструкция? Тогда почему это перестает быть законным, когда вы просто возвращаетесь к этой строке и нажимаете ENTER?
2 ответа
С такой длинной строкой кода трудно определить, где появляется ошибка компиляции, но есть небольшая разница, которая заключается в том, что VBE применяет автозамену к строке, так как, или, более вероятно, после ее анализа.
Это оригинальная строка - как вставлено из буфера обмена
Строка будет выглядеть так, пока вы не переместите курсор на другую строку. Обратите внимание на двоеточие, выделенное жирным шрифтом, между Loop
а такжеElse
утверждениеключевых слов:
При локальной ошибке возобновить следующее: если не пусто, то ничего не значит, тогда делать пока ноль: ReDim i(True To False) в качестве валюты:
Loop: Else
Debug.Assert CCur (CLng (CInt (CBool (False Imp True Xor False Eqv True)))): Stop: On Local Error GoTo 0
Это строкапосле перемещения курсора на другую строку:
Обратите внимание, что двоеточие было автоматически удалено VBE. Кажется, что синтаксический анализатор VBE распознает оператор, а затем "оптимизирует" "лишнее" двоеточие.
При локальной ошибке возобновить следующее: если не пусто, то ничего не значит, тогда делать пока ноль: ReDim i(True To False) в качестве валюты:
Loop Else
Debug.Assert CCur (CLng (CInt (CBool (False Imp True Xor False Eqv True)))): Stop: On Local Error GoTo 0
Если вы добавляете двоеточие обратно в строку с недопустимым синтаксическим состоянием, тогда автозамена снова включается, но ваша строка возвращается к действительному, но хрупкому коду.
Таким образом, кажется, что VBE анализирует строку, идентифицирует оптимизацию (избыточное двоеточие), а затем применяет исправление, но VBE не осознает, что у оптимизированной строки есть проблемы с синтаксисом.
Но почему линия оказывается хрупкой? В строке много отвлекающих и нерелевантных ключевых слов, поэтому давайте резко их уменьшим:
Делай пока... петля
Если мы минимизируем сложность линии, чтобы изолировать проблему, мы можем упростить линию до:
If True Then Do While True: Beep: Loop: Else
Который, опять же, VBE автоматически исправляет в хрупкую линию:If True Then Do While True: Beep: Loop Else
Но мы можем пойти еще дальше и сократить строку до нелогично короткой:If True Then Do: Loop: Else
И VBE, опять же, покорно удаляет двоеточие, чтобы произвести эту строку: (НЕ ВЫПОЛНЯЙТЕ ЭТУ ЛИНИЮ, ИЛИ ВЫ НЕ ВЕРНЕТЕ EXCEL)If True Then Do: Loop Else
While..Wend
Повторяя линию, но меняя местами
Do While Loop
для пожилыхWhile Wend
синтаксис:If True Then While True: Beep: Wend: Else
Опять же, VBE оптимизирует толстую кишку, чтобы дать:If True Then While True: Beep: Wend Else
Но теперь линия больше не хрупкая!
Итак, While..Wend является более старой конструкцией, а конструкция Do..Loop более новой (и более гибкой), но, похоже, синтаксический анализатор VBE (и оптимизатор синтаксиса) борется с конструкцией Do..Loop.
Ключевой момент: не используйте Do..Loop
в одной линии If
заявления, которые включают Else
заявление.
Я бы хотел ответить, что Resume Next
является ключевой инструкцией здесь, так как все остальное недействительно будет переходить к следующей инструкции.
Двоеточие разделяет команды, как если бы они были новыми строками.
В остальном это действительно очень интересно.