vanchelo

calc

Jun 28th, 2014
309
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 4.44 KB | None | 0 0
  1. <?php
  2.  
  3. function calc($s)
  4. {
  5.     $s = '(' . $s . ')';
  6.     $Operands = array();
  7.     $Functions = array();
  8.     $pos = 0;
  9.     $prevToken = 'x';
  10.  
  11.     while (($token = getToken($s, $pos)) !== null) {
  12.         // разруливаем унарный + и -
  13.         if (!ctype_digit($token[0]) && !ctype_digit($prevToken[0]) &&
  14.             // если эту сточку заменить на (char)prevToken != ')' &&
  15.             // то можно будет писать (2 * -5) или даже (2 - -4)
  16.             // но нужно будет ввести ещё одну проверку так, как запись 2 + -+-+-+2 тоже будет работать :)
  17.             $prevToken[0] === '(' &&
  18.             ($token[0] === '+' || $token[0] === '-'))
  19.         {
  20.             $Operands[] = 0; // Добавляем нулевой элемент
  21.         }
  22.  
  23.  
  24.         if (is_numeric($token)) // Если операнд
  25.         {
  26.             $Operands[] = $token; // то просто кидаем в стек
  27.         }
  28.         // в данном случае у нас только числа и операции. но можно добавить функции, переменные и т.д. и т.п.
  29.         else if (!is_numeric($token[0])) // Если операция
  30.         {
  31.             if ($token[0] === ')')
  32.             {
  33.                 // Скобка - исключение из правил. выталкивает все операции до первой открывающейся
  34.                 while (count($Functions) > 0 && end($Functions) !== '(')
  35.                 {
  36.                     popFunction($Operands, $Functions);
  37.                 }
  38.                 array_pop($Functions); // Удаляем саму скобку "("
  39.             }
  40.             else
  41.             {
  42.                 while (canPop($token[0], $Functions)) // Если можно вытолкнуть
  43.                 {
  44.                     popFunction($Operands, $Functions); // то выталкиваем
  45.                 }
  46.  
  47.                 $Functions[] = $token[0]; // Кидаем новую операцию в стек
  48.             }
  49.         }
  50.  
  51.         $prevToken = $token;
  52.     }
  53.  
  54.     if (count($Operands) > 1 || count($Functions) > 0)
  55.     {
  56.         throw new Exception('Ошибка в выражении');
  57.     }
  58.  
  59.     return array_pop($Operands);
  60. }
  61.  
  62. function popFunction(& $Operands, & $Functions)
  63. {
  64.     $B = (float) array_pop($Operands);
  65.     $A = (float) array_pop($Operands);
  66.     switch (array_pop($Functions))
  67.     {
  68.         case '+': $Operands[] = $A + $B;
  69.             break;
  70.         case '-': $Operands[] = $A - $B;
  71.             break;
  72.         case '*': $Operands[] = $A * $B;
  73.             break;
  74.         case '/': $Operands[] = $A / $B;
  75.             break;
  76.     }
  77. }
  78.  
  79. function canPop($op1, & $Functions)
  80. {
  81.     if (!count($Functions))
  82.     {
  83.         return false;
  84.     }
  85.  
  86.     $p1 = (int) getPriority($op1);
  87.     $p2 = (int) getPriority(end($Functions));
  88.  
  89.     return $p1 >= 0 && $p2 >= 0 && $p1 >= $p2;
  90. }
  91.  
  92. function getPriority($op)
  93. {
  94.     switch ($op[0])
  95.     {
  96.         case '(':
  97.             return -1; // не выталкивает сам и не дает вытолкнуть себя другим
  98.         case '*':
  99.         case '/':
  100.             return 1;
  101.         case '+':
  102.         case '-':
  103.             return 2;
  104.         default:
  105.             throw new Exception("недопустимая операция");
  106.     }
  107. }
  108.  
  109. function getToken($s, & $pos)
  110. {
  111.     readWhiteSpace($s, $pos);
  112.     if ($pos == strlen($s)) // конец строки
  113.     {
  114.         return null;
  115.     }
  116.  
  117.     if (is_numeric($s[$pos]))
  118.     {
  119.         return readDouble($s, $pos);
  120.     }
  121.     else
  122.     {
  123.         return readFunction($s, $pos);
  124.     }
  125. }
  126.  
  127. function readFunction($s, & $pos)
  128. {
  129.     // в данном случае все операции состоят из одного символа
  130.     // но мы можем усложнить код добавив == && || mod div и ещё чегонить
  131.     return (string) $s[$pos++];
  132. }
  133.  
  134. function readDouble($s, & $pos)
  135. {
  136.     $res = '';
  137.     while ($pos < strlen($s) && (ctype_digit($s[$pos]) || $s[$pos] == '.'))
  138.     {
  139.         $res .= $s[$pos++];
  140.     }
  141.  
  142.     return $res;
  143. }
  144.  
  145. // Считывает все проблемы и прочие левые символы.
  146. function readWhiteSpace($s, & $pos)
  147. {
  148.     while ($pos < strlen($s) && ctype_space($s[$pos]))
  149.     {
  150.         $pos++;
  151.     }
  152. }
Advertisement
Add Comment
Please, Sign In to add comment