2016-11-05 12 views
2

条件に基づいてルールを呼び出すいくつかのVBAコード(テキストファイルに格納されている)を考えて、コードを解析して、そのルールを生成するために知る必要があります(ルールの本質をより理解しやすくするため)。Pythonを使用してVBAコードを小さなコードに解析する

私は、ルールの一部を正規表現しようとしましたが、存在する場合はホイールを再作成したくありませんでした。 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時だったので私は寝ました:) ...私は、この時点で周りので、まだ本当にずさんなものを投げたが、あなたのVBAコードがだらしなく、ほとんどのExcelのコードは例えば、書かれているように見えるように書かれている場合は、私の進捗

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) 
+0

ハッキングで動作するルールが1つあります(4999)。 –

+0

"Python"はなぜこの質問の基本的な部分ですか? –

+0

理由はありません - もっと単純なPythonパーサーがあるかもしれないと思っていました。ルールは(潜在的に/最終的に)VBAからPythonに移行するので、Pythonの経験を持つデータアナリストはルールを維持することができます。もしルールが個別に作られれば、移動が容易になるかもしれないと思っていました。それはそれほど時間を費やすつもりはないという要件ではありませんでした。 – NikT

答えて

1

に更新したかったんです既存のVBAコードに頼ることができず、きちんとした、きちんとした、簡単に解読可能な構造を持つことはできません。それほど一貫性がない場合は、ad hoc/regexベースのテクニックを使用するチャンスはありません。

この場合、完全なVBAパーサーが必要ですが、1つの悪い近似ではありません。 Regexes(よく知られている事実)を使ってまともなパーサを書くことはできません。より合理的な解析技術を使用すると、文書化が非常に難しいため、文法を定義するのが難しくなります。

ここでの教訓は、おそらくあなた自身のパーサーを書くことを試みたくないということです。あなたは働いているものを見つけ出し、それを使う方が良いです。私はあなたがPythonで起こっていることはもちろんのこと、あなたが簡単に見つけ出すことを見つけることはできません。私は間違っている可能性があります。

私の会社は、DMSソフトウェアリエンジニアリングツールキットの上に構築された have such a parser for VB6(本質的にVBAです)です。このパーサは何百万行ものコードでテストされています。自動的に は、プログラムを表す抽象構文木を構築する。あなたがやりたいために

、あなたがしなければならない:

  • 解析ソースコード
  • 検索ルールの呼び出し
  • 発見ルールの呼び出し
  • への関数開始からの制御流路そのパスに沿って条件を構成する

VBAにはgoto文があることを前提としていますあなたが関数のエントリからルールの呼び出しまでのパスを見つけるためにそれに沿ってナビゲートできるように、コードのコントロールフローグラフを構築する必要があります。 DMSは、コード内のパターンの検索、制御およびデータフローグラフの作成、フローグラフの歩行、および元のコードのサブツリーから有効なプログラムチャンクを構成するためのサポートを提供します。あなたはこれをすべて必要とするでしょう。

[すべての妥当なVBAサブルーチンであり、いくつかの条件の組み合わせではなく、あなたの例に基づいて、各ルール呼び出しからbackward slicesを計算するように見えます。それは私が上で説明したすべての機械を必要とします。]

これは心がかすかな仕事でも週末の運動でもないでしょう。しかし、それは実行可能性が高いでしょう。

あなたの目標が「読みやすくするために」漠然としているとすれば、これを正しく行うためのエンジニアリングの努力を正当化できない場合があります。

アドホックなアプローチを主張するならば、おそらく終了しないでしょうし、もしあなたが "もっと読みやすい"バージョンのルールが得られれば、実際の真実とあなたの読者ではないことがわかります反乱するでしょう。誰も間違っているかもしれないことを読んで時間を無駄にしたくない。

関連する問題