<?php
class Parser
{
const NUM = 'd';
private $tokenizer;
private $error;
private $result;
function __construct($tokenizer)
{
$this->tokenizer = $tokenizer;
$this->process();
}
private function parseNumb()
{
return new NumNode($this->tokenizer->getToken(self::NUM));
}
private function parseBracketsOrNumber()
{
if ($this->tokenizer->isOneOf("(")) {
$this->tokenizer->getToken("(");
$result = $this->parseSum();
$this->tokenizer->getToken(")");
return $result;
} else {
return $this->parseNumb();
}
}
private function parsePow()
{
$first = $this->parseBracketsOrNumber();
$result = new PowNode($first);
while ($this->tokenizer->isOneOf("^")) {
$this->tokenizer->getToken("^");
$next = $this->parseBracketsOrNumber();
$result->append($next);
}
return $result;
}
private function parseNegative(){
return new NegativeNode($this->parsePow());
}
private function ParseMult()
{
if($this->tokenizer->isOneOf("-")){
$this->tokenizer->getToken("-");
$first = $this->parseNegative();
} else {
$first = $this->parsePow();
}
$result = new MultNode($first);
while ($this->tokenizer->isOneOf("*", "/")) {
$op = $this->tokenizer->getToken("*", "/");
$next = $this->parsePow();
$result->append($op, $next);
}
return $result;
}
private function parseSum()
{
$first = $this->parseMult();
$result = new SumNode($first);
while ($this->tokenizer->isOneOf("+", "-")) {
$op = $this->tokenizer->getToken("+", "-");
$next = $this->parseMult();
$result->append($op, $next);
}
return $result;
}
private function process()
{
try {
$this->result = $this->parseSum()->calc();
}
catch (Exception $e) {
$this->error = $e->getMessage();
}
}
function isError()
{
if (!empty($this->error)) { return true;
} else {
return false;
}
}
function getError()
{
return $this->error;
}
function getResult()
{
return $this->result;
}
}
class Tokenazer
{
private $tokens = array(); private $index = 0;
function __construct($text)
{
preg_match_all('/(\\d[.]\\d+)|\\d+|\\+|\\*|\\/|-|\\(|\\)|\\^/', $text, $matches); foreach ($matches[0] as $match) {
$this->tokens[] = $match;
}
}
function getToken()
{
$result = "";
$token = $this->getNextToken();
if($this->ifMatch($token, $arg)){
$result=$token;
}
}
throw new Exception("токен не соотв. типу");
} else {
return $result;
}
}
private function ifMatch($token, $type){
return (preg_match("/\\{$type}/", $token)) ?
(true) : (false); }
private function getNextToken()
{
$index = $this->index;
$this->index++;
if (isset($this->tokens[$index])) { return $this->tokens[$index];
}
}
function isOneOf(){
$resultBool=false;
foreach($args as $arg){
if(isset($this->tokens[$this->index])){ if($this->tokens[$this->index] == $arg){
$resultBool=true;
}
}
}
return $resultBool;
}
}
abstract class abstractNode
{
abstract function calc();
}
class NumNode extends abstractNode
{
protected $left;
function __construct($left)
{
$this->left = $left;
}
function calc()
{
return $this->left;
}
}
class NegativeNode extends NumNode
{
function calc()
{
return (0 - $this->left->calc());
}
}
class PowNode extends NumNode
{
protected $childrens = array();
function append($right)
{
$this->childrens[] = $right;
}
function calc()
{
$result = $this->left->calc();
if (!empty($this->childrens)) { while (count($this->childrens) != 1) { $degree = array_pop($this->childrens)->calc(); $this->childrens[] = new NumNode
(pow($numb, $degree)); }
$result = array_pop($this->childrens)->calc(); }
return $result;
}
}
class MultNode extends NumNode
{
protected $childrens = array();
function append($op, $right)
{
$temp['op'] = $op;
$temp['node'] = $right;
$this->childrens[] = $temp;
}
function calc()
{
$result = $this->left->calc();
if (!empty($this->left)) { foreach ($this->childrens as $child) {
if ($child['op'] == "*") {
$result *= $child['node']->calc();
} else {
$result /= $child['node']->calc();
}
}
}
return $result;
}
}
class SumNode extends MultNode
{
function calc()
{
$result = $this->left->calc();
if (!empty($this->left)) { foreach ($this->childrens as $child) {
if ($child['op'] == "+") {
$result += $child['node']->calc();
} else {
$result -= $child['node']->calc();
}
}
}
return $result;
}
}
//все как у людей блеять
class UnitTest
{
private function test($answer, $string)
{
$tokenazer = new Tokenazer($string);
$parser = new Parser($tokenazer);
if ($parser->isError()) {
$answerFromCode = $parser->getError();
} else {
$answerFromCode = $parser->getResult();
}
if ($answerFromCode == $answer) {
echo "✓ OK $string → $answerFromCode\n";
} else {
echo "✗ ошибка $string должно быть равно «{$answer}», но калькулятор вернул «{$answerFromCode}»\n";
}
}
public function run()
{
$this->test(5, "2+3");
$this->test(20, "4*5");
$this->test(6561, "3^2^3");
$this->test("токен не соотв. типу", "3^2^+3");
$this->test(22, "2 + 4*5");
$this->test(301, "(2 + 4) * 5 + 6 * 45 + 1");
$this->test(2, "-3+5");
$this->test(5, "-(-(2 + 3))");
}
}
$test = new UnitTest();
$test->run();
?>