Проверка правильности расстановки парных скобок


Проверка правильности расстановки скобок происходит
с помощью стека по простому алгоритму:
* Все открывающие скобки - помещаем в стек
* При встрече закрывающей скобки - проверяем, образует
  ли закрывающая скобка пару с последней в стеке.
* Если пара образуется - извлекаем последний элемент из стека,
  продолжаем работу. Если нет - скобки не парны
* В финале нужно проверить, что стек пуст. Не пустой стек -
  свидетельство о непарности скобок.

Приведенный алгоритм работает красиво, но не сможет корректно отслеживать
парность кавычек, слешей, поскольку в этом случае из-за одинаковых
открывающего и закрывающего элементов невозможно корректно определить
вложенность.

/*///////////////////////////////////////////////////////////////

 CheckPara "()", PHP, 2011-01-01
 
 Artix, master@7masterov.ru, icq:53666599, skype:artixmaster
 
 * Error in code? Nothing is perfect!
 * Free source for free Linux, use it for free!
 * Please, do not remove this comment!

///////////////////////////////////////////////////////////////*/

function check($s)
{
    $para = array(
        '(' => ')',
        '[' => ']',
        '{' => '}',
        '<' => '>'
    );
    $para = array_flip($para);
    $stack = array();
    $stack_size = 0;
    for($i=0;$i<strlen($s);$i++)
    {
        if (in_array($s{$i},array_values($para)))
            $stack[$stack_size++] = $s{$i};
        else if (in_array($s{$i},array_keys($para)))
        {
            $last = $stack_size? $stack[$stack_size-1] : '';
            if ($last!=$para[$s{$i}])
                return false;
            else
                unset($stack[--$stack_size]);
        }
    }
    return count($stack)==0;
}


$s = "(t<es>t[test{test}]tes{t)";

print check($s)?"TRUE\n":"FALSE\n";

Для того, чтобы заставить этот алгоритм работать с кавычками я использовал
такой прием: на первом этапе собрал все скобки и кавычки в строку, а затем
из этой строки в цикле удалил валидные пары. Если в результате операии в
строке ничего не осталось - пары кавычек и скобок расставлены правильно.

/*///////////////////////////////////////////////////////////////

 CheckMegaPara "()", PHP, 2011-01-01
 
 Artix, master@7masterov.ru, icq:53666599, skype:artixmaster
 
 * Error in code? Nothing is perfect!
 * Free source for free Linux, use it for free!
 * Please, do not remove this comment!

///////////////////////////////////////////////////////////////*/

function check($s)
{
    $para = array(
        '(' => ')',
        '[' => ']',
        '{' => '}',
        '<' => '>',
        '/' => '/',
        '|' => '|',
        '"' => '"'
    );
    $stack='';
    for($i=0; $i<strlen($s); $i++)
    {
        $test = ( in_array(substr($s,$i,1), array_keys($para)) ) ||
                ( in_array(substr($s,$i,1), $para) );
        if ($test)
            $stack .= $s{$i};
    }
    $len = strlen($stack)/2;
    for($i=0;$i<$len;$i++)
    {
        foreach($para as $k=>$v)
            $stack = str_replace($k.$v, '', $stack);
    }
    return (strlen($stack)==0);
}


$s = '{z(/x/(ka"z"ak)x)z}';

echo $s,': ',check($s)?"TRUE":"FALSE","\n";

К сожалению этот алгоритм не очень хорош как раз потому, что в нем есть
вложенные циклы. Есть более быстрый вариант - в той точке, где встречается
кавычка, сделать рекурсивное разветвление, рассматривая в одном случае
кавычку, как закрывающий символ, а в другом - как открывающий. Такой
алгоритм будет работать быстрее, но написать его сложнее.