diff --git a/Arithmetic.pp b/Arithmetic.pp index fb2ace4..d02ef89 100644 --- a/Arithmetic.pp +++ b/Arithmetic.pp @@ -40,17 +40,20 @@ // -%skip space [\x20\x09]+ -%token bracket_ \( -%token _bracket \) -%token comma , -%token number (0|[1-9]\d*)(\.\d+)?([eE][\+\-]?\d+)? -%token plus \+ -%token minus \-|− -%token times \*|× -%token div /|÷ -%token constant [A-Z_]+[A-Z0-9_]+ -%token id \w+ +%skip space [\x20\x09]+ +%token bracket_ \( +%token _bracket \) +%token comma , +%token hexadecimal 0[xX][0-9a-fA-F]{1,16} +%token octal 0[0-7]{1,21} +%token binary 0[bB][01]{1,64} +%token number (0|[1-9]\d*)(\.\d+)?([eE][\+\-]?\d+)? +%token plus \+ +%token minus \-|− +%token times \*|× +%token div /|÷ +%token constant [A-Z_]+[A-Z0-9_]+ +%token id \w+ expression: primary() ( ::plus:: #addition expression() )? @@ -73,7 +76,10 @@ | function() number: - + + | + | + | constant: diff --git a/Exception/OutOfBounds.php b/Exception/OutOfBounds.php new file mode 100644 index 0000000..262fbfa --- /dev/null +++ b/Exception/OutOfBounds.php @@ -0,0 +1,49 @@ + + | + | + | diff --git a/Test/Unit/Visitor/Arithmetic.php b/Test/Unit/Visitor/Arithmetic.php index 470bd9c..a5c3076 100644 --- a/Test/Unit/Visitor/Arithmetic.php +++ b/Test/Unit/Visitor/Arithmetic.php @@ -65,7 +65,7 @@ public function case_visitor_exhaustively() new Regex\Visitor\Isotropic( new LUT\Sampler\Random() ), - 9 + 7 ), $compiler = Compiler\Llk\Llk::load( new File\Read('hoa://Library/Math/Arithmetic.pp') @@ -75,7 +75,7 @@ public function case_visitor_exhaustively() ->executeOnFailure(function () use (&$expression) { echo 'Failed expression: ', $expression, '.', "\n"; }) - ->when(function () use (&$sampler, &$compiler, &$visitor) { + ->when(function () use (&$sampler, &$compiler, &$visitor, &$expression) { foreach ($sampler as $i=> $expression) { try { $x = (float) $visitor->visit( @@ -95,7 +95,7 @@ public function case_visitor_exhaustively() $this ->float($x) - ->isNearlyEqualTo($y); + ->isNearlyEqualTo($y, 1.0e-10); } }); } @@ -223,4 +223,66 @@ public function case_change_default_context() ->call('getVariable')->withArguments($variableName)->once ->call('getConstant')->withArguments('PI')->once; } + + public function case_visitor_hexadecimal_number() + { + $this + ->given( + $compiler = Compiler\Llk\Llk::load(new File\Read('hoa://Library/Math/Arithmetic.pp')), + $visitor = new CUT(), + $hexadecimalNumber = '0x2a' + ) + ->then + ->object($compiler->parse($hexadecimalNumber)) + ->isInstanceOf('Hoa\Compiler\Llk\TreeNode') + ->float($visitor->visit($compiler->parse($hexadecimalNumber))) + ->isEqualTo(hexdec('2a')); + } + + public function case_visitor_out_of_bounds_hexadecimal_number() + { + $this + ->given( + $compiler = Compiler\Llk\Llk::load(new File\Read('hoa://Library/Math/Arithmetic.pp')), + $visitor = new CUT(), + $hexadecimalNumber = '0x9000000000000000' + ) + ->then + ->object($compiler->parse($hexadecimalNumber)) + ->isInstanceOf('Hoa\Compiler\Llk\TreeNode') + ->exception(function () use ($hexadecimalNumber, $compiler, $visitor) { + $visitor->visit($compiler->parse($hexadecimalNumber)); + }) + ->isInstanceOf('Hoa\Math\Exception\OutOfBounds'); + } + + public function case_visitor_octal_number() + { + $this + ->given( + $compiler = Compiler\Llk\Llk::load(new File\Read('hoa://Library/Math/Arithmetic.pp')), + $visitor = new CUT(), + $octalNumber = '052' + ) + ->then + ->object($compiler->parse($octalNumber)) + ->isInstanceOf('Hoa\Compiler\Llk\TreeNode') + ->float($visitor->visit($compiler->parse($octalNumber))) + ->isEqualTo(octdec('52')); + } + + public function case_visitor_binary_number() + { + $this + ->given( + $compiler = Compiler\Llk\Llk::load(new File\Read('hoa://Library/Math/Arithmetic.pp')), + $visitor = new CUT(), + $binary = '0b101010' + ) + ->then + ->object($compiler->parse($binary)) + ->isInstanceOf('Hoa\Compiler\Llk\TreeNode') + ->float($visitor->visit($compiler->parse($binary))) + ->isEqualTo(bindec('101010')); + } } diff --git a/Visitor/Arithmetic.php b/Visitor/Arithmetic.php index a7da754..67bc25b 100644 --- a/Visitor/Arithmetic.php +++ b/Visitor/Arithmetic.php @@ -37,6 +37,7 @@ namespace Hoa\Math\Visitor; use Hoa\Math; +use Hoa\Math\Exception\OutOfBounds; use Hoa\Visitor; /** @@ -245,18 +246,39 @@ public function visit( case 'token': $value = $element->getValueValue(); + $token = $element->getValueToken(); $out = null; - if ('constant' === $element->getValueToken()) { + if ('constant' === $token) { if (defined($value)) { $out = constant($value); } else { $out = $this->getConstant($value); } - } elseif ('id' === $element->getValueToken()) { + } elseif ('id' === $token) { return $value; } else { - $out = (float) $value; + $number = preg_replace('/^0[xXbB]/', '', $value); + + switch ($token) { + case 'binary': + $number = bindec($number); + break; + + case 'hexadecimal': + $number = hexdec($number); + break; + + case 'octal': + $number = octdec($number); + break; + } + + if ('hexadecimal' === $token && $number > PHP_INT_MAX) { + throw new OutOfBounds('Number ' . $value . ' is out of bound for the ' . php_uname('m') . ' platform.'); + } + + $out = (float) $number; } $acc = function () use ($out, $acc) {