Skip to content

Commit

Permalink
⬆️ Update to V0.3.16
Browse files Browse the repository at this point in the history
+Add calc command using php-math-phraser by aboyadzhiev.
+Add encrypt and decrypt command.
  • Loading branch information
LovelyA72 committed Jul 1, 2018
1 parent 180853b commit ddbe8ed
Show file tree
Hide file tree
Showing 11 changed files with 561 additions and 3 deletions.
95 changes: 95 additions & 0 deletions src/calc/Math/Lexer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
<?php

namespace Math;

/**
* Tokenize mathematical expression.
*
* @author Adrean Boyadzhiev (netforce) <[email protected]>
*/
class Lexer
{

/**
* Collection of Token instances
*
* @var array
*/
protected $tokens;

/**
* The mathematical expression that should be tokenized
*
* @var string
*/
protected $code;

/**
* Mathematical operators map
*
* @var array
*/
protected static $operatorsMap = array(
'+' => array('priority' => 0, 'associativity' => Operator::O_LEFT_ASSOCIATIVE),
'-' => array('priority' => 0, 'associativity' => Operator::O_LEFT_ASSOCIATIVE),
'*' => array('priority' => 1, 'associativity' => Operator::O_LEFT_ASSOCIATIVE),
'/' => array('priority' => 1, 'associativity' => Operator::O_LEFT_ASSOCIATIVE),
'%' => array('priority' => 1, 'associativity' => Operator::O_LEFT_ASSOCIATIVE),
);

public function __construct()
{
$this->tokens = array();
}

/**
* Tokenize matematical expression.
*
* @param type $code
* @return array Collection of Token instances
* @throws \InvalidArgumentException
*/
public function tokenize($code)
{
$code = trim((string) $code);
if (empty($code)) {
throw new \InvalidArgumentException('Cannot tokenize empty string.');
}

$this->code = $code;
$this->tokens = array();

$tokenArray = explode(' ', $this->code);

if (!is_array($tokenArray) || empty($tokenArray)) {
throw new \InvalidArgumentException(
sprintf('Cannot tokenize string: %s, please use " "(empty space for delimeter betwwen tokens)', $this->code)
);
}

foreach ($tokenArray as $t) {
if (array_key_exists($t, static::$operatorsMap)) {
$token = new Operator(
$t,
static::$operatorsMap[$t]['priority'],
static::$operatorsMap[$t]['associativity']
);
} elseif (is_numeric($t)) {
$token = new Token((float) $t, Token::T_OPERAND);
}elseif('(' === $t) {
$token = new Token($t, Token::T_LEFT_BRACKET);
}elseif(')' === $t) {
$token = new Token($t, Token::T_RIGHT_BRACKET);
}elseif('' === $t) {
continue;
}else {
throw new \InvalidArgumentException(sprintf('Syntax error: unknown token "%s"', $t));
}

$this->tokens[] = $token;
}

return $this->tokens;
}

}
73 changes: 73 additions & 0 deletions src/calc/Math/Operator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?php

namespace Math;

/**
* Value object representing one operator of mathematical expression.
*
* @author Adrean Boyadzhiev (netforce) <[email protected]>
*/
class Operator extends Token
{
const O_LEFT_ASSOCIATIVE = -1;
const O_NONE_ASSOCIATIVE = 0;
const O_RIGHT_ASSOCIATIVE = 1;

protected $priority;
protected $associativity;

/**
* Create new "Value object" which represent one mathematical operator.
*
* @param string $value string representation of this operator
* @param integer $priority priority value of this token
* @param integer $associativity one of Operator associative constants
* @throws \InvalidArgumentException
*/
public function __construct($value, $priority, $associativity)
{
if(!in_array($associativity, array(self::O_LEFT_ASSOCIATIVE, self::O_NONE_ASSOCIATIVE, self::O_RIGHT_ASSOCIATIVE))) {
throw new \InvalidArgumentException(sprintf('Invalid associativity: %s', $associativity));
}

$this->priority = (int) $priority;
$this->associativity = (int) $associativity;
parent::__construct($value, Token::T_OPERATOR);
}

/**
* Return associativity of this operator.
*
* @return integer
*/
public function getAssociativity()
{
return $this->associativity;
}

/**
* Return priority of this operator.
*
* @return integer
*/
public function getPriority()
{
return $this->priority;
}

/**
* Return true if this operator has lower priority of operator $o.
*
* @param \Math\Operator $o
* @return boolean
*/
public function hasLowerPriority(Operator $o)
{
$hasLowerPriority = ((Operator::O_LEFT_ASSOCIATIVE == $o->getAssociativity()
&& $this->getPriority() == $o->getPriority())
|| $this->getPriority() < $o->getPriority());


return $hasLowerPriority;
}
}
132 changes: 132 additions & 0 deletions src/calc/Math/Parser.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
<?php

namespace Math;

/**
* Evaluate mathematical expression.
*
* @author Adrean Boyadzhiev (netforce) <[email protected]>
*/
class Parser
{
/**
* Lexer wich should tokenize the mathematical expression.
*
* @var Lexer
*/
protected $lexer;

/**
* TranslationStrategy that should translate from infix
* mathematical expression notation to reverse-polish
* mathematical expression notation.
*
* @var TranslationStrategy\TranslationStrategyInterface
*/
protected $translationStrategy;

/**
* Array of key => value options.
*
* @var array
*/
private $options = array(
'translationStrategy' => '\Math\TranslationStrategy\ShuntingYard',
);

/**
* Create new Lexer wich can evaluate mathematical expression.
* Accept array of configuration options, currently supports only
* one option "translationStrategy" => "Fully\Qualified\Classname".
* Class represent by this options is responsible for translation
* from infix mathematical expression notation to reverse-polish
* mathematical expression notation.
*
* <code>
* $options = array(
* 'translationStrategy' => '\Math\TranslationStrategy\ShuntingYard'
* );
* </code>
*
* @param array $options
*/
public function __construct(array $options = array())
{
$this->lexer = new Lexer();
$this->options = array_merge($this->options, $options);
$this->translationStrategy = new $this->options['translationStrategy']();
}

/**
* Evaluate string representing mathematical expression.
*
* @param string $expression
* @return float
*/
public function evaluate($expression)
{
$lexer = $this->getLexer();
$tokens = $lexer->tokenize($expression);

$translationStrategy = new \Math\TranslationStrategy\ShuntingYard();

return $this->evaluateRPN($translationStrategy->translate($tokens));
}

/**
* Evaluate array sequence of tokens in Reverse Polish notation (RPN)
* representing mathematical expression.
*
* @param array $expressionTokens
* @return float
* @throws \InvalidArgumentException
*/
private function evaluateRPN(array $expressionTokens)
{
$stack = new \SplStack();

foreach ($expressionTokens as $token) {
$tokenValue = $token->getValue();
if (is_numeric($tokenValue)) {
$stack->push((float) $tokenValue);
continue;
}

switch ($tokenValue) {
case '+':
$stack->push($stack->pop() + $stack->pop());
break;
case '-':
$n = $stack->pop();
$stack->push($stack->pop() - $n);
break;
case '*':
$stack->push($stack->pop() * $stack->pop());
break;
case '/':
$n = $stack->pop();
$stack->push($stack->pop() / $n);
break;
case '%':
$n = $stack->pop();
$stack->push($stack->pop() % $n);
break;
default:
throw new \InvalidArgumentException(sprintf('Invalid operator detected: %s', $tokenValue));
break;
}
}

return $stack->top();
}

/**
* Return lexer.
*
* @return Lexer
*/
public function getLexer()
{
return $this->lexer;
}
}
85 changes: 85 additions & 0 deletions src/calc/Math/Token.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<?php

namespace Math;

/**
* Value object representing one token of mathematical expression.
*
* @author Adrean Boyadzhiev (netforce) <[email protected]>
*/
class Token
{

const T_OPERATOR = 1;
const T_OPERAND = 2;
const T_LEFT_BRACKET = 3;
const T_RIGHT_BRACKET = 4;

/**
* String representation of this token
*
* @var string
*/
protected $value;

/**
* Token type one of Token::T_* constants
*
* @var integer
*/
protected $type;

/**
* Create new "Value object" which represent one token
*
* @param integer|string $value
* @param integer $type
* @throws \InvalidArgumentException
*/
public function __construct($value, $type)
{
$tokeTypes = array(
self::T_OPERATOR,
self::T_OPERAND,
self::T_LEFT_BRACKET,
self::T_RIGHT_BRACKET
);
if (!in_array($type, $tokeTypes, true)) {
throw new \InvalidArgumentException(sprintf('Invalid token type: %s', $type));
}

$this->value = $value;
$this->type = $type;
}

/**
* Return token value
*
* @return string|integer
*/
public function getValue()
{
return $this->value;
}

/**
* Return token type
*
* return integer
*/
public function getType()
{
return $this->type;
}

/**
* Return string representation of this token.
*
* @return string
*/
public function __toString()
{
return (string) $this->getValue();
}

}
Loading

0 comments on commit ddbe8ed

Please sign in to comment.