Skip to content

Instantly share code, notes, and snippets.

@fimak
Last active August 29, 2015 14:06
Show Gist options
  • Save fimak/1192c03ae0b06cd1568d to your computer and use it in GitHub Desktop.
Save fimak/1192c03ae0b06cd1568d to your computer and use it in GitHub Desktop.
<?php
class CalcParser
{
public function parse($str)
{
$result = $this->PlusMinus($str);
if (!empty($result->rest)) {
echo "Error: can't parse all.<br />";
echo "rest: " . $result->rest;
}
return $result->acc;
}
private function plusMinus($str)
{
$current = $this->mulDiv($str);
$acc = $current->acc;
while (strlen($current->rest) > 0) {
if (!($current->rest[0] == '+' || $current->rest[0] == '-')) {
break;
}
$sign = $current->rest[0];
$next = substr($current->rest, 1);
$current = $this->mulDiv($next);
if ($sign == '+') {
$acc += $current->acc;
} else {
$acc -= $current->acc;
}
$current->acc = $acc;
}
return new Result($current->acc, $current->rest);
}
private function mulDiv($str)
{
$current = $this->pow($str);
$acc = $current->acc;
while (true) {
if (strlen($current->rest) == 0) {
return $current;
} else {
$sign = $current->rest[0];
$next = substr($current->rest, 1);
$right = $this->pow($next);
if ($sign == '*') {
$acc *= $right->acc;
} elseif ($sign == '/') {
$acc /= $right->acc;
} else {
return $current;
}
$current = new Result($acc, $right->rest);
}
}
}
private function pow($str)
{
$current = $this->bracket($str);
$acc = $current->acc;
while (true) {
if (strlen($current->rest) == 0) {
return $current;
} else {
$sign = $current->rest[0];
$next = substr($current->rest, 1);
$right = $this->bracket($next);
if ($sign == '^') {
$acc = pow($acc, $right->acc);
} else {
return $current;
}
$current = new Result($acc, $right->rest);
}
}
}
private function bracket($str)
{
if ($str[0] == '(') {
$result = $this->plusMinus(substr($str, 1));
if (!empty($result->rest) && $result->rest[0] == ')') {
$result->rest = substr($result->rest, 1);
} else {
echo "Error: bracket is not closed";
}
return $result;
}
return $this->extraFunctions($str);
}
private function extraFunctions($str)
{
$funct = '';
$i = 0;
while ($i < strlen($str) && ctype_alpha($str[$i])) {
$funct .= $str[$i];
$i++;
}
if (!empty($funct)) {
if (strlen($str) > $i && $str[$i] == "(") {
$result = $this->bracket(substr($str, strlen($funct)));
return $this->processFunction($funct, $result);
} else {
echo "Error: undefined function";
}
}
return $this->num($str);
}
private function processFunction($funct, $result)
{
switch ($funct) {
case "sin" :
return new Result(sin(deg2rad($result->acc)), $result->rest);
break;
case "cos" :
return new Result(cos(deg2rad($result->acc)), $result->rest);
break;
case "tan" :
return new Result(tan(deg2rad($result->acc)), $result->rest);
break;
case "sqrt" :
return new Result(sqrt($result->acc), $result->rest);
break;
default:
echo "Error: undefined function";
}
return $result;
}
private function num($str)
{
$i = 0;
$dot_cnt = 0;
$negative = false;
if ($str[0] == '-') {
$negative = true;
$str = substr($str, 1);
}
while ($i < strlen($str) && (is_numeric($str[$i]) || $str[$i] == '.')) {
if ($str[$i] == '.' && ++$dot_cnt > 1) {
throw new Exception("Number has two dots in a row '" . substr($str, 0, $i + 1) + "'");
}
$i++;
}
$part = substr($str, 0, $i);
if ($negative) {
$part *= -1;
}
$restPart = substr($str, $i);
return new Result($part, $restPart);
}
}
<?php
include "CalcParser.php";
include "Result.php";
$calc = new CalcParser();
$str = "2+2*2-cos(15+45)+2^(3+2)";
try {
echo "Result: {$calc->parse($str)}";
} catch(Exception $e) {
echo "Error while parsing '{$str}' with message: " . $e->getMessage();
}
<?php
class Result
{
public $acc;
public $rest;
public function result($part, $str)
{
$this->acc = $part;
$this->rest = $str;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment