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

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

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

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

Прокрутка удалена:

В поле "Локальная ошибка" Далее: если "Пустое" ничего не делает, тогда делать "Нуль": "ReDim i" ( "Истина для False" ). "Валюта": "Петля": "Else Debug.Assert CCur" (CLng (CINt ( "Ключ" (False Imp True Xor False Eq True) )))): Stop: On Local Error GoTo 0

Весьма разумно, что этот код компилируется (я не пытался его запустить, но это неуместно), если вы просто вставляете его как есть (и затем не трогайте его снова!) в действительный уровень процедуры сфера.

Некоторые наблюдения:

  • Если разделители команд/двоеточия заменены новыми строками, они больше не компилируются
  • On Local Error можно упростить до On Error
  • Вложенные конверсии не представляют особого интереса с первого взгляда, но оказывается, что замена этой серии преобразований и сравнение с простым Debug.Assert True заставляет код последовательно компилироваться, поэтому что-то в нем запутывает компилятор.
  • Если код вставлен, он компилируется; если он каким-либо образом изменил (даже просто удалив Local) после того, как VBE проверил строку, он прекратит компиляцию, и ничто не заставит VBA больше его понять, если строка не удалена и не вставлена ​​повторно.
  • Последний rubberduck грамматика/парсер будучи тщательно смоделированным по фактическим спецификациям VBA, он анализирует и решает просто отлично (что, честно говоря, дует мой разум)
  • Если строка, как известно, не компилируется, а затем вырезает/повторно вставлена, она не компилируется... но повторно вставляя ее снова извне VBE, она неожиданно компилируется.

Вопрос, как этот код удается скомпилировать против спецификаций языка VB? Это ошибка в реализации VB [6 | A | E]? Другими словами, почему/как это работает?

Я думаю, что это имеет какое-то отношение к разделителю команд (:) и синтаксису inline-if - если нет инструкции End If, вещь - это одна строка, а не блок.

Но тогда, что делает этот конкретный код кодом Шредингера? Что делает его одновременно законным и незаконным?

Если код правильно обрабатывается парсером, сгенерированным с использованием формального определения грамматики (ANTLR), то это должна быть законная конструкция? Тогда почему это перестает быть законным, когда вы просто возвращаетесь к этой строке и нажимаете ENTER?

Ответ 1

С такой длинной строкой кода трудно определить, где возникает ошибка компиляции, но есть тонкая разница, которая, как представляется, является VBE, применяющей автокоррекцию к строке, или, более вероятно, после нее, она анализируется.

Это строка original - как вставлена ​​из буфера обмена

Строка выглядит как , пока не переместит курсор на другую строку. Обратите внимание на двоеточие, выделенное жирным шрифтом, между оператором Loop и Else keywords:

В поле "Локальная ошибка" Далее: если "Пустое" ничего не делает, тогда делать "Нуль": "ReDim i" ( "Истина до ложного" ). Валюта: Loop: Else Debug.Assert CCur (CLng (CInt (CBool ​​( False Imp True Xor False Eqv True)))): Stop: On Local Error GoTo 0

Это строка после, вы перемещаете курсор в другую строку:

Обратите внимание, что двоеточие было автоматически удалено VBE. Похоже, что парсер VBE распознает выражение, а затем "оптимизирует" "избыточную" двоеточие.

В поле "Локальная ошибка": "Нет", "Пустое", "Ничего не делает", пока "Null: ReDim я (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 не понимает, что оптимизированная строка имеет проблемы с синтаксисом.

Но почему линия заканчивается хрупкой? В строке есть много отвлекающих и нерелевантных ключевых слов, поэтому давайте резко уменьшим их:

Do While..Loop

Если мы минимизируем сложность линии, чтобы изолировать проблему, мы можем упростить ее:
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.

Ответ 2

Я бы посоветовал ответить, что здесь Resume Next является ключевой инструкцией, так как все остальное недействительно пропустит следующую команду.

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

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