This project consists in evaluating a mathematical expression.
It was realized as a student project during the beginning of my 3rd year at EPITECH.
The complexity behind the project doesn't come from the main problem itself but from its implementation in Haskell and constraints added by our instructor.
For a bit of context with the constraints:
-
The project ends up not using that many keywords.
do
,case
,if
(and others...) aren't allowed most of the time as we will end up writing imperative code instead of functional. -
For chaining function calls, you will mostly see the application operator
$
and the composition operator.
being used. -
For conditions, you will mostly see pattern matching and guards being used.
-
Majority of types used as parameters and return types of functions are newly created types (via
type
,newtype
ordata
) for ease of comprehension and logic throughout the program as well as rigorousness in type conversion. -
Almost every type
deriving
frominstances
will use those derived instances in production and not specifically for another environment.
A common example of this bad habit is that usually people derive fromEq
as it makes testing for those types easier in testing environment.
Deriving fromEq
when it serves no purpose in the production environment makes no sense and also adds unreacheable code for coverage.
Therefore, we try to reduce this bad habit as much as we can with the tools we are given.
Inside the testing environment, where we need an instance ofEq
for assertion checking, it is being bypassed by wrapping those types inside wrapping types and making them derive from their own instance ofEq
.
Most specifically, we wrap the return of the function we are testing and the value we are using as expected result. As they have matching types, everything perfectly works.
Exceptions
don't follow this rule because, in testing, as we can't wrap the exception that was thrown. -
We also had to create a tree of the expression that was given as parameter before evaluating it for a result.
This project has been setup with Stack and works with a Makefile that wraps this framework with different rules.
Command | Result |
---|---|
make |
Builds a funEvalExpr executable. |
make tests_run |
Runs unit tests. |
make clean |
Cleans build dumps, keeping the executable. |
make fclean |
Removes all of the files created by the build. |
make re |
Calls make fclean and then make . |
Use the newly created funEvalExpr
executable to launch the project.
The expression to evaluate needs to be given as argument to the program as such:
$ ./funEvalExpr "(3+1.5)*3"
The program outputs the result rounded down to 2 decimal places and returns 0
upon success.
$ ./funEvalExpr "(0.345+5)*(-2-1)/3"
-5.34
In case of error with the given arguments (e.g. division by 0) or with the expression itself, the program displays information about the error and exits by returning 84
.
The syntax of an expression is as a mathematical expression goes.
You should use quotes (""
or ''
) when entering an expression with parentheses ()
or spaces
as those characters are often treated differently by your shell.
The limits are the operators and tokens of expression this project handles (found below).
The different operators the program understands are the followings:
Operator | Description | Binary / Unary |
---|---|---|
+ |
Plus sign | Binary & Unary |
- |
Minus sign | Binary & Unary |
* |
Multiplication sign | Binary |
/ |
Division sign | Binary |
^ |
Exponent sign | Binary |
Parentheses are allowed to influence operation priorities.
Spaces & tabulations are ignored in the expression.