Skip to content

Commit

Permalink
Executor de formulas
Browse files Browse the repository at this point in the history
  • Loading branch information
= committed Jul 3, 2023
1 parent 9b41ad3 commit 2b1d905
Show file tree
Hide file tree
Showing 16 changed files with 694 additions and 1 deletion.
49 changes: 49 additions & 0 deletions .github/workflows/php.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
name: PHP Composer

on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]

permissions:
contents: read

jobs:
build:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3

- name: Validate composer.json and composer.lock
run: composer validate --strict

- name: Cache Composer packages
id: composer-cache
uses: actions/cache@v3
with:
path: vendor
key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }}
restore-keys: |
${{ runner.os }}-php-
- name: Install dependencies
run: |
composer install --no-interaction
composer update --${{ matrix.dependency-version }} --prefer-dist --no-interaction --no-suggest
- name: XDebug
run:
export XDEBUG_MODE=coverage
XDEBUG_MODE=coverage pest --coverage

- name: Execute tests
run: vendor/bin/pest --coverage

# Add a test script to composer.json, for instance: "test": "vendor/bin/phpunit"
# Docs: https://getcomposer.org/doc/articles/scripts.md

# - name: Run test suite
# run: composer run-script test
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
composer.phar
composer.lock
/vendor/
.idea
.phpunit.result.cache

# Commit your application's lock file https://getcomposer.org/doc/01-basic-usage.md#commit-your-composer-lock-file-to-version-control
# You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file
Expand Down
26 changes: 25 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,26 @@
# formula-executor
Formula converter in text format for mathematical operation result
Simple math expression calculator

## Install:
```
$ composer require nxp/math-executor
```

## Support:
* Multiplication
* Division
* Addition
* Subtraction
* Exponentiation
* Parentheses

## Basic usage:
```php
use Andersonrezende\FormulaExecutor\FormulaExecutor;

$formula = '(a * (b + c) / d - e)';
$values = array('a' => 5, 'b' => 3, 'c' => 2, 'd' => 4, 'e' => 6);
$formulaExecutor = new FormulaExecutor($formula, $values);
$resultFormula = $formulaExecutor->execute();
```

45 changes: 45 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{
"name": "andersonrezende/formula-executor",
"description": "Formula converter in text format for mathematical operation result",
"type": "library",
"license": "MIT",
"autoload": {
"psr-4": {
"Andersonrezende\\FormulaExecutor\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Andersonrezende\\Tests\\": "tests/"
}
},
"authors": [
{
"name": "Anderson Rezende",
"email": "[email protected]",
"role": "Developer",
"homepage": "https://github.com/AndersonRezende/"
}
],
"minimum-stability": "stable",
"keywords": [
"infix",
"postfix",
"expression",
"formula",
"converter",
"calculator",
"math"
],
"require": {
"php": ">=7.0"
},
"require-dev": {
"pestphp/pest": "^2.8"
},
"config": {
"allow-plugins": {
"pestphp/pest-plugin": true
}
}
}
18 changes: 18 additions & 0 deletions phpunit.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="./vendor/phpunit/phpunit/phpunit.xsd"
bootstrap="vendor/autoload.php"
colors="true"
>
<testsuites>
<testsuite name="Test Suite">
<directory suffix="Test.php">./tests</directory>
</testsuite>
</testsuites>
<coverage>
<include>
<directory suffix=".php">./app</directory>
<directory suffix=".php">./src</directory>
</include>
</coverage>
</phpunit>
198 changes: 198 additions & 0 deletions src/Classes/InfixToPostfix.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
<?php

namespace Andersonrezende\FormulaExecutor\Classes;

use Andersonrezende\FormulaExecutor\Exception\MalformedExpressionException;

class InfixToPostfix
{
private string $infix;
private string $postfix;
private array $tokenizedInfix = array();
private array $tokenizedPostfix = array();
private array $operators = array('+','-','*','/','^');
private array $parentheses = array('(',')');
private array $operatorsAndParentheses = array('+','-','*','/','^', '(', ')');

/**
* @throws MalformedExpressionException
*/
public function __construct($expression)
{
$this->infix = $this->removeWhitespace($expression);
$this->postfix = '';
$this->tokenize();
$this->validateTokenizedExpression();
}


/**
* @return void
*/
public function convert(): void
{
$stack = new Stack();
foreach ($this->tokenizedInfix as $token) {
switch ($token) {
case '+':
case '-':
case '*':
case '/':
case '^':
while (!$stack->isEmpty() && $this->priority($token) <= $this->priority($stack->peek())) {
$this->postfix .= $stack->peek();
$this->tokenizedPostfix[] = $stack->peek();
$stack->pop();
}
$stack->push($token);
break;
case '(':
$stack->push($token);
break;
case ')':
while ($stack->peek() != '(') {
$this->postfix .= $stack->peek();
$this->tokenizedPostfix[] = $stack->peek();
$stack->pop();
}
if ($stack->peek() == '(') {
$stack->pop();
}
break;
default:
$this->postfix .= $token;
$this->tokenizedPostfix[] = $token;
break;
}
}
while (!$stack->isEmpty()) {
if ($stack->peek() != '(') {
$this->postfix .= $stack->peek();
$this->tokenizedPostfix[] = $stack->peek();
}
$stack->pop();
}
}


/**
* @return void
*/
private function tokenize(): void
{
$token = '';
for ($char = 0; $char < strlen($this->infix); $char++) {
$token .= $this->infix[$char];
if (($char + 1) < strlen($this->infix)) {
if ($this->isOperatorOrParenthesis($this->infix[$char])
|| $this->isOperatorOrParenthesis($this->infix[$char + 1])) {
$this->tokenizedInfix[] = $token;
$token = '';
}
} else {
$this->tokenizedInfix[] = $token;
$token = '';
}
}
}

/**
* @throws MalformedExpressionException
*/
private function validateTokenizedExpression(): void
{
$tokenizedInfixSize = count($this->tokenizedInfix);
if ($tokenizedInfixSize < 3) {
throw new MalformedExpressionException(
"The expression does not contain a valid minimum number of operands and operators.");
} else {
if($this->isOperator($this->tokenizedInfix[0])
|| $this->isOperator($this->tokenizedInfix[$tokenizedInfixSize - 1])
|| $this->tokenizedInfix[0] == ')'
|| $this->tokenizedInfix[$tokenizedInfixSize - 1] == '(') {
throw new MalformedExpressionException(
"Expression does not start or end with a valid symbol.");
} else {
for ($index = 0; $index < $tokenizedInfixSize - 1; $index++) {
if (!$this->isValidOrder($this->tokenizedInfix[$index], $this->tokenizedInfix[$index + 1])) {
$value1 = $this->tokenizedInfix[$index];
$value2 = $this->tokenizedInfix[$index + 1];

throw new MalformedExpressionException(
"The following part of the given expression is incorrect: $value1$value2 .");
}
}
}
}
}

public function getStringPostfix(): string
{
return $this->postfix;
}

public function getTokenizedPostfix(): array
{
return $this->tokenizedPostfix;
}


/**
* @param $element
* @return int
*/
private function priority($element): int
{
$priority = 0;
switch ($element) {
case '+':
case '-':
$priority = 1;
break;
case '*':
case '/':
$priority = 2;
break;
case '^':
$priority = 3;
break;
}
return $priority;
}


/**
* @param $expression
* @return string
*/
private function removeWhitespace($expression): string
{
return str_replace(' ', '', $expression);
}

/**
* @param $value
* @return bool
*/
private function isOperatorOrParenthesis($value): bool
{
return in_array($value, $this->operatorsAndParentheses);
}

private function isOperator($value)
{
return in_array($value, $this->operators);
}

private function isValidOrder($value1, $value2): bool
{
if ($value1 == '(' && $this->isOperator($value2)
|| ($this->isOperator($value1) && $this->isOperator($value2))
|| ($this->isOperator($value1) && $value2 == ')')
|| (in_array($value1, $this->parentheses) && in_array($value2, $this->parentheses))) {
return false;

}
return true;
}
}
19 changes: 19 additions & 0 deletions src/Classes/Node.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

namespace Andersonrezende\FormulaExecutor\Classes;

class Node
{
public $element;
public $next;

/**
* @param $element
* @param $next
*/
public function __construct($element, $next)
{
$this->element = $element;
$this->next = $next;
}
}
Loading

0 comments on commit 2b1d905

Please sign in to comment.