Skip to content

Commit

Permalink
code quality
Browse files Browse the repository at this point in the history
  • Loading branch information
felixdorn committed Nov 5, 2021
1 parent 3e8a50a commit 98994d6
Show file tree
Hide file tree
Showing 10 changed files with 74 additions and 42 deletions.
21 changes: 12 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,22 @@ composer require felixdorn/sey

## Usage

### Floating Point Precision
```php
Sey::parse('(0.5 + 0.5) / 3)') // 0.3333333333333333
// or
sey('a / b', ['a' => 1, 'b' => 2]) // 0.5
```

By default, the precision is 16 which is roughly equal to the PHP default (even though it is platform-dependent, it's
very common).
### Precision

By default, the maximum floating precision is 16.

You may change it:

```php
\Felix\Sey\Sey:precision(32)
```

Under the hood, it just calls `bcscale` for the calculation and then rollbacks to the previous value as to not create
side effects.

## Syntax

It's just math.
Expand All @@ -61,7 +63,7 @@ pi()
You can not define variables in your code but you can pass them at compile-time.

```php
sey('2 * r * pi', [
Sey::parse('2 * r * pi', [
'r' => 10,
'pi' => 3.1415
])
Expand All @@ -81,19 +83,20 @@ sey('2 * r * pi', [

#### Custom functions

They can override built-ins so you can redefine `!` to use a lookup table for example.
> You can override built-ins functions.
```php
Sey::define('!', function (int $n, /* as many arguments as you want */) {
return $factorials[$n] ?? bcfact($n);
});
```

The function name must match the following regex `[a-z_A-Z!]+[a-z_A-Z0-9]*`.

So, first character must be a letter or ! followed by any number of letters or numbers.


### Tests

```bash
composer test
```
Expand Down
5 changes: 0 additions & 5 deletions src/Contracts/Token.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,5 @@ public function __construct(public string $value)
{
}

public static function empty(): NullToken
{
return new NullToken();
}

abstract public function consume(Runtime $runtime): void;
}
7 changes: 5 additions & 2 deletions src/Scanner.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Felix\Sey;

use Felix\Sey\Tokens\CloseParenthesis;
use Felix\Sey\Tokens\Comma;
use Felix\Sey\Tokens\Func;
use Felix\Sey\Tokens\Identifier;
use Felix\Sey\Tokens\Number;
Expand Down Expand Up @@ -37,15 +38,17 @@ public static function scan(string $code): Stack
continue;
}

if ($value === ')') {
if ($value === ',') {
$tokens->push(new Comma());
} elseif ($value === ')') {
$tokens->push(new CloseParenthesis());
} elseif ($value === '(') {
if ($behind === ')' || is_numeric($behind)) {
$tokens->push(new Operator('*'));
}

$tokens->push(new OpenParenthesis());
} elseif (Operator::valid($value)) {
} elseif (Operator::isValid($value)) {
$tokens->push(new Operator($value));
} elseif ($ahead === '(') {
$tokens->push(new Func($value));
Expand Down
4 changes: 2 additions & 2 deletions src/Sey.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public static function precision(?int $precision = null): int
return $precision;
}

public static function parse(string $expression, array $variables = [], array $functions = []): string
public static function parse(string $expression, array $variables = [], array $functions = [], ?int $precision = null): string
{
$expression = trim($expression);

Expand All @@ -41,7 +41,7 @@ public static function parse(string $expression, array $variables = [], array $f
$expression,
$variables,
array_merge(static::$functions, $functions),
static::$precision
$precision ?? static::$precision
);

return $runtime->run();
Expand Down
20 changes: 0 additions & 20 deletions src/Tokens/NullToken.php

This file was deleted.

2 changes: 1 addition & 1 deletion src/Tokens/Operator.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

class Operator extends Token
{
public static function valid(string $value): bool
public static function isValid(string $value): bool
{
return match ($value) {
'+', '-', '*', '/', '%', '^' => true,
Expand Down
4 changes: 2 additions & 2 deletions src/helpers.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
use Felix\Sey\Sey;

if (!function_exists('sey')) {
function sey(string $code, array $variables = []): string
function sey(string $code, array $variables = [], array $functions = [], ?int $precision = null): string
{
return Sey::parse($code, $variables);
return Sey::parse($code, $variables, $functions, $precision);
}
}

Expand Down
7 changes: 7 additions & 0 deletions tests/HelpersTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

use Felix\Sey\Sey;

it('can parse using the sey function', function () {
expect(sey('1 / 3'))->toBe(Sey::parse('1 / 3'));
});
35 changes: 35 additions & 0 deletions tests/OperatorTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

use Felix\Sey\Sey;
use Felix\Sey\Tokens\Number;
use Felix\Sey\Tokens\Operator;

it('can evaluate correctly an expression', function (string $bcFunction, string $operator, string $left, string $right, string $result) {
$op = new Operator($operator);

expect($op->evaluate(
new Number($left),
new Number($right),
Sey::precision()
))->toBe($bcFunction($left, $right, Sey::precision()));
})->with([
['bcadd', '+', '1', '2', '3'],
['bcsub', '-', '1', '2', '-1'],
['bcmul', '*', '1', '2', '2'],
['bcdiv', '/', '1', '2', '0.5'],
['bcmod', '%', '10', '2', '0'],
['bcpow', '^', '2', '4', '32'],
]);

it('can correctly evaluate an operator', function (string $op, bool $isValid) {
expect(Operator::isValid($op))->toBe($isValid);
})->with([
['+', true],
['-', true],
['*', true],
['/', true],
['%', true],
['^', true],
['@', false],
['4', false],
]);
11 changes: 10 additions & 1 deletion tests/ParserTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
expect($output)->toBe('70.0000000000000000');
});

it('can define and call functions', function () {
it('can call non-std functions', function () {
Sey::define('seven', function ($delta) {
return 7 + $delta;
});
Expand All @@ -39,6 +39,15 @@
expect($output)->toBe('9.0000000000000000');
});

it('can call functions with x arguments', function () {
Sey::define('called_with', function () {
return func_num_args();
});

$output = Sey::parse('called_with(1,1,1,1,1)');
expect($output)->toBe('5');
});

it('can define an arbitrary precision', function () {
$output = Sey::parse('1 / 3');
expect($output)->toBe('0.' . str_repeat('3', Sey::precision()));
Expand Down

0 comments on commit 98994d6

Please sign in to comment.