[Include] MathParser.inc ─ Advanced mathematical expression parser
#1

Introduction

It has been quite a while since I last released something as I was running out of ideas and didn't know what to script nor program, but luckily, ****** PM'ed me about some useful ideas and finally picked this one out. This include simply parses mathematical expressions in a correct way. You can dynamically add your own operators, constants and functions as you wish. More examples and features below.

Functions

Here's a quick list of the main functions you can use:
pawn Код:
stock Float: Math::ParseExpression(szExpr[], &e_Error: iError = ERROR_NONE, &iIdx = 0, const iSize = sizeof(szExpr));
stock Math::AddFunction(const szName[], const szFunc[], const iParams);
stock Math::RemoveFunction(const szName[]);
stock Math::AddConstant(const szConst[], const Float: fValue);
stock Math::RemoveConstant(const szConst[]);
stock Math::AddOperator(const szOp[], const szFunc[], const iPrecedence, const e_Assoc: iAssoc);
stock Math::RemoveOperator(const szOp[]);
A list of the defines:
pawn Код:
#if !defined PRECISION
    #define PRECISION 3
#endif

#define MAX_OPERATORS (32)
#define MAX_OPERATOR_LENGTH (8)
#define MAX_OPERATOR_FUNCTION_LENGTH (20)

#define MAX_CONSTANTS (32)
#define MAX_CONSTANT_LENGTH (20)

#define MAX_FUNCTIONS (32)
#define MAX_FUNCTION_LENGTH (16)
#define MAX_FUNCTION_FUNCTION_LENGTH (20)
#define MAX_FUNCTION_PARAMS (8)
#define MAX_FUNCTION_PARAMS_LENGTH (32)
If you define PRECISION before this include, you can have your own decimal precision value instead.

And the error types:
pawn Код:
enum e_Error {
    ERROR_NONE,
    ERROR_LOGICAL_VALUE,
    ERROR_LOGICAL_OPERATOR,
    ERROR_LOGICAL_PARANTHESES,
    ERROR_PARAMETER_FUNCTION
};
Examples

Basic examples

First of all, the following operators, constants and functions are pre-added:
pawn Код:
Math::AddOperator("E", "E", 25, RIGHT_TO_LEFT);
Math::AddOperator("^", "Pow", 20, RIGHT_TO_LEFT);
Math::AddOperator("*", "Mul", 15, LEFT_TO_RIGHT);
Math::AddOperator("/", "Div", 15, LEFT_TO_RIGHT);
Math::AddOperator("%", "Mod", 15, LEFT_TO_RIGHT);
Math::AddOperator("+", "Add", 10, LEFT_TO_RIGHT);
Math::AddOperator("-", "Sub", 10, LEFT_TO_RIGHT);
               
Math::AddConstant("pi", 3.141592);
Math::AddConstant("e", 2.718281);
               
Math::AddFunction("log", "Log", 2);
Math::AddFunction("sqrt", "Sqrt", 1);
Let's start with a simple example:
pawn Код:
new
    szExpr[128] = "3 + 0x4 * 0x2 / (1 - 0b00000101) ^ 2 ^ 3"
;
printf("Solution: %." #PRECISION "f", Math::ParseExpression(szExpr));
Will print Solution: 3.000. PRECISION decides the amount of numbers after the comma which is 3 in this case. Please note you can also use hexadecimal and binary notation.

Example with the pre-added function(s):
pawn Код:
new
    szExpr[128] = "log(32, 2.0)"
;
printf("Solution: %." #PRECISION "f", Math::ParseExpression(szExpr));
Will print 5.000.

We can also use constants and expressions in our function parameters:
pawn Код:
new
    szExpr[128] = "(sqrt(log(32.0, 2.0) - 1) ^ 2 ^ 3) * 4"
;
printf("Solution: %." #PRECISION "f", Math::ParseExpression(szExpr));
Will print 1024.000.

Let's use some constants:
pawn Код:
new
    szExpr[128] = "e ^ pi"
;
printf("Solution: %." #PRECISION "f", Math::ParseExpression(szExpr));
Is the same as 2.718 ^ 3.141 and results 23.119.

And some other examples:
pawn Код:
new
    szExpr[128] = "((3 * (2 * (sqrt(25) + log(0x2000, 2.0) + 3) * 3 ^ (3 % 2))) / 0b00100000 * e)"
;
printf("Solution: %." #PRECISION "f", Math::ParseExpression(szExpr));
Will print 32.106

pawn Код:
new
    szExpr[128] = "5E+6 + 20.556"
;
printf("Solution: %." #PRECISION "f", Math::ParseExpression(szExpr));
Will print 5000020.500 (rounded)

How to customize?

It's very easy to define your own operators, functions or constants. Let's start with the operator:
pawn Код:
Math::AddOperator("<<", "SHL", 5, LEFT_TO_RIGHT); // To be called before parsing (ex. OnGameModeInit)

Math::Operator SHL(const Float: fValue1, const Float: fValue2) { // To be added somewhere in your script
    return float(floatround(fValue1) << floatround(fValue2));
}
You might be wondering like "what the hell are those parameters for"? The answer is quite simple, each operator has its own precedence and associativity. I usually look here for the data I need, but as you may have noticed, the precedences are a bit different. I just use them in reverse order with other values (as the value doesn't really matter, just make sure it's bigger or smaller).

pawn Код:
Math::AddFunction("dist", "Dist", 4); // To be called before parsing (ex. OnGameModeInit)

Math::Function Dist(const Float: fX1, const Float: fX2, const Float: fY1, const Float: fY2) { // To be added somewhere in your script
    return floatsqroot(floatpower(fX1 - fX2, 2.0) + floatpower(fY1 - fY2, 2.0));
}
This one is quite obvious I guess. In the first parameter you add the name you want to use in your expression, the second one the function to call, in the last parameter the number of parameters the function has.

pawn Код:
Math::AddConstant("MAX_PLAYERS", MAX_PLAYERS); // To be added somewhere in your script
Same here. In the first parameter the name you want to use in your expression and in the second one the value of it.

Let's use them all:
pawn Код:
new
    szExpr[128] = "(dist(5, -9, pi, e) << 10) / MAX_PLAYERS"
;
printf("Solution: %." #PRECISION "f", Math::ParseExpression(szExpr));
Will print us 28.672 when you consider MAX_PLAYERS to be 500.

What if I enter an invalid expression?

The answer is simple, the iError variable will change from ERROR_NONE to an error index from e_Error. You can retrieve the error index by adding a second parameter to Math::ParseExpression. Example:
pawn Код:
new
    szExpr[128] = "5 + 5 + ",
    e_Error: iError
;
printf("Solution: %." #PRECISION "f", Math::ParseExpression(szExpr, iError));
Will set iError to ERROR_LOGICAL_VALUE in this case because you don't have any value added after the + operator.

Download

MathParser.inc

Changelog
  • 02/02/2011 ─ v0.1.0:
    • Initial release
  • 04/02/2011 ─ v0.1.0:
    • Fixes:
      • Bug regarding decimal and hexadecimal values.
    • Removes:
      • The E (scientific notation) function is removed and is replaced with an operator instead.
      • A small, unnecessary debugging code.
    • Adds:
      • Due to this bugfix, it is now possible to use E (scientific notation) as an operator which means you can now use this syntax:
        pawn Код:
        new
            szExpr[128] = "5E-3"
        ;
        printf("Solution: %." #PRECISION "f", Math::ParseExpression(szExpr));
        Will print us 0.005.

        pawn Код:
        new
            szExpr[128] = "5.5128E+5"
        ;
        printf("Solution: %." #PRECISION "f", Math::ParseExpression(szExpr));
        And this will print us 551280.000
Notes

I'm not sure if I explained everything, but this is pretty much it. This include may contains bugs so please report if you find one. Also please note to allocate enough cells in your szExpr string-array containing your expression in case it replaces (functions, constants) and the length becomes something longer than the total size of your expression string.
Reply
#2

Wow, very nice and usefull!
Reply
#3

This, actually looks helpfull (:
Reply
#4

RyDeR`always amazes me. 5/5
Awesome, no comments...
Reply
#5

Actually I won't use it... but anyways the script looks very clean and nice, I like it....

Very nice work, keep it up
Reply
#6

Very nice release RyDeR`!
Reply
#7

Thanks! I'm open for suggestions and improvements, so if there are any, let me know.
Reply
#8

Again a great release from you RyDeR`
Reply
#9

Nice!
Reply
#10

Thanks - glad you liked it!
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)