Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions phpunit.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@
<testsuite name="AllTests">
<directory>tests</directory>
</testsuite>

<testsuite name="BooleanOperators">
<file>tests/MathParser/Interpreting/EvaluatorBooleanOperatorTest.php</file>

</testsuite>
</testsuites>

<filter>
Expand Down
40 changes: 32 additions & 8 deletions src/MathParser/Interpreting/Evaluator.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use MathParser\Interpreting\Visitors\Visitor;
use MathParser\Parsing\Nodes\Node;
use MathParser\Parsing\Nodes\ExpressionNode;
use MathParser\Parsing\Nodes\NotBooleanNode;
use MathParser\Parsing\Nodes\NumberNode;
use MathParser\Parsing\Nodes\VariableNode;
use MathParser\Parsing\Nodes\FunctionNode;
Expand Down Expand Up @@ -104,16 +105,32 @@ public function visitExpressionNode(ExpressionNode $node)
// Perform the right operation based on the operator
switch ($operator) {
case '+':
return $leftValue + $rightValue;
return $leftValue + $rightValue;
case '-':
return $rightValue === null ? -$leftValue : $leftValue - $rightValue;
return $rightValue === null ? -$leftValue : $leftValue - $rightValue;
case '*':
return $rightValue * $leftValue;
return $rightValue * $leftValue;
case '/':
if ($rightValue == 0) throw new DivisionByZeroException();
return $leftValue/$rightValue;
if ($rightValue == 0) throw new DivisionByZeroException();
return $leftValue/$rightValue;
case '^':
return pow($leftValue, $rightValue);
return pow($leftValue, $rightValue);
case '=':
return intval(ceil($leftValue)) === intval(ceil($rightValue));
case '&&':
return intval(ceil($leftValue)) && intval(ceil($rightValue));
case '||':
return intval(ceil($leftValue)) || intval(ceil($rightValue));

case '>':
return ($leftValue > $rightValue);
case '>=':
return $leftValue >= $rightValue;
case '<':
return $leftValue < $rightValue;
case '<=':
return $leftValue <= $rightValue;


default:
throw new UnknownOperatorException($operator);
Expand Down Expand Up @@ -274,8 +291,15 @@ public function visitFunctionNode(FunctionNode $node)
return $inner >= 0 ? 1 : -1;

case '!':
$logGamma = Math::logGamma(1+$inner);
return exp($logGamma);
if($node instanceof NotBooleanNode) {
$intValue = intval(ceil($inner));
$result = !$intValue;
}
else {
$logGamma = Math::logGamma(1+$inner);
$result = exp($logGamma);
}
return $result;

case '!!':
if (round($inner) != $inner)throw new \UnexpectedValueException("Expecting positive integer (semifactorial)");
Expand Down
10 changes: 10 additions & 0 deletions src/MathParser/Lexing/ComplexLexer.php
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,16 @@ public function __construct()
$this->add(new TokenDefinition('/\//', TokenType::DivisionOperator));
$this->add(new TokenDefinition('/\^/', TokenType::ExponentiationOperator));

$this->add(new TokenDefinition('/\=/', TokenType::EqualCompareOperator));
$this->add(new TokenDefinition('/\>=/', TokenType::GreaterOrEqualOperator));
$this->add(new TokenDefinition('/\>/', TokenType::GreaterOperator));
$this->add(new TokenDefinition('/\<=/', TokenType::SmallerOrEqualOperator));
$this->add(new TokenDefinition('/\</', TokenType::SmallerOperator));


$this->add(new TokenDefinition('/\&&/', TokenType::BooleanAndOperator));
$this->add(new TokenDefinition('/\|\|/', TokenType::BooleanOrOperator));

$this->add(new TokenDefinition('/pi/', TokenType::Constant));
$this->add(new TokenDefinition('/e/', TokenType::Constant));
$this->add(new TokenDefinition('/i/', TokenType::Constant));
Expand Down
4 changes: 3 additions & 1 deletion src/MathParser/Lexing/Lexer.php
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,10 @@ public function tokenize($input)

// If no tokens were matched, it means that the string has invalid tokens
// for which we did not define a token definition
if (!$token)
if (!$token) {
throw new UnknownTokenException(0,$currentIndex);
}


// Add the matched token to our list of token
$tokens[] = $token;
Expand Down
10 changes: 10 additions & 0 deletions src/MathParser/Lexing/StdMathLexer.php
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,16 @@ public function __construct()
$this->add(new TokenDefinition('/\//', TokenType::DivisionOperator));
$this->add(new TokenDefinition('/\^/', TokenType::ExponentiationOperator));


$this->add(new TokenDefinition('/\=/', TokenType::EqualCompareOperator));
$this->add(new TokenDefinition('/\>=/', TokenType::GreaterOrEqualOperator));
$this->add(new TokenDefinition('/\>/', TokenType::GreaterOperator));
$this->add(new TokenDefinition('/\<=/', TokenType::SmallerOrEqualOperator));
$this->add(new TokenDefinition('/\</', TokenType::SmallerOperator));

$this->add(new TokenDefinition('/\&&/', TokenType::BooleanAndOperator));
$this->add(new TokenDefinition('/\|\|/', TokenType::BooleanOrOperator));

// Postfix operators
$this->add(new TokenDefinition('/\!\!/', TokenType::SemiFactorialOperator));
$this->add(new TokenDefinition('/\!/', TokenType::FactorialOperator));
Expand Down
8 changes: 7 additions & 1 deletion src/MathParser/Lexing/Token.php
Original file line number Diff line number Diff line change
Expand Up @@ -146,8 +146,14 @@ public static function canFactorsInImplicitMultiplication($token1, $token2)

if (!$check2) return false;

if ($token1->type == TokenType::FunctionName && $token2->type == TokenType::OpenParenthesis)
if ($token1->type == TokenType::FunctionName && $token2->type == TokenType::OpenParenthesis) {
return false;
}

if($token1->type == TokenType::FactorialOperator && $check2) {
return false;
}


return true;
}
Expand Down
10 changes: 10 additions & 0 deletions src/MathParser/Lexing/TokenType.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,16 @@ final class TokenType
/** Token representing postfix subfactorial operator '!!' */
const SemiFactorialOperator = 105;

/** Token representing equal operator */
const EqualCompareOperator = 106;
const BooleanAndOperator = 107;
const BooleanOrOperator = 108;
const GreaterOperator = 109;
const GreaterOrEqualOperator = 110;
const SmallerOperator = 111;
const SmallerOrEqualOperator = 112;


/** Token represented a function name, e.g. 'sin' */
const FunctionName = 200;

Expand Down
35 changes: 35 additions & 0 deletions src/MathParser/Parsing/Nodes/ExpressionNode.php
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,41 @@ function __construct($left, $operator = null, $right = null)
$this->associativity = self::RIGHT_ASSOC;
break;

case '=':
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO, && should have higher precedence than || and || higher than =. Then =, >, < and other have same precedence.
So, A = B && C means A = (B && C)

$this->precedence = 5;
$this->associativity = self::LEFT_ASSOC;
break;

case '&&':
$this->precedence = 5;
$this->associativity = self::LEFT_ASSOC;
break;

case '||':
$this->precedence = 5;
$this->associativity = self::LEFT_ASSOC;
break;

case '>':
$this->precedence = 5;
$this->associativity = self::LEFT_ASSOC;
break;

case '>=':
$this->precedence = 5;
$this->associativity = self::LEFT_ASSOC;
break;

case '<':
$this->precedence = 5;
$this->associativity = self::LEFT_ASSOC;
break;

case '<=':
$this->precedence = 5;
$this->associativity = self::LEFT_ASSOC;
break;

default:
throw new UnknownOperatorException($operator);
}
Expand Down
73 changes: 73 additions & 0 deletions src/MathParser/Parsing/Nodes/Factories/BoolAndNodeFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?php
/*
* @package Parsing
* @author Frank Wikström <frank@mossadal.se>
* @copyright 2015 Frank Wikström
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
*
*/

namespace MathParser\Parsing\Nodes\Factories;

use MathParser\Parsing\Nodes\Interfaces\ExpressionNodeFactory;

use MathParser\Parsing\Nodes\Node;
use MathParser\Parsing\Nodes\NumberNode;
use MathParser\Parsing\Nodes\IntegerNode;
use MathParser\Parsing\Nodes\RationalNode;

use MathParser\Parsing\Nodes\ExpressionNode;
use MathParser\Parsing\Nodes\Traits\Sanitize;
use MathParser\Parsing\Nodes\Traits\Numeric;

/**
* Factory for creating an ExpressionNode representing '&&'.
*
* Some basic simplification is applied to the resulting Node.
*
*/
class BoolAndNodeFactory implements ExpressionNodeFactory
{
use Sanitize;
use Numeric;

public function makeNode($leftOperand, $rightOperand)
{
$leftOperand = $this->sanitize($leftOperand);
$rightOperand = $this->sanitize($rightOperand);

$node = $this->numericTerms($leftOperand, $rightOperand);
if ($node) return $node;

return new ExpressionNode($leftOperand, '&&', $rightOperand);
}

protected function numericTerms($leftOperand, $rightOperand)
{

if (!$this->isNumeric($leftOperand) || !$this->isNumeric($rightOperand)) {
return null;
}
$type = $this->resultingType($leftOperand, $rightOperand);
switch($type) {
case Node::NumericFloat:
$result = ( intval(ceil($leftOperand->getValue())) && intval(ceil($rightOperand->getValue())));
return new NumberNode($result);

case Node::NumericRational:
$leftValue = ($leftOperand->getNumerator() / $leftOperand->getDenominator());
$leftValue = intval(ceil($leftValue));
$rightValue = ($rightOperand->getDenominator() / $rightOperand->getNumerator());
$rightValue = intval(ceil($rightValue));
$result = ($leftValue && $rightValue);
return new IntegerNode($result);

case Node::NumericInteger:
$result = ($leftOperand->getValue() && $rightOperand->getValue());
return new IntegerNode($result);
}


return null;
}
}
71 changes: 71 additions & 0 deletions src/MathParser/Parsing/Nodes/Factories/BoolEqualNodeFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<?php
/*
* @package Parsing
* @author Frank Wikström <frank@mossadal.se>
* @copyright 2015 Frank Wikström
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
*
*/

namespace MathParser\Parsing\Nodes\Factories;

use MathParser\Parsing\Nodes\Interfaces\ExpressionNodeFactory;

use MathParser\Parsing\Nodes\Node;
use MathParser\Parsing\Nodes\NumberNode;
use MathParser\Parsing\Nodes\IntegerNode;
use MathParser\Parsing\Nodes\RationalNode;

use MathParser\Parsing\Nodes\ExpressionNode;
use MathParser\Parsing\Nodes\Traits\Sanitize;
use MathParser\Parsing\Nodes\Traits\Numeric;

/**
* Factory for creating an ExpressionNode representing '='.
*
* Some basic simplification is applied to the resulting Node.
*
*/
class BoolEqualNodeFactory implements ExpressionNodeFactory
{
use Sanitize;
use Numeric;

public function makeNode($leftOperand, $rightOperand)
{
$leftOperand = $this->sanitize($leftOperand);
$rightOperand = $this->sanitize($rightOperand);

$node = $this->numericTerms($leftOperand, $rightOperand);
if ($node) return $node;

return new ExpressionNode($leftOperand, '=', $rightOperand);
}

protected function numericTerms($leftOperand, $rightOperand)
{
if (!$this->isNumeric($leftOperand) || !$this->isNumeric($rightOperand)) {
return null;
}
$type = $this->resultingType($leftOperand, $rightOperand);

switch($type) {
case Node::NumericFloat:
$result = ($leftOperand->getValue() === $rightOperand->getValue());
return new NumberNode($result);

case Node::NumericRational:
$leftValue = ($leftOperand->getNumerator() / $leftOperand->getDenominator());
$rightValue = ($rightOperand->getDenominator() / $rightOperand->getNumerator());
$result = ($leftValue === $rightValue);
return new IntegerNode($result);

case Node::NumericInteger:
$result = ($leftOperand->getValue() === $rightOperand->getValue());
return new IntegerNode($result);
}


return null;
}
}
Loading