<?php
	// Staring straight up into the sky ... oh my my
	error_reporting(-1);
	mb_internal_encoding('utf-8');
	
	define( 'UNDEFTOK', '0' );
	define( 'NUMBERTOK', '1' );
	define( 'OPERATORTOK', '2' );
	define( 'ENDPARSE', '3' );

	$token_type = UNDEFTOK;
	$token = "";
	$exp_ptr = 0;

	function returnPpiopitet($op){
		switch ($op) {
			case "(":
				return 0;
			case ")":
				return 1;
			case "+":
			case "-":
				return 2;
			case "*":
			case "/":
				return 3;
			case "^":
				return 4;
			default:
				return -1;
		}
	}

	function isoperator($tok){
		return preg_match_all("/[\+\-\*\/\^\(\)]/u", $tok, $temp);
	}
	
	function isdigit($tok){
		return preg_match_all("/[\d\.]/u", $tok, $temp);
	}
	
	function getToken($op){
		global $exp_ptr;
		global $token, $token_type;

		$tok = "";
		$token_type = UNDEFTOK;
		$token = "";
		if($op == ""){
			$token_type = UNDEFTOK;
			$token = "";
			return;
		}
		if($exp_ptr == strlen($op)){
			$token_type = ENDPARSE;
			$token = "";
			return;
		}
		$tok = substr($op, $exp_ptr, 1);
		if(isoperator($tok)){
			$token_type = OPERATORTOK;
			$token = $tok;
			$exp_ptr++;
		}elseif(isdigit($tok)){
			$token_type = NUMBERTOK;
			while(isdigit($tok)){
				$token = $token.$tok;
				$exp_ptr++;
				$tok = substr($op, $exp_ptr, 1);
			}
		}else{
			return;
		}
	}
	
	function convertReversePolishNotation(&$stringExpression){		
		global $token, $token_type, $exp_ptr;
		
		$token_type = UNDEFTOK;
		$token = "";
		$exp_ptr = 0;
		
		$stack = new SplStack();
		$conversionResults = array();
		$stringExpression = preg_replace("/\s+/", "", $stringExpression);
		// Изъёбство для обхода унарного минуса -а заменяется на 0-а
		// -(а+в) на (0-1)*(а+в)
		$stringExpression = preg_replace("/(^-[\d\.]{1,})/u", "(0$1)", $stringExpression);
		$stringExpression = preg_replace("/([\/\*\-\+\^\(])(\-[\d\.]{1,})/u", "$1(0$2)", $stringExpression);
		$stringExpression = preg_replace("/([\/\*\-\+\^])(\-)\(/u", "$1(0\${2}1)*(", $stringExpression);
		
		if(preg_match_all("/(\()/u", $stringExpression, $match) != preg_match_all("/(\))/u", $stringExpression, $match)){
			exit("Несовпадение скобок");
		}elseif(preg_match_all("/[\+\-\*\/\^]{2,}/u", $stringExpression, $match) && !preg_match_all("/[\+\*\/\^]-/u", $stringExpression, $match)){
			exit("Несколько операторов подряд");
		}elseif(preg_match_all("/[^\+\-\*\/\^\(\)\d\.]/u", $stringExpression, $match)){
			exit("Неизвестные символы");
		}elseif(preg_match_all("/\d\(|\)\d|\)\(/u", $stringExpression, $match)){
			exit("Пропущен оператор");
		}
		
		while($token_type != ENDPARSE){
			getToken($stringExpression);
			if($token_type == OPERATORTOK && $token == ")"){
				while(($op = $stack->pop()) != "("){
					array_push($conversionResults, $op);
				}
			}
			if($token_type == NUMBERTOK) {
				array_push($conversionResults, $token);
			}
			if($token_type == OPERATORTOK && $token == "("){
				$stack->push($token);
			}
			if($token_type == OPERATORTOK && ($token == "+" || $token == "-" || $token == "*" || $token == "/" || $token == "^")){
				if($stack->count() == 0){
					$stack->push($token);
				} else {
					if(returnPpiopitet($token) > returnPpiopitet($stack->top())){
						$stack->push($token);
					} else {
						while($stack->count() !=0 && returnPpiopitet($stack->top()) >= returnPpiopitet($token)){
							array_push($conversionResults, $stack->pop());
						}
						$stack->push($token);
					}
				}
			}
			
		}
		while($stack->count() !=0){
			array_push($conversionResults, $stack->pop());
		}
		return $conversionResults;
	}
	
	function compute(&$stringExpression){
		$conversionResults = convertReversePolishNotation($stringExpression);
		$stack = new SplStack();
		
		while(count($conversionResults) !=0){
			$op = array_shift($conversionResults);
			if(returnPpiopitet($op)>=0){
				switch($op){
					case "+":
						$op1 = $stack->pop();
						$op2 = $stack->pop();
						$stack->push((double)$op2 + (double)$op1);
						break;
					case "-":
						$op1 = $stack->pop();
						$op2 = $stack->pop();
						$stack->push((double)$op2 - (double)$op1);
						break;
					case "*":
						$op1 = $stack->pop();
						$op2 = $stack->pop();
						$stack->push((double)$op2 * (double)$op1);
						break;
					case "/":
						$op1 = $stack->pop();
						$op2 = $stack->pop();
						if($op2 == 0){
							exit("Делим на ноль");
						}
						$stack->push((double)$op2 / (double)$op1);
						break;
					case "^":
						$op1 = $stack->pop();
						$op2 = $stack->pop();
						$stack->push(pow((double)$op2, (double)$op1));
						break;
				}
			}else{
				$stack->push($op);
			}
		}
		return $stack->pop();
	}

	$str = "6+5/8+15*9/8*9/8*(55+6*5)";
	$rez = compute($str);
	echo $str." = ".$rez."\n";
	
	$str = "6 + 5^2*8 + 6 / 89*8 - 9+3";
	$rez = compute($str);
	echo $str." = ".$rez."\n";
	
	$str = "6.6 + 5.2^5.5555+6/8*9.9^55.66";
	$rez = compute($str);
	echo $str." = ".$rez."\n";
	
	$str = "6.6 * (2+(6+(9/8)*6/(5+9)))";
	$rez = compute($str);
	echo $str." = ".$rez."\n";
	
	$str = "5 * (-3 + 8)";
	$rez = compute($str);
	echo $str." = ".$rez."\n";
	
	$str = "-5+-6*-(-5+-9)/-8+6*8*(-9--6)/-8^-6";
	echo $str;
	$rez = compute($str);
	echo " = ".$rez."\n";

?>