【问题标题】:Why does this code compile when pasted in but fail otherwise?为什么粘贴时此代码会编译但否则会失败?
【发布时间】:2016-05-10 20:06:54
【问题描述】:

一位朋友让我查看this page,并注意到其中一位论坛用户的签名中有一段奇怪的代码。

代码是单行的,如下所示:

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

已移除滚动:

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

非常令人兴奋的是,如果您只是将它按原样粘贴(然后不要再触摸它!)到有效的过程级别,那么该代码会编译(我没有尝试运行它,但这无关紧要)范围。

一些观察:

  • 如果将指令分隔符/冒号替换为新行,则不再编译
  • On Local Error 可以简化为 On Error
  • 乍一看,嵌套转换并没有什么特别的意义,但事实证明,用简单的Debug.Assert True 替换这一系列转换和比较可以使代码始终编译,所以 something in编译器搞砸了。
  • 如果粘贴代码,则编译;如果在 VBE 验证该行之后以任何方式对其进行了修改(甚至只是删除了 Local),它会停止编译,并且似乎没有任何东西可以让 VBA 再理解它,除非该行被删除并重新粘贴。
  • 最新的 语法/解析器非常接近实际的 VBA 规范 it parses and resolves just fine(这真的让我大吃一惊)
  • 如果已知该行无法编译,然后剪切/重新粘贴,它不会编译...但从 VBE 外部再次重新粘贴,它会突然编译。

问题是,这段代码是如何根据 VB 语言规范编译的?它是 VB[6|A|E] 实现中的错误吗?换句话说,为什么/如何工作

认为它与指令分隔符 (:) 和 inline-if 语法有关 - 鉴于没有 End If 语句,事情 单行而不是块。

但是,是什么让特定代码成为薛定谔的代码?是什么让它既合法又非法?

如果代码被使用正式语法定义 (ANTLR) 生成的解析器正确解析,那么它一定是合法构造吗?那为什么当你只是回到那一行并按 ENTER 时它就不再合法了?

【问题讨论】:

  • 在那只讨厌的猫消失之前,我正要纠正给薛定谔,再次
  • 是否将嵌套转换简化为 True,让代码保持相同的 2 个状态?
  • @ThunderFrame it... 没有(当它显示 Debug.Assert True 时,您可以再次编辑它,它保持可编译状态)。似乎嵌套转换确实很重要。
  • 有趣的是,如果您手动输入它,它也不会编译。是否可以在文本中隐藏控制代码?这在很久以前是可能的,但我认为你不能再这样做了。
  • 无法在 VB6 上重现。

标签: rubberduck vba vb6 specifications vbe


【解决方案1】:

有了这么长的代码行,很难发现编译错误的来源,但有一个细微的差别,似乎是 VBE 对行应用了自动更正,或者更有可能是在它被解析之后.

这是 original 行 - 从剪贴板粘贴

该行看起来像这样直到您将光标移动到另一行。 请注意LoopElse 关键字之间的冒号,以粗体表示

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

这是您将光标移动到另一行之后的行:

请注意,冒号已被 VBE 自动删除。看起来 VBE 解析器识别出该语句,然后“优化”掉“冗余”冒号。

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

如果您加回冒号到处于无效语法状态的行,则自动更正再次启动,但您的行恢复为有效,但脆弱 代码。

因此,VBE 似乎解析了该行,识别了优化(多余的冒号),然后应用了修复,但 VBE 没有意识到优化的行存在语法问题。

但是为什么这条线最终会变得脆弱?该行中有很多令人分心且不相关的关键字,因此让我们大幅减少它:

执行While..循环

如果我们尽量减少线路的复杂性,以隔离问题,我们可以将线路简化为:
If True Then Do While True: Beep: Loop: Else

其中,VBE 再次自动更正为 fragile 行:
If True Then Do While True: Beep: Loop Else

但我们可以更进一步,将这条线缩短为一条不合逻辑的短线:
If True Then Do: Loop: Else

VBE 再次尽职尽责地删除冒号以生成以下行:(请勿执行此行,否则您将挂起 EXCEL)
If True Then Do: Loop Else

当..温德

重复该行,但将 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 构造存在冲突。

要点:不要在包含Else 语句的单行If 语句中使用Do..Loop

【讨论】:

    【解决方案2】:

    我会冒险回答 Resume Next 是这里的关键指令,因为任何其他无效的内容都会跳到下一条指令。

    冒号分隔命令,就像它们是新行一样。

    其他方面确实很有趣。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-02-10
      • 2017-09-18
      • 2016-02-27
      • 2012-01-03
      • 2017-05-14
      • 2012-10-12
      • 1970-01-01
      相关资源
      最近更新 更多