【问题标题】:Using Python to parse VBA code into smaller pieces of code使用 Python 将 VBA 代码解析为更小的代码片段
【发布时间】:2023-04-02 06:39:01
【问题描述】:

鉴于一些 VBA 代码(存储在文本文件中)会根据条件调用规则,我想解析代码并创建生成该规则所需的所有内容的迷你代码片段(以便理解规则的本质更容易)。

我开始尝试在 python 中对一些规则进行正则表达式,但如果轮子存在,我不想重新创建它。我知道像Retrieving JSON objects from a text file (using Python) 这样的例子来覆盖一个基类来创建一个客户解析器。我不确定是否有任何最适合开始使用的包,并且没有任何运气找到一个。

背景是我想像这样“缩小”大约 5000 条规则,更简单地说明规则周围的逻辑,看看有多少规则受到某个变量的影响,等等。

输入:

Sub One(position As Long)    
    Dim y As Long    
    With TEMP_ARRAY(position)
        'Comments
        If .VAR_A = "A" And .VAR_B = "B" And .VAR_C = "C" Then
            Call Some_Rule("Rule 1")
        End If

        'More Comments
        If IsEmpty(.SUB_ARRAY) Then
            Call Some_Rule("Rule 2")
        Else
            If .VAR_A = 2 Then
                If .VAR_B <> "" Then
                    'Some more comments
                    For y = 0 To UBound(.SUB_ARRAY)
                        If .SUB_ARRAY(y, 2) = 1 Or .SUB_ARRAY(y, 2) = 2 Then Exit For
                    Next y
                    If y = UBound(.SUB_ARRAY, 1) + 1 Then
                        Call Some_Rule("Rule 3")
                    End If
                Else
                    'Still more comments
                    Select Case .SUB_ARRAY(0, 2)
                        Case 3
                            Call Some_Rule("Rule 4")
                        Case 4
                            Call Some_Rule("Rule 5")
                    End Select
                End If
            End If
        End If
    End With
End Sub

所需的输出:

## RULE 1
Sub One(position As Long)
    With TEMP_ARRAY(position)
        'Comments
        If .VAR_A = "A" And .VAR_B = "B" And .VAR_C = "C" Then
            Call Some_Rule("Rule 1")
        End If
    End With
End Sub

## RULE 2
Sub One(position As Long)
    With TEMP_ARRAY(position)
        'More Comments
        If IsEmpty(.SUB_ARRAY) Then
            Call Some_Rule("Rule 2")
        End If
    End With
End Sub

## RULE 3
Sub One(position As Long)
    Dim y As Long
    With TEMP_ARRAY(position)
        'More Comments
        If IsEmpty(.SUB_ARRAY) Then
        Else
            If .VAR_A = 2 Then
                If .VAR_B <> "" Then
                    'Some more comments
                    For y = 0 To UBound(.SUB_ARRAY)
                        If .SUB_ARRAY(y, 2) = 1 Or .SUB_ARRAY(y, 2) = 2 Then Exit For
                    Next y
                    If y = UBound(.SUB_ARRAY, 1) + 1 Then
                        Call Some_Rule("Rule 3")
                    End If
                End If
            End If
        End If
    End With
End Sub

## RULE 4
Sub One(position As Long)
    With TEMP_ARRAY(position)
        'More Comments
        If IsEmpty(.SUB_ARRAY) Then
        Else
            If .VAR_A = 2 Then
                If .VAR_B <> "" Then
                Else
                    'Still more comments
                    Select Case .SUB_ARRAY(0, 2)
                        Case 3
                            Call Some_Rule("Rule 4")
                    End Select
                End If
            End If
        End If
    End With
End Sub

## RULE 5
Sub One(position As Long)
    With TEMP_ARRAY(position)
        'More Comments
        If IsEmpty(.SUB_ARRAY) Then
        Else
            If .VAR_A = 2 Then
                If .VAR_B <> "" Then
                Else
                    'Still more comments
                    Select Case .SUB_ARRAY(0, 2)
                        Case 4
                            Call Some_Rule("Rule 5")
                    End Select
                End If
            End If
        End If
    End With
End Sub

编辑:这是我到目前为止所做的(更多代码,但这是它的核心)。基本上,找到以“Some_Rule”开头的行(使用正则表达式),然后从向上方向开始调用此函数。当它找到一个打开的标签时,它会改变方向并开始寻找它的结束标签,然后从它停止的地方重新开始,等等。我以这种方式成功地获得了规则 1,然后是凌晨 4 点,所以我去睡觉了 :) ...我现在正在折腾东西,所以仍然很草率,但想更新我的进度

def compile_rule(lines, j, direction, statement_open=False):
    """
    lines           : total lines in file
    j               : current position
    direction       : 1 is up, -1 is down
    statement_open  : vba syntax not yet closed ex: if without end if
    """
    global rule
    j -= direction
    if line_type(lines[j]) in [0, 3] and not statement_open:
        rule.append(lines[j], j, direction)
    elif line_type(lines[j]) == 1 and not statement_open:
        rule.append(lines[j], j, direction)
        rule.start_looking_for(line_check(lines[j]))
        statement_open = True
        direction *= -1
    elif line_type(lines[j]) == 2 and rule.looking_for() == line_check(lines[j]) and statement_open:
        rule.append(lines[j], j, direction)
        statement_open = False
        direction *= -1
    else:
        rule.set_position(j, direction)
    if (j > 0 and j < len(lines) - 1) or (j == 0 and statement_open):
        compile_rule(lines, rule.get_position(direction), direction, statement_open)

【问题讨论】:

  • 那么你有 1 条规则可以通过 hacking 来工作,还有 4999 条规则可以使用?
  • 为什么“Python”是问题的基本部分?
  • 没有理由——认为可能有一些更简单的 python 解析器可用。这些规则将(可能/最终)从 VBA 转移到 Python,以便具有 Python 经验的数据分析师可以维护这些规则。认为如果规则是单独的,它可能会更容易移动——这不是一个要求,所以不打算花太多时间在它上面。
  • 我非常怀疑有人用 Python 构建了一个严肃的 VBA 解析器。所以你真正的问题是将VBA转换为Python?您要求的是 VBA 到 Python 的翻译器,除非您想手动完成。
  • 是的,似乎是这样......不幸的是,我认为这部分必须由手工完成。这是过去 7 年(由许多不同的人)建立的规则集合,需要大量整合和清理。

标签: python vba excel parsing


【解决方案1】:

如果您的 VBA 代码是以大多数 Excel 代码的编写方式编写的,例如,草率,您将无法依赖现有的 VBA 代码来拥有任何漂亮、整洁、易于解码的结构。如果它不是很一致,您将没有机会在其上使用基于 ad hoc/regex 的技术。

在这种情况下,您需要一个完整的 VBA 解析器,而不是一个糟糕的近似值。你不能用正则表达式编写一个像样的解析器(众所周知的事实)。如果您使用更合理的解析技术,您将很难定义语法,因为它的文档记录非常差。

这里的教训是您可能不想尝试编写自己的解析器。你最好找到一个有效的,并使用它。我怀疑你会很容易找到一个,更不用说碰巧在 Python 中的一个了;但我可能是错的。

我的公司碰巧 have such a parser for VB6,(本质上是 VBA)建立在我们的 DMS 软件再工程工具包之上。这个解析器已经在数百万行代码上进行了测试。它会自动 构建代表程序的抽象语法树。

要做你想做的事,你必须:

  • 解析源代码
  • 查找规则调用
  • 发现从函数开始到规则调用的控制流路径
  • 沿该路径编写条件句

鉴于 VBA 具有 goto 语句(您的示例代码没有,但有 5000 个实例,这似乎是一个非常糟糕的选择),您需要构建代码的控制流图,以便您可以沿着它导航找到从函数入口到规则调用的路径。 DMS 支持在代码中查找模式、构建控制和数据流图、沿着流图走动以及从原始代码的子树中组成有效的程序块。您很可能需要所有这些。

[根据您的示例,这些示例似乎都是有效的 VBA 子例程,而不仅仅是一些条件的结合,您似乎希望从每个规则调用中计算 backward slices。这需要我上面描述的所有机制。]

这不是胆小的任务,也不太可能是周末锻炼。但它很可能是可行的。

鉴于您的目标是模糊的“使它们更具可读性”,您可能无法证明工程努力做到这一点是正确的。

如果您坚持采用临时方法,您可能无法完成,并且您可能会发现您的“更易读”版本的规则(如果您得到它们)并不是真正的事实,您的读者会反叛。没有人愿意浪费时间阅读可能出错的内容。

【讨论】:

    猜你喜欢
    • 2017-06-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多