sfcnr/includes/MathParser.inc
2023-12-29 01:48:17 +03:00

727 lines
18 KiB
PHP

/*
SA-MP MathParser Include (v0.1.0)
Copyright © 2012 RyDeR`
*/
#include <a_samp>
#if defined MathParser_INC
#endinput
#else
#define MathParser_INC
#endif
#define Math:: Math_
#define Parser:: Parser_
#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)
#define Debug(%0,%1,%2) \
printf("\n » [MathParser - " #%0 " @ " #%1 "] " %2)
#define Math_Operator%1(%2) \
Float:%1_Op(%2); public Float:%1_Op(%2)
#define Math_Function%1(%2) \
Float:%1_Fu(%2); public Float:%1_Fu(%2)
enum e_Assoc {
LEFT_TO_RIGHT,
RIGHT_TO_LEFT
};
enum e_Operator {
Op_szOp[MAX_OPERATOR_LENGTH char],
Op_szFunc[MAX_OPERATOR_FUNCTION_LENGTH char],
Op_iPrecedence,
e_Assoc: Op_iAssoc
};
enum e_Constant {
Const_szConst[MAX_CONSTANT_LENGTH char],
Float: Const_fValue
}
enum e_Function {
Func_szName[MAX_FUNCTION_LENGTH char],
Func_szFunc[MAX_FUNCTION_FUNCTION_LENGTH char],
Func_iParams
};
enum e_Index {
Operator,
Constant,
Function
};
enum e_Stack {
Stk_iOpIdx,
Stk_iPrecedence,
e_Assoc: Stk_iAssoc,
Float: Stk_fValue
};
enum e_Error {
ERROR_NONE,
ERROR_LOGICAL_VALUE,
ERROR_LOGICAL_OPERATOR,
ERROR_LOGICAL_PARANTHESES,
ERROR_PARAMETER_FUNCTION
};
// Avoid warnings
static stock Float: Parser::Calculate(const Float: fValue1, const Float: fValue2, const iOpIdx);
static stock Float: Parser::ParseValue(const szExpr[], &e_Error: iError, &iIdx);
static stock Float: Parser::Evaluate(const szExpr[], &e_Error: iError, &iIdx);
static
g_eiIndex[e_Index],
g_aeOperator[MAX_OPERATORS][e_Operator],
g_aeConstant[MAX_CONSTANTS][e_Constant],
g_aeFunction[MAX_FUNCTIONS][e_Function]
;
MathParser(); public MathParser() { // Prevent crash from SYSREQ.C
CallRemoteFunction("_", "");
}
stock Float: Math::ParseExpression(szExpr[], &e_Error: iError = ERROR_NONE, &iIdx = 0, const iSize = sizeof(szExpr)) {
static
bool: s_Init
;
if(!s_Init) {
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);
s_Init = !s_Init;
}
Parser::ReplaceConstants(szExpr, iSize);
Parser::ReplaceFunctions(szExpr, iError, iSize);
if(iError != ERROR_NONE) {
Debug(Error, ParseExpression, "Something is wrong with your expression! \n");
return 0.0;
}
return Parser::Evaluate(szExpr, iError, iIdx);
}
stock Math::AddFunction(const szName[], const szFunc[], const iParams) {
if(strlen(szName) >= MAX_FUNCTION_LENGTH) {
Debug(Error, AddFunction, "MAX_FUNCTION_LENGTH (%d) exceeded! \n", MAX_FUNCTION_LENGTH);
return -1;
}
if(strlen(szFunc) >= MAX_FUNCTION_FUNCTION_LENGTH) {
Debug(Error, AddFunction, "MAX_FUNCTION_FUNCTION_LENGTH (%d) exceeded! \n", MAX_FUNCTION_FUNCTION_LENGTH);
return -1;
}
if(!(-1 < iParams < MAX_FUNCTION_PARAMS)) {
Debug(Error, AddFunction, "MAX_FUNCTION_PARAMS (%d) exceeded! \n", MAX_FUNCTION_PARAMS);
return -1;
}
new
i = g_eiIndex[Function]
;
if(-1 < i < MAX_FUNCTIONS) {
static
s_szFunc[MAX_FUNCTION_FUNCTION_LENGTH]
;
format(s_szFunc, sizeof(s_szFunc), "%s_Fu", szFunc);
if(funcidx(s_szFunc) != -1) {
strpack(g_aeFunction[i][Func_szName], szName, MAX_FUNCTION_LENGTH char);
strpack(g_aeFunction[i][Func_szFunc], s_szFunc, MAX_FUNCTION_FUNCTION_LENGTH char);
g_aeFunction[i][Func_iParams] = iParams;
do {
g_eiIndex[Function]++;
} while(g_aeFunction[g_eiIndex[Function]][Func_szName][0] != EOS);
return i;
} else {
Debug(Error, AddFunction, "Unable to find functions function \"%s\"! \n", szFunc);
return -1;
}
} else {
Debug(Error, AddFunction, "MAX_FUNCTIONS (%d) exceeded! \n", MAX_FUNCTIONS);
return -1;
}
}
stock Math::RemoveFunction(const szName[]) {
for(new i = 0; i < MAX_FUNCTIONS; ++i) {
if(g_aeFunction[i][Func_szName][0] != EOS) {
static
s_szName[MAX_FUNCTION_LENGTH]
;
strunpack(s_szName, g_aeFunction[i][Func_szName], MAX_FUNCTION_LENGTH);
if(!strcmp(s_szName, szName, false, MAX_FUNCTION_LENGTH)) {
g_aeFunction[i][Func_szName][0] = EOS;
if(i < g_eiIndex[Function]) {
g_eiIndex[Function] = i;
}
return 1;
}
}
}
Debug(Error, RemoveFunction, "Unable to find function \"%s\"! \n", szName);
return 0;
}
static stock Parser::ReplaceFunctions(szExpr[], &e_Error: iError, const iSize) {
new
szFormat[MAX_FUNCTION_PARAMS] = {
'f', ...
},
szName[MAX_FUNCTION_LENGTH],
szFunc[MAX_FUNCTION_FUNCTION_LENGTH],
aszParams[MAX_FUNCTION_PARAMS][MAX_FUNCTION_PARAMS_LENGTH]
;
for(new i = 0; i < MAX_FUNCTIONS; ++i) {
if(g_aeFunction[i][Func_szName][0] != EOS) {
new
iPos = -1
;
strunpack(szName, g_aeFunction[i][Func_szName], MAX_FUNCTION_LENGTH);
while((iPos = strfind(szExpr, szName, false, (iPos + 1))) != -1) {
new
iParanthese = Parser::GetParantheseClosePos(szExpr, iPos, iSize),
iParams
;
if(iParanthese != -1) {
new
iLen = strlen(szName)
;
Parser::ReplaceFunctions(szExpr[iPos + iLen + 1], iError, iParanthese);
szExpr[iParanthese] = EOS;
if(g_aeFunction[i][Func_iParams] > 1) {
iParams = Parser::Explode(szExpr[iPos + iLen + 1], ",", aszParams);
} else {
strmid(aszParams[0], szExpr, iPos + iLen + 1, iParanthese);
iParams++;
}
szExpr[iParanthese] = ')';
if(iParams != g_aeFunction[i][Func_iParams]) {
Parser::PrintErrors((iError = ERROR_PARAMETER_FUNCTION), -1);
return 0;
}
while(iParams--) {
new
iTemp
;
format(aszParams[iParams], MAX_FUNCTION_PARAMS_LENGTH, "%." #PRECISION "f", Parser::Evaluate(aszParams[iParams], iError, iTemp));
}
iParams = (g_aeFunction[i][Func_iParams] + 2) << 2;
new
iIdx = g_aeFunction[i][Func_iParams],
Float: fValue
;
while(iIdx--) {
fValue = floatstr(aszParams[iIdx]);
#emit LOAD.S.PRI fValue
#emit HEAP 4
#emit STOR.I
#emit PUSH.ALT
}
szFormat[g_aeFunction[i][Func_iParams]] = EOS;
#emit PUSH.ADR szFormat
strunpack(szFunc, g_aeFunction[i][Func_szFunc], MAX_FUNCTION_FUNCTION_LENGTH);
#emit PUSH.ADR szFunc
#emit PUSH.S iParams
#emit SYSREQ.C CallRemoteFunction
#emit STOR.S.PRI fValue
szFormat[g_aeFunction[i][Func_iParams]] = 'f';
iParams += 4;
#emit LCTRL 4
#emit LOAD.S.ALT iParams
#emit ADD
#emit SCTRL 4
iParams = (g_aeFunction[i][Func_iParams] << 2);
#emit LCTRL 2
#emit LOAD.S.ALT iParams
#emit ADD
#emit SCTRL 2
strdel(szExpr, iPos, iParanthese + 1);
strins(szExpr, Parser::ToString(fValue), iPos, iSize);
} else {
Parser::PrintErrors((iError = ERROR_LOGICAL_PARANTHESES), -1);
return 0;
}
}
}
}
return 1;
}
stock Math::AddConstant(const szConst[], const Float: fValue) {
if(strlen(szConst) >= MAX_CONSTANT_LENGTH) {
Debug(Error, AddConstant, "MAX_CONSTANT_LENGTH (%d) exceeded! \n", MAX_CONSTANT_LENGTH);
return -1;
}
new
i = g_eiIndex[Constant]
;
if(-1 < i < MAX_CONSTANTS) {
strpack(g_aeConstant[i][Const_szConst], szConst, MAX_CONSTANT_LENGTH char);
g_aeConstant[i][Const_fValue] = fValue;
do {
g_eiIndex[Constant]++;
} while(g_aeConstant[g_eiIndex[Constant]][Const_szConst][0] != EOS);
return i;
} else {
Debug(Error, AddConstant, "MAX_CONSTANTS (%d) exceeded! \n", MAX_CONSTANTS);
return -1;
}
}
stock Math::RemoveConstant(const szConst[]) {
for(new i = 0; i < MAX_CONSTANTS; ++i) {
if(g_aeConstant[i][Const_szConst][0] != EOS) {
static
s_szConst[MAX_CONSTANT_LENGTH]
;
strunpack(s_szConst, g_aeConstant[i][Const_szConst], MAX_CONSTANT_LENGTH);
if(!strcmp(s_szConst, szConst, false, MAX_CONSTANT_LENGTH)) {
g_aeConstant[i][Const_szConst][0] = EOS;
if(i < g_eiIndex[Constant]) {
g_eiIndex[Constant] = i;
}
return 1;
}
}
}
Debug(Error, RemoveConstant, "Unable to find constant \"%s\"! \n", szConst);
return 0;
}
static stock Parser::ReplaceConstants(szExpr[], const iSize = sizeof(szExpr)) {
for(new i = 0; i < MAX_CONSTANTS; ++i) {
if(g_aeConstant[i][Const_szConst][0] != EOS) {
static
s_szConst[MAX_CONSTANT_LENGTH],
s_iPos,
s_iLen
;
strunpack(s_szConst, g_aeConstant[i][Const_szConst], MAX_CONSTANT_LENGTH);
s_iPos = -1;
s_iLen = strlen(s_szConst);
while((s_iPos = strfind(szExpr, s_szConst, false, (s_iPos + 1))) != -1) {
if(s_iPos < s_iLen || s_iPos >= MAX_CONSTANT_LENGTH) {
goto SkipCheck;
}
if(!(Parser::IsAlpha(szExpr[s_iPos - 1]) || Parser::IsAlpha(szExpr[s_iPos + s_iLen]))) {
SkipCheck: {
strdel(szExpr, s_iPos, s_iPos + s_iLen);
strins(szExpr, Parser::ToString(g_aeConstant[i][Const_fValue]), s_iPos, iSize);
}
}
}
}
}
}
stock Math::AddOperator(const szOp[], const szFunc[], const iPrecedence, const e_Assoc: iAssoc) {
if(strlen(szOp) >= MAX_OPERATOR_LENGTH) {
Debug(Error, AddOperator, "MAX_OPERATOR_LENGTH (%d) exceeded! \n", MAX_OPERATOR_LENGTH);
return -1;
}
if(strlen(szFunc) >= MAX_OPERATOR_FUNCTION_LENGTH) {
Debug(Error, AddOperator, "MAX_OPERATOR_FUNCTION_LENGTH (%d) exceeded! \n", MAX_OPERATOR_FUNCTION_LENGTH);
return -1;
}
new
i = g_eiIndex[Operator]
;
if(-1 < i < MAX_OPERATORS) {
static
s_szFunc[MAX_OPERATOR_FUNCTION_LENGTH]
;
format(s_szFunc, sizeof(s_szFunc), "%s_Op", szFunc);
if(funcidx(s_szFunc) != -1) {
strpack(g_aeOperator[i][Op_szOp], szOp, MAX_OPERATOR_LENGTH char);
strpack(g_aeOperator[i][Op_szFunc], s_szFunc, MAX_OPERATOR_FUNCTION_LENGTH char);
g_aeOperator[i][Op_iPrecedence] = iPrecedence;
g_aeOperator[i][Op_iAssoc] = iAssoc;
do {
g_eiIndex[Operator]++;
} while(g_aeOperator[g_eiIndex[Operator]][Op_szOp][0] != EOS);
return i;
} else {
Debug(Error, AddOperator, "Unable to find operator function \"%s\"! \n", szFunc);
return -1;
}
} else {
Debug(Error, AddOperator, "MAX_OPERATORS (%d) exceeded! \n", MAX_OPERATORS);
return -1;
}
}
stock Math::RemoveOperator(const szOp[]) {
for(new i = 0; i < MAX_OPERATORS; ++i) {
if(g_aeOperator[i][Op_szOp][0] != EOS) {
if(!strcmp(g_aeOperator[i][Op_szOp], szOp, false, MAX_OPERATOR_LENGTH)) {
g_aeOperator[i][Op_szOp][0] = EOS;
if(i < g_eiIndex[Operator]) {
g_eiIndex[Operator] = i;
}
return 1;
}
}
}
Debug(Error, RemoveOperator, "Unable to find operator \"%s\"! \n", szOp);
return 0;
}
static stock Float: Parser::Evaluate(const szExpr[], &e_Error: iError, &iIdx) {
new
iStack,
eStack[e_Stack],
aeStack[MAX_OPERATORS][e_Stack],
Float: fValue = Parser::ParseValue(szExpr, iError, iIdx)
;
aeStack[0][Stk_iOpIdx] = -1;
while(iStack > -1) {
if(iError != ERROR_NONE) {
return 0.0;
}
Parser::ParseOperator(szExpr, iError, iIdx, eStack);
while(eStack[Stk_iPrecedence] < aeStack[iStack][Stk_iPrecedence] || (eStack[Stk_iPrecedence] == aeStack[iStack][Stk_iPrecedence] && eStack[Stk_iAssoc] == LEFT_TO_RIGHT)) {
if(aeStack[iStack][Stk_iOpIdx] == -1) {
return fValue;
}
fValue = Parser::Calculate(aeStack[iStack][Stk_fValue], fValue, aeStack[iStack][Stk_iOpIdx]);
iStack--;
}
aeStack[++iStack][Stk_iOpIdx] = eStack[Stk_iOpIdx];
aeStack[iStack][Stk_iPrecedence] = eStack[Stk_iPrecedence];
aeStack[iStack][Stk_iAssoc] = eStack[Stk_iAssoc];
aeStack[iStack][Stk_fValue] = fValue;
fValue = Parser::ParseValue(szExpr, iError, iIdx);
}
return 0.0;
}
static stock Float: Parser::Calculate(const Float: fValue1, const Float: fValue2, const iOpIdx) {
if(g_aeOperator[iOpIdx][Op_szOp][0] != EOS) {
static
s_szFunc[MAX_OPERATOR_FUNCTION_LENGTH]
;
strunpack(s_szFunc, g_aeOperator[iOpIdx][Op_szFunc], MAX_OPERATOR_FUNCTION_LENGTH);
return Float: CallRemoteFunction(s_szFunc, "ff", fValue1, fValue2);
}
return 0.0;
}
static stock Parser::ParseOperator(const szExpr[], &e_Error: iError, &iIdx, eStack[e_Stack]) {
Parser::StripSpaces(szExpr, iIdx);
eStack[Stk_iOpIdx] = 0;
eStack[Stk_iPrecedence] = 0;
eStack[Stk_iAssoc] = LEFT_TO_RIGHT;
if(szExpr[iIdx] == ')') {
return 1;
}
if(iError == ERROR_NONE) {
for(new i = 0, iLen = 0; i < MAX_OPERATORS; ++i) {
static
s_szOp[MAX_OPERATOR_LENGTH]
;
strunpack(s_szOp, g_aeOperator[i][Op_szOp], MAX_OPERATOR_LENGTH);
if(s_szOp[0] == szExpr[iIdx]) {
if(!strcmp(szExpr[iIdx], s_szOp, false, (iLen = strlen(s_szOp)))) {
iIdx += iLen;
eStack[Stk_iOpIdx] = i;
eStack[Stk_iPrecedence] = g_aeOperator[i][Op_iPrecedence];
eStack[Stk_iAssoc] = g_aeOperator[i][Op_iAssoc];
return 1;
}
}
}
}
Parser::PrintErrors((iError = ERROR_LOGICAL_OPERATOR), iIdx);
return 0;
}
static stock Float: Parser::ParseValue(const szExpr[], &e_Error: iError, &iIdx) {
Parser::StripSpaces(szExpr, iIdx);
if(iError == ERROR_NONE) {
switch(szExpr[iIdx]) {
case '0' .. '9': {
switch(szExpr[iIdx + 1]) {
case 'x', 'X': {
iIdx += 2;
new
iValue
;
while(('a' <= szExpr[iIdx] <= 'f' || 'A' <= szExpr[iIdx] <= 'F' || ('0' <= szExpr[iIdx] <= '9'))) {
switch(szExpr[iIdx]) {
case 'a' .. 'f': {
iValue = (iValue << 4) + (10 + szExpr[iIdx] - 'a');
}
case 'A' .. 'F': {
iValue = (iValue << 4) + (10 + szExpr[iIdx] - 'A');
}
case '0' .. '9': {
iValue = (iValue << 4) + (szExpr[iIdx] - '0');
}
}
iIdx++;
}
return float(iValue);
}
case 'b', 'B': {
iIdx += 2;
new
iValue
;
while(('0' <= szExpr[iIdx] <= '1')) {
iValue = (iValue << 1) | (szExpr[iIdx++] - '0');
}
return float(iValue);
}
default: {
new
Float: fValue,
Float: fScale
;
while(('0' <= szExpr[iIdx] <= '9' || szExpr[iIdx] == '.')) {
if(szExpr[iIdx] == '.') {
fScale = 1.0;
} else {
fValue = (fValue * 10.0) + (szExpr[iIdx] - '0');
fScale *= 10.0;
}
iIdx++;
}
return fValue / ((fScale != 0.0) ? (fScale) : (1.0));
}
}
}
case '(': {
iIdx++;
new
Float: fValue = Parser::Evaluate(szExpr, iError, iIdx)
;
Parser::StripSpaces(szExpr, iIdx);
if(szExpr[iIdx] != ')') {
Parser::PrintErrors((iError = ERROR_LOGICAL_PARANTHESES), iIdx);
return 0.0;
}
iIdx++;
return fValue;
}
case '+': {
iIdx++;
return Parser::ParseValue(szExpr, iError, iIdx);
}
case '-': {
iIdx++;
return -Parser::ParseValue(szExpr, iError, iIdx);
}
default: {
Parser::PrintErrors((iError = ERROR_LOGICAL_VALUE), iIdx);
}
}
}
return 0.0;
}
static stock Parser::GetParantheseClosePos(szExpr[], iPos, const iSize) {
new
iOpened,
iClosed
;
while(iPos < iSize-1) {
if(szExpr[iPos] == '(') {
iOpened++;
} else if(szExpr[iPos] == ')') {
iClosed++;
if(iOpened == iClosed) {
break;
}
}
++iPos;
}
if(iOpened != iClosed) {
return -1;
}
return iPos;
}
static stock Parser::Explode(szSrc[], const szDelim[], aszDest[][], iSize = sizeof(aszDest), iLen = sizeof(aszDest[])) { // Slice
new
iPos = -1,
iLastPos,
i
;
goto LoopEnd;
do {
strcat((aszDest[i][0] = EOS, aszDest[i]), szSrc[iLastPos], min(iLen, iPos - iLastPos + 1));
if (++i >= iSize)
break;
LoopEnd:
iLastPos = ++iPos;
} while((iPos = strfind(szSrc, szDelim, false, iPos)) != -1);
if(i < iSize) {
strcat((aszDest[i][0] = EOS, aszDest[i++]), szSrc[iLastPos], iLen);
}
return i;
}
static stock Parser::PrintErrors(const e_Error: iError, const iIdx) {
switch(iError) {
case ERROR_LOGICAL_VALUE: {
Debug(Syntax Error, Parsing, "Invalid value found at index %d! \n", iIdx);
}
case ERROR_LOGICAL_OPERATOR: {
Debug(Syntax Error, Parsing, "Invalid operator found at index %d! \n", iIdx);
}
case ERROR_LOGICAL_PARANTHESES: {
Debug(Syntax Error, Parsing, "Invalid paranthese logic found at index %d! \n", iIdx);
}
case ERROR_PARAMETER_FUNCTION: {
Debug(Syntax Error, Parsing, "Invalid parameters found in a function! \n");
}
}
}
static stock Parser::StripSpaces(const szInput[], &iIdx) {
while(Parser::IsSpace(szInput[iIdx]) != 0) {
iIdx++;
}
}
static stock Parser::IsSpace(const iChar) {
switch(iChar) {
case ' ', '\n', '\r', '\t': {
return 1;
}
}
return 0;
}
static stock Parser::IsAlpha(const iChar) {
switch(iChar) {
case 'a' .. 'z', 'A' .. 'Z': {
return 1;
}
}
return 0;
}
static stock Parser::ToString(const Float: fValue) {
static
szValue[16]
;
format(szValue, sizeof(szValue), "%." #PRECISION "f", fValue);
return szValue;
}
Math::Operator E(const Float: fValue, const Float: fExponent) {
return fValue * floatpower(10.0, fExponent);
}
Math::Operator Pow(const Float: fValue1, const Float: fValue2) {
return floatpower(fValue1, fValue2);
}
Math::Operator Mul(const Float: fValue1, const Float: fValue2) {
return fValue1 * fValue2;
}
Math::Operator Div(const Float: fValue1, const Float: fValue2) {
return fValue1 / fValue2;
}
Math::Operator Mod(const Float: fValue1, const Float: fValue2) {
return fValue1 - fValue2 * floatround((fValue1 / fValue2), floatround_floor);
}
Math::Operator Add(const Float: fValue1, const Float: fValue2) {
return fValue1 + fValue2;
}
Math::Operator Sub(const Float: fValue1, const Float: fValue2) {
return fValue1 - fValue2;
}
Math::Function Log(const Float: fValue, const Float: fBase) {
return floatlog(fValue, fBase);
}
Math::Function Sqrt(const Float: fValue) {
return floatsqroot(fValue);
}