2015-11-01 105 views
1

我正在嘗試創建一個公式解析器,並且我當前被困在如何遞歸地將代碼轉換爲表達式數組。以下面的公式,例如:遞歸解析預格式化數組

$formula = " 
    @VAR[a, 3]; 
    @IF[ (a <= 3) & (a = 3) ]: 
     @VAR[a, a + 4]; 
     @IF[ a > 5 ]: 
      @USE[a]; 
     @ENDIF 
    @ELSEIF[ a > 4 ]: 
     @VAR[a, 2]; 
    @ELSE: 
     @VAR[a, 5]; 
    @ENDIF 
    @VAR[a,5]; 
    @USE[a]; 
"; 

應該輸出:

{ 
    "0": "VAR[a, 3];", 
    "IF[ (a <= 3) & (a = 3) ]:": { 
     "0": "VAR[a, a + 4];", 
     "1": "ENDIF", 
     "IF[ a > 5 ]:": [ 
      "USE[a];" 
     ] 
    }, 
    "ELSEIF[ a > 4 ]:": [ 
     "VAR[a, 2];" 
    ], 
    "ELSE:": [ 
     "VAR[a, 5];" 
    ], 
    "1": "ENDIF", 
    "2": "VAR[a,5];", 
    "3": "USE[a];", 
} 

所以,我可以通過每個項目迭代並評估每個表達式。

我目前有以下代碼,它不輸出預期的格式。

<?php 

$formula = " 
    @VAR[a, 3]; 
    @IF[ (a <= 3) & (a = 3) ]: 
     @VAR[a, a + 4]; 
     @IF[ a > 5 ]: 
      @USE[a]; 
     @ENDIF 
    @ELSEIF[ a > 4 ]: 
     @VAR[a, 2]; 
    @ELSE: 
     @VAR[a, 5]; 
    @ENDIF 
    @VAR[a,5]; 
    @USE[a]; 
"; 

$formulas = explode("@", $formula); 
$result = parse($formulas); 

echo json_encode($result); 
function parse($lines ){ 
    $exec_tree = array(); 
    foreach($lines as $i => $block){ 
     unset($lines[$i]); 
     $block = trim(str_replace(array(" ") , "" , preg_replace('/\s\s+/', ' ', $block))); 
     if(trim($block) != ""){ 

      // MATCH Variable assignments 
      if(preg_match('/VAR\[(.*)\]\;?/', $block)){ 
       $exec_tree[] = $block;    
      } 
      // MATCH USE Statements 
      if(preg_match('/USE\[(.*)\]\;?/', $block)){ 
       $exec_tree[] = $block; 
      }  
      // MATCH IFs 
      if(preg_match('/^IF\[(.*)\]\:/', $block)){ 
       $exec_tree[$block] = parse($lines); 
      }   
      // MATCH ELSEIFs 
      if(preg_match('/^ELSEIF\[(.*)\]\:/', $block)){ 
       $exec_tree[$block] = parse($lines); 
      }  
      // MATCH ELSEs 
      if(preg_match('/^ELSE:/', $block)){ 
       $exec_tree[$block] = parse($lines); 
      }  
      // MATCH ENDIFs 
      if(preg_match('/^ENDIF/', $block)){ 
       break; 
      } 
     } 
    } 
    return $exec_tree; 
} 

代碼本質上是遞歸的,但我認爲我錯過了遞歸的終止。它應該以ENDIF關鍵字結束。任何人都可以指出我正確的方向將不勝感激。

現在這是它的輸出:(JSON格式)

[ 
"VAR[a,3];", 
[ 
    "IF[(a<=3)&(a=3)]:", 
    [ 
     "VAR[a,a+4];", 
     [ 
      "IF[a>5]:", 
      [ 
       "USE[a];" 
      ] 
     ], 
     "USE[a];" 
    ] 
], 
"VAR[a,a+4];", 
[ 
    "IF[a>5]:", 
    [ 
     "USE[a];" 
    ] 
], 
"USE[a];" 

]

感謝,

回答

1

誠然不完全乾淨的解決方案,但工作和易於重構:

<?php 

$formula = " 
    @VAR[a, 3]; 
    @IF[ (a <= 3) & (a = 3) ]: 
     @VAR[a, a + 4]; 
     @IF[ a > 5 ]: 
      @USE[a]; 
     @ENDIF 
    @ELSEIF[ a > 4 ]: 
     @VAR[a, 2]; 
    @ELSE: 
     @VAR[a, 5]; 
    @ENDIF 
    @VAR[a,5]; 
    @USE[a]; 
"; 

$formulas = explode("@", $formula); 
$rec = false; 
$result = parse($formulas, $rec); 

echo json_encode($result, JSON_PRETTY_PRINT); 

function parse(&$lines, &$rec) { 
    $exec_tree = array(); 
    while ((bool) $lines === true) { 
     $block = array_shift($lines); 

     $block = trim(str_replace(array(" "), "", preg_replace('/\s\s+/', ' ', $block))); 
     if (trim($block) != "") { 

      // MATCH Variable assignments 
      if (preg_match('/VAR\[(.*)\]\;?/', $block)) { 
       $exec_tree[] = $block; 
      } elseif (preg_match('/USE\[(.*)\]\;?/', $block)) { 
       $exec_tree[] = $block; 
      } elseif (preg_match('/^IF\[(.*)\]\:/', $block)) { 
       $rec = true; 
       $exec_tree[ $block ] = parse($lines, $rec); 
      } elseif (preg_match('/^ELSEIF\[(.*)\]\:/', $block)) { 
       $rec = !$rec; 
       if ($rec === false) { 
        array_unshift($lines, $block); 
        break; 
       } else { 
        $exec_tree[ $block ] = parse($lines, $rec); 
       } 
      } elseif (preg_match('/^ELSE:/', $block)) { 
       $rec = !$rec; 
       if ($rec === false) { 
        array_unshift($lines, $block); 
        break; 
       } else { 
        $exec_tree[ $block ] = parse($lines, $rec); 
       } 
      } elseif (preg_match('/^ENDIF/', $block)) { 
       $rec = !$rec; 
       if ($rec === false) { 
        array_unshift($lines, $block); 
        break; 
       } else { 
        $exec_tree[] = $block; 
       } 
      } 
     } 
    } 

    return $exec_tree; 
} 

回報

{ 
    "0": "VAR[a,3];", 
    "IF[(a<=3)&(a=3)]:": { 
     "0": "VAR[a,a+4];", 
     "IF[a>5]:": [ 
      "USE[a];" 
     ], 
     "1": "ENDIF" 
    }, 
    "ELSEIF[a>4]:": [ 
     "VAR[a,2];" 
    ], 
    "ELSE:": [ 
     "VAR[a,5];" 
    ], 
    "1": "ENDIF", 
    "2": "VAR[a,5];", 
    "3": "USE[a];" 
} 

訣竅真的只是以某種方式跟蹤您是否是在一個塊已經和突破上的其他人,ELSEIF和ENDIF,但仍附加這些值的最終結果。

+0

我只想澄清一下,&$ rec有什麼用? –

+0

哦。這是一個遞歸標誌,注意是否已達到最後一個案例。 –

+0

工作,非常感謝! :) –