<?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' );
	
	class Calculator{
		private $token_type = 0;
		private $token = "";
		private $exp_ptr = 0;
		
		public $parse_string = "";
		
		function __construct($str){
			$this->parse_string = preg_replace("/\s+/", "", $str);
			$this->exp_ptr = 0;
			
			$this->tok_type = ENDPARSE;
			$this->token = "";
		}
		
		private function isoperator($tok){
			return preg_match_all("/[\+\-\*\/\^\=\(\)%]/u", $tok, $temp);
		}
		
		private function isdigit($tok){
			return preg_match_all("/[\d\.]/u", $tok, $temp);
		}
		
		public function get_token(){
			$tok = "";
			$this->token_type = UNDEFTOK;
			$this->token = "";
			if($this->parse_string == ""){
				$this->token_type = UNDEFTOK;
				$this->token = "";
				return;
			}
			if($this->exp_ptr == strlen($this->parse_string)){
				$this->token_type = ENDPARSE;
				$this->token = "";
				return;
			}
			$tok = substr($this->parse_string, $this->exp_ptr, 1);
			if($this->isoperator($tok)){
				$this->token_type = OPERATORTOK;
				$this->token = $tok;
				$this->exp_ptr++;
			}elseif($this->isdigit($tok)){
				$this->token_type = NUMBERTOK;
				while($this->isdigit($tok)){
					$this->token = $this->token.$tok;
					$this->exp_ptr++;
					$tok = substr($this->parse_string, $this->exp_ptr, 1);
				}
			}else{
				return;
			}
		}
		
		public function compute(){
			$result = 0.0;
			$this->get_token();
			if($this->token == ""){
				echo "Выражение отсутствует";
				return 0.0;
			}
			$this->addOrSubtractMembers($result);
			return $result;
		}
		
		// Сложить или вычесть два члена
		public function addOrSubtractMembers(&$result){
			$op = "";
			$temp = 0.0;
			
			$this->multiplyOrDivide($result);
			$op = $this->token;
			
			While ($op == "+" || $op == "-") {
				$this->get_token();
				$this->multiplyOrDivide($temp);
				switch ($op) {
					case "-":
						$result = $result - $temp;
						break;
					case "+":
						$result = $result + $temp;
						break;
				}
				$op = $this->token;
			}
		}
		
		// Перемножить или поделить
		public function multiplyOrDivide(&$result){
			$op = "";
			$temp = 0.0;
			
			$this->processingDegree($result);
			$op = $this->token;
			While ($op == "*" || $op == "/" || $op == "%") {
				$this->get_token();
				$this->processingDegree($temp);
				switch ($op) {
					case "*":
						$result = $result * $temp;
						break;
					case "/":
						if($temp == 0){
							echo "Делим на ноль";
							exit(-1);
						}
						$result = $result / $temp;
						break;
					case "%":
						$result = $result % $temp;
						break;
				}
				$op = $this->token;
			}
			return;
		}
		
		// обработка степени
		public function processingDegree(&$result){
			$op = "";
			$temp = 0.0;
			$ex = 0.0;
			
			$this->unaryOperation($result);
			if($this->token == "^"){
				$this->get_token();
				$this->processingDegree($temp);
				$ex = $result;
				if($temp == 0){
					$result = 0.0;
					return;
				}
				$result = pow($result, $temp);
			}
			return;
		}
		
		// Унарный + или -
		public function unaryOperation(&$result){
			$op = "";
			if($this->token_type == OPERATORTOK && $this->token == "+" || $this->token == "-" ){
				$op = $this->token;
				$this->get_token();
			}
			$this->processingBrackets($result);
			if($op == "-"){
				$result = -1 * $result;
			}
			return;
		}
		
		// Выражения в скобках
		public function processingBrackets(&$result){
			if($this->token == "("){
				$this->get_token();
				$this->addOrSubtractMembers($result);
				if($this->token != ")"){
					exit("Не хватает скобок");
				}
				$this->get_token();
			} else {
				$this->receiveValueNumber($result);
			}
		}
		
		// Получить значение числа
		public function receiveValueNumber(&$result){
			switch ($this->token_type) {
				case NUMBERTOK:
					$result = (double)$this->token;
					$this->get_token();
					return;
				default:
					exit("Что-то пошло не так receiveValueNumber()\t\$this->token = {$this->token}\t\$this->token_type = {$this->token_type}");
			}
		}
	}
	
	function test(&$tst){
		$tst++;
	}
	
	$test = new Calculator("6+5/8+15*9/8*9/8*(55+6*5)");
	echo "{$test->parse_string} = ".$test->compute()."\n";
	
	$test2 = new Calculator("6 + 5^2*8 + 6 / 89*8 - 9+3");
	echo "{$test2->parse_string} = ".$test2->compute()."\n";
	
	$test3 = new Calculator("6.6 + 5.2^5.5555+6/8*9.9^55.66");
	echo "{$test3->parse_string} = ".$test3->compute()."\n";
	
	$test4 = new Calculator("6.6 * (2+(6+(9/8)*6/(5+9)))");
	echo "{$test4->parse_string} = ".$test4->compute()."\n";
	
?>