FormCalc - Formula Calculator Engine
A Rust implementation of the Formula Calculator Engine, providing a powerful formula evaluation system with dependency management.
Features
- Formula Parsing: Parse and evaluate complex formulas with arithmetic, logical, and comparison operations
- Dependency Management: Automatically resolve and execute formulas in the correct order based on dependencies
- Parallel Execution: Formulas in the same dependency layer are executed in parallel for maximum performance
- Built-in Functions: Support for mathematical, string, and date functions
- Custom Functions: Register custom functions to extend functionality
- Variables: Support for variables in formulas
- Type System: Strong typing with support for numbers, strings, and booleans
- Error Handling: Comprehensive error reporting with detailed messages
Formula Syntax
Basic Expressions
return 2 + 2 // Arithmetic
return 'Hello' + ' World' // String concatenation
return 5 > 3 // Comparison
return true and false // Logical operations
Conditional Statements
if (x > 10) then
return 'High'
else if (x > 5) then
return 'Medium'
else
return 'Low'
end
Built-in Functions
Mathematical Functions
max(a, b)- Maximum of two numbersmin(a, b)- Minimum of two numbersrnd(value, decimals)- Round to specified decimal placesceil(value)- Round up to nearest integerfloor(value)- Round down to nearest integerexp(value)- Exponential functionmod- Modulo operator
Date Functions
year(date)- Extract year from date stringmonth(date)- Extract month from date stringday(date)- Extract day from date stringadd_days(date, days)- Add days to a dateget_diff_days(date1, date2)- Get difference between dates in daysget_diff_months(date1, date2)- Get difference in months
String Functions
substr(string, start, length)- Extract substringpadded_string(string, width)- Pad string with zeros
Formula Functions
get_output_from('formula_name')- Get result from another formula
Usage Examples
Basic Calculation
use ;
let mut engine = new;
let formula = new;
engine.execute.unwrap;
let result = engine.get_result.unwrap;
assert_eq!;
Using Variables
use ;
let mut engine = new;
engine.set_variable;
engine.set_variable;
let formula = new;
engine.execute.unwrap;
let result = engine.get_result.unwrap;
assert_eq!;
Formula Dependencies
use ;
let mut engine = new;
let formula1 = new;
let formula2 = new;
let formula3 = new;
// The engine automatically resolves dependencies and executes in correct order
engine.execute.unwrap;
let result = engine.get_result.unwrap;
assert_eq!;
Custom Functions
use ;
use Arc;
// Define a custom function
;
let mut engine = new;
engine.register_function;
let formula = new;
engine.execute.unwrap;
let result = engine.get_result.unwrap;
assert_eq!;
Conditional Logic
use ;
let mut engine = new;
engine.set_variable;
let formula = new;
engine.execute.unwrap;
let result = engine.get_result.unwrap;
assert_eq!;
Supported Operators
Arithmetic
+- Addition (also string concatenation)-- Subtraction*- Multiplication/- Division^- Powermod- Modulo
Comparison
=- Equal<>- Not equal<- Less than>- Greater than<=- Less than or equal>=- Greater than or equal
Logical
and- Logical ANDor- Logical OR!- Logical NOT
Error Handling
use ;
let mut engine = new;
let formula = new;
engine.execute.unwrap;
// Check for errors
if let Some = engine.get_errors.get
Architecture
The engine follows the architecture:
- Lexer - Tokenizes the input formula
- Parser - Builds an Abstract Syntax Tree (AST)
- Evaluator - Evaluates the AST using the visitor pattern
- DAG - Manages formula dependencies using a directed acyclic graph
- Engine - Orchestrates parsing, dependency resolution, and execution
Performance Considerations
- Parallel Execution: Formulas in the same dependency layer are executed in parallel using Rayon
- Results are cached to avoid re-computation
- Function results are cached per execution
- Layer-by-layer execution ensures dependencies are resolved correctly
Contributing
We welcome contributions to FormCalc! Here's how you can help:
Reporting Issues
- Use the GitHub issue tracker to report bugs
- Describe the issue clearly with steps to reproduce
- Include sample formulas and expected vs actual behavior
Submitting Changes
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Make your changes with clear, descriptive commit messages
- Add tests for any new functionality
- Ensure all tests pass (
cargo test) - Run the formatter (
cargo fmt) - Run the linter (
cargo clippy) - Submit a pull request
Development Guidelines
- Follow Rust naming conventions and idioms
- Write unit tests for new features
- Update documentation for API changes
- Keep commits focused and atomic
License
This project is licensed under the MIT License - see the LICENSE file for details.