Почему этот код компилируется при вставке, но в противном случае происходит сбой?

Друг заставил меня взглянуть на эту страницу и заметил странный кусок кода в подписи одного из пользователей форума.

Код является однострочным и выглядит следующим образом:

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: ElseDebug.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 является ключевой инструкцией здесь, так как все остальное недействительно будет переходить к следующей инструкции.

Двоеточие разделяет команды, как если бы они были новыми строками.

В остальном это действительно очень интересно.

Другие вопросы по тегам