Installation:
composer require ardiakov/string-stack-calc
Service Registration:
The package provides a StringStackCalculator service. Register it in your Laravel config/app.php under bindings or use dependency injection directly:
$calculator = app('StringStackCalculator');
First Use Case: Parse and evaluate a simple arithmetic expression passed as a string:
$result = $calculator->calculate('2 + 3 * (4 - 1)');
// Returns: 11 (2 + (3 * 3))
Key Classes:
StringStackCalculator: Main service for parsing/evaluating expressions.Token: Represents tokens (numbers, operators, parentheses) in the parser.Parser: Converts infix notation (e.g., 2+3) to postfix (Reverse Polish Notation).Input Handling:
"5 * (10 - 2) / 3".Parsing Pipeline:
$tokens = $calculator->tokenize('3 + 4 * 2');
// Returns: [Token('3'), Token('+'), Token('4'), Token('*'), Token('2')]
$postfix = $calculator->parseToPostfix($tokens);
// Converts to: [Token('3'), Token('4'), Token('2'), Token('*'), Token('+')]
Evaluation:
$result = $calculator->evaluatePostfix($postfix);
// Returns: 11 (3 + (4 * 2))
Integration with Laravel:
AppServiceProvider:
public function register() {
$this->app->bind('StringStackCalculator', function ($app) {
return new \Ardiakov\StringStackCalc\StringStackCalculator();
});
}
// app/Facades/StringCalc.php
namespace App\Facades;
use Illuminate\Support\Facades\Facade;
class StringCalc extends Facade {
protected static function getFacadeAccessor() { return 'StringStackCalculator'; }
}
Usage:
$result = StringCalc::calculate('5 / 2');
Validation Layer: Wrap calculations in a validator to handle edge cases:
public function safeCalculate(string $expression): ?float {
try {
return $this->calculate($expression);
} catch (\InvalidArgumentException $e) {
Log::error("Invalid expression: {$expression}");
return null;
}
}
Operator Precedence:
* before +), but no custom operators are supported out-of-the-box.Parser to add new operators (e.g., ^ for exponentiation).Floating-Point Precision:
0.1 + 0.2 → 0.30000000000000004).bcmath for financial calculations:
$result = bcdiv($calculator->calculate('0.1 + 0.2'), 1, 10);
Memory Limits:
set_time_limit() or break expressions into smaller steps.Error Handling:
InvalidArgumentException for malformed input (e.g., "2 + *").try {
$result = $calculator->calculate($expression);
} catch (\InvalidArgumentException $e) {
return "Error: {$e->getMessage()}";
}
Symfony Dependency:
DependencyInjection and Config. If using Laravel, avoid Symfony 5+ to prevent version conflicts.Custom Functions:
StringStackCalculator to add functions (e.g., sin, log):
class ExtendedCalculator extends StringStackCalculator {
protected function evaluatePostfix(array $postfix) {
$stack = [];
foreach ($postfix as $token) {
if ($token->isNumber()) {
$stack[] = $token->value;
} elseif ($token->value === 'sin') {
$stack[] = sin(array_pop($stack));
}
// ... handle other operators
}
return array_pop($stack);
}
}
Tokenization:
Token or Parser to support variables (e.g., "2 * $x"). Requires a symbol table.Unit Testing:
$this->assertEquals(0, $calculator->calculate(''));
$this->assertEquals(4, $calculator->calculate('2 + 2'));
$this->expectException(InvalidArgumentException::class);
$calculator->calculate('2 +');
config/ directory. All behavior is hardcoded in the StringStackCalculator class.+, -, *, /). For other locales (e.g., × instead of *), override the tokenize() method.$tokens = $calculator->tokenize('3 + 4 * 2');
dd($tokens); // Debug token stream
$postfix = $calculator->parseToPostfix($tokens);
dd($postfix); // Verify RPN conversion
$stack = [];
foreach ($postfix as $token) {
if ($token->isNumber()) $stack[] = $token->value;
elseif ($token->isOperator()) {
$b = array_pop($stack);
$a = array_pop($stack);
$stack[] = $a . $token->value . $b; // Log intermediate steps
}
}
How can I help you explore Laravel packages today?