From 754af5bd4b95a0fbde238d81f797162186bcc969 Mon Sep 17 00:00:00 2001 From: kichik Date: Mon, 15 Sep 2003 23:20:36 +0000 Subject: [PATCH] Another cool plug-in by brainsucker, a calculator :) git-svn-id: https://svn.code.sf.net/p/nsis/code/NSIS/trunk@2920 212acab6-be3b-0410-9dea-997c60f758d6 --- Contrib/Math/Math.txt | 190 ++++ Contrib/Math/Source/Math.c | 1518 +++++++++++++++++++++++++++++++ Contrib/Math/Source/Math.h | 87 ++ Contrib/Math/Source/Math.sln | 21 + Contrib/Math/Source/Math.vcproj | 240 +++++ Contrib/Math/Source/MyMath.c | 353 +++++++ Contrib/Math/Source/MyMath.h | 164 ++++ Contrib/Math/Source/mathcrt.h | 87 ++ Contrib/Math/Source/mathcrt.lib | Bin 0 -> 25562 bytes Contrib/Math/Source/plugin.c | 135 +++ Contrib/Math/math.nsi | 39 + Contrib/Math/mathtest.ini | 101 ++ Contrib/Math/mathtest.nsi | 171 ++++ Contrib/Math/mathtest.txt | 7 + Plugins/Math.dll | Bin 0 -> 14336 bytes 15 files changed, 3113 insertions(+) create mode 100644 Contrib/Math/Math.txt create mode 100644 Contrib/Math/Source/Math.c create mode 100644 Contrib/Math/Source/Math.h create mode 100644 Contrib/Math/Source/Math.sln create mode 100644 Contrib/Math/Source/Math.vcproj create mode 100644 Contrib/Math/Source/MyMath.c create mode 100644 Contrib/Math/Source/MyMath.h create mode 100644 Contrib/Math/Source/mathcrt.h create mode 100644 Contrib/Math/Source/mathcrt.lib create mode 100644 Contrib/Math/Source/plugin.c create mode 100644 Contrib/Math/math.nsi create mode 100644 Contrib/Math/mathtest.ini create mode 100644 Contrib/Math/mathtest.nsi create mode 100644 Contrib/Math/mathtest.txt create mode 100644 Plugins/Math.dll diff --git a/Contrib/Math/Math.txt b/Contrib/Math/Math.txt new file mode 100644 index 00000000..551207f7 --- /dev/null +++ b/Contrib/Math/Math.txt @@ -0,0 +1,190 @@ +Math::Script NSIS plugin. + +Another huge, undocumented, bug-full, but "might be useful" plugin. + +C-like style scripting (operators at least). +Tip1: plugin watches the case of the letters. +Tip2: plugin makes almost no error checks. So YOU should check your script +twice before run :) + +New HOW TO USE: run the MathTest.Exe, and try yourself. After spending +some minutes your should be able to write your script by yourself. +To include it to your NSIS script just insert that (don't forget /NOUNLOAD +in case of multiple calls): + Math::Script /NOUNLOAD "YourScript1" + Math::Script /NOUNLOAD "YourScript2" + Math::Script "YourScriptFinal" + +How to use it? Simple: + Strcpy $0 "Brainsucker" + Math::Script "a = 'Math'; B = 'Script'; r0 += ' wants to use ' + a + '::' + b +'!'" + DetailPrint "$0" +That string will fill r0 with some shit. + +Here are some other samples: + 10! (factorial, r0 will contain '10! = 362880'): + r0 = '10! = ' + (1*2*3*4*5*6*7*8*9) + the same: + a = b = 1; #{++a <= 10, b = b*a}; r0 = (a-1) + '! = ' + b + Some floating point: + Strcpy $R0 "1e1" + Math::Script "pi = 3.14159; R1 = 2*pi*R0; r0 = 'Length of circle with radius ' + R0 + ' is equal to ' + R1 + '.'" + Detailprint "$0" + +Ok. Variables. +NSIS: r0-r9 -> $0-$9. R0-R9 -> $R0-$R9. +Also CL ($CMDLINE), ID ($INSTDIR), OD ($OUTDIR), LG ($LANG), ED ($EXEDIR). +User definable: name starting from character, up to 28 letters long. + +Stacks. Two stacks are supported: NSIS stack and plugin's own stack. I see no +reasons for using plugin stack, but if you will, remember - the plugin stores +variables used at function to that stack before function execution, and restores +after execution. Even less I recommend you to use NSIS stack. You should use it +only for input/output. +How to use? It's variable styled. Plugins stack is associated with S variable, +and NSIS stack associated with NS variable. To push to stack just do "S=0" or +"NS=0", to pop from stack "a=S" or "b=NS". Combined operations supported too: +"S += 1.5" will increment value at the top of stack by 1.5. + +Supported types: int (in fact that is __int64), float (double in fact), +string. +Int: just numbers, may include sign. +Float: -123.456, 123.456e-78, 123e-45 +String: something in quotes ("", '', ``). + +There is also an array type. It is actually a reference type, so if b is array +and you will perform "a=b", the a and b will reference a single array. +To create a copy of array, use ca func: dest = ca(source). Btw - you couldn't +control dimensions of arrays - they are autosized. +To declare array: +a = {}; +To declare array and initialize some items with values: +{"Hello!", "Use", "mixed types", 1.01e23, "like that" ,1234}; +To access array: +a[index] = "Cool"; + +Also [] operation could be used to strings. str[x] gives you a single char with +index x (zero-based) as new string. str[-x] - the same, but x counts from the +string end (so the last char is -1). str[x,y] gives you characters in range x-y +(inclusive), both x and y could be <0 - in this case they counted from the end +of the string. + +The function could be useful - is conversion of arrays to strings and back. +Example: +a = a("Hello"); str = s(a); +After running such script array a will contain 6 integers (chars and last zero +- end of string), and str will contain your string back. + +Operators (some binary, some unary): +>>= <<= -= += /= *= |= &= ^= %= -- ++ >> << && || <= =< >= => != == += + - * / % < > & | ^ ~ ! +Only some are applicable to float (logic & arithmetic) and string (+ and logic) +of course. + +Script is set of expressions (mathematical in general) delimeted with ';'. +Processing is not mathematicaly right (2+2*2 will give 8, not 6), so use round +brakes (for ex: 2+(2*2) ). + +Flow control: + if-then-else like: #[if-expression, then-expr, else-expr] + example: + #[a==0, b=1; c=2, b *= (--c); c/=10] + C eq: + if (a==0) { b=1; c=2;} else { b*=(c++);c-=10; } + while (expr) do; like #{expr, do} + example: + #{(c<1.1e25)&&(b < 10), b++; c*=1.23} + C eq: + while ((c<1.1e25)&&(b<10)) { b++; c*=1.23; } + +WATCH OUT! Comma (,) separates if-expr, then-expr, and else-expr from each +other. All sub-expressions separated by (;) are the part of one expression, +and the result of the last one of these sub-exprs gives you the result of +expression. + +All the shit (like variables and functions) will be saved between calls if +you'll use /NOUNLOAD or setpluginunload alwaysoff. + +Functions: + type conversions: + l(string) returns the length of string or array argument + s(source) converts source to string type + i(source) converts source to int type + f(source) converts source to float type + c(source) if source is string, returns int value of first + char, if source is int, returns string which consists + of a single char (source) (+0 terminator). + a(source) converts source to array (only string supported) + ff(float, format) converts float to string, with format + options. + options = precision + flags. + Precision shows how many digits after decimal point + will be shown. Flags: + 16 (or 0x10) - No Exponential View + (number will be shown as 123.123) + 32 (or 0x20) - Only exponential view + (number will be shown as 123.12e123) + 64 (or 0x40) - use 'E' character instead of 'e' + By default the plugin decides itself how to show your + number. + + math (description of all these functions is available at MSDN, use the + second given name for search): + sin(x), sin Sine of argument + cos(x), cos Cosine of argument + cel(x), ceil Ceil of argument (no fract. part) + csh(x), cosh Hyperbolic Cosine of Argument + exp(x), exp Exponential + abs(x), abs Absolute value (warning: float) + flr(x), floor Floor of argument (no fract. part) + asn(x), asin ArcSine of argument + acs(x), acos ArcCosine of argument + atn(x), atan ArcTangent of argument + ln(x), log Exponential Logarithm + log(x), log10 Decimal logarithm + snh(x), sinh Hyperbolic Sine of Argument + sqt(x), sqrt Square root of argument + tan(x), tan Tangent of argument + tnh(x), tanh Hyperbolic tangent of argument + + functions taking two arguments + at2(x, y) atan2 Arctangent of the value (y/x) + pow(x, y) pow power, x^y + fmd(x, y) fmod floating point remainder + fex(x, o) frexp Gets the mantissa (result = r) + and exponent (o) of floating-point + number (x): x = r*(2^o) + mdf(x, o) modf Splits a floating-point value into + fractional and integer parts. + +User-defined functions. +It's very simple. Example: + test(a,b)a+b; +After that test(1,2) will give you 3. If you need more than one expression, use +round brakes: + test2(a,b) (a=a+b; b *= a); +The result of function is always the result of last expression. +As said before it better not to use stack (S) in between function calls. +It will be better to develop variable-safe functions, i.e. functions which will +not corrupt variables. For this you should either push/pop them to stack, or +declare as additional arguments, which will never be used. Example: + test3(a,b,c) (c=10; #{--c > 0, a=sqrt(a*b)}; a) +No matter how many arguments will be passed to function all three vars will be +saved. +Such variable-safe functions could be recursive: + Math::Script /NOUNLOAD 'rec(a) (#[a > 0, rec(a-1), 0]+a);' + Math::Script 'R1 = rec(10)' +will set R1 to right result 55. +Sometimes functions will need to return more than one value, in this case you +could declare argument as referent (b at example): + test4(a, &b) (*b = a*a; a*a*a) +In this case test4 will return a^3, and if we will call it like that test4(a,c), +it will place a^2 to c. BUT! Note: you should use de-referencer (*) with variable, +at example *b. CAUTION: never use the same variable as function internal reference +variable and external argument variable (for example test4(a,b)). It will surely +fail. Also: if you declared argument as reference - you should never supply +a constant expression to it. It could be either array item (array[1]), NSIS +register R0, any of the user variables (beside the variable with the same name:) +, but never the constant. + +(c) Nik Medved (brainsucker) \ No newline at end of file diff --git a/Contrib/Math/Source/Math.c b/Contrib/Math/Source/Math.c new file mode 100644 index 00000000..44137030 --- /dev/null +++ b/Contrib/Math/Source/Math.c @@ -0,0 +1,1518 @@ +#include +#include "mathcrt.h" +#include "MyMath.h" +#include "Math.h" + +extern "C" int _fltused; +int _fltused; +ExpressionItem *stack; + +int UserVarsCount, UserFuncsCount; +UserVar UserVars[MAX_USER_VARS]; +UserFunc UserFuncs[MAX_USER_FUNCS]; + +void PrintTree(ExpressionItem *root, char *str); +void ParseString(char *&sp, ExpressionItem* &itemplace, int options); +void CleanupItems(ExpressionItem* &itemplace); + +void PlaceNewItem(ParseInfo *pi) +{ + ExpressionItem *newroot; + if (pi->OpsStack) + { + // second operand for our operator + *((ExpressionItem **)&(pi->OpsStack->param2)) = pi->item; + newroot = pi->OpsStack; + pi->OpsStack = newroot->next; + newroot->next = NULL; + } else + { + // no operands found - we have got new root + newroot = pi->item; + } + + // sometimes there could be more than one operators at stack, pop them all + if (pi->OpsStack) + { + // Another operator which will consume first one + pi->item = newroot; + PlaceNewItem(pi); + } else + { + if (pi->SetupNewRoot) + { + (*pi->root)->next = newroot; + pi->root = &((*pi->root)->next); + pi->SetupNewRoot = 0; + } + if (pi->place == *pi->root) pi->place = *pi->root = newroot; + else *pi->root = newroot; + } +} + +#define NSIS_VARS_COUNT 27 +#define NSIS_VARS_STACK 25 +#define NSIS_VARS_NSTACK 26 + +typedef char smallstr[2]; +const smallstr NSISVariablesNames[NSIS_VARS_COUNT] = {{'r','0'}, {'r','1'}, {'r','2'}, {'r','3'}, {'r','4'}, {'r','5'}, {'r','6'}, {'r','7'}, {'r','8'}, {'r','9'}, +{'R','0'}, {'R','1'}, {'R','2'}, {'R','3'}, {'R','4'}, {'R','5'}, {'R','6'}, {'R','7'}, {'R','8'}, {'R','9'}, +{'C','L'}, {'I','D'}, {'O','D'}, {'E','D'}, {'L','G'}, {'S',0}, {'N','S'}}; + +ExpressionItem *FindVariable(char *varname) +{ + ExpressionItem *item = AllocItem(); + // check NSIS variables + for (int i = 0; i < NSIS_VARS_COUNT; i++) + { + if (lstrcmpn(varname, NSISVariablesNames[i],2) == 0) + { + if (i == NSIS_VARS_STACK) item->type = IT_VARIABLE | ITV_STACK; + else if (i == NSIS_VARS_NSTACK) item->type = IT_VARIABLE | ITV_NSTACK; + else + item->type = (IT_VARIABLE | ITV_NSIS) + i; + return item; + } + } + // no.. that's user variable + for (int i = 0; i < UserVarsCount; i++) + { + if (lstrcmp(varname, UserVars[i].name) == 0) + { + // ok. we found user var expression needed + break; + } + } + if (i == UserVarsCount) + { + // new variable + UserVarsCount++; + lstrcpy(UserVars[i].name, varname); + UserVars[i].item = NULL; + } + item->type = (IT_VARIABLE | ITV_USER) + i; + return item; +} + +void PlaceVariable(char *&vb, ParseInfo *pi) +{ + if (vb <= pi->valbuf) return; + *vb = 0; + pi->item = FindVariable(pi->valbuf); + PlaceNewItem(pi); + vb = pi->valbuf; +} + +#define MATHFUNCNUM 29 +const MathFunction MathFunctions[MATHFUNCNUM] = { + {{'s','i','n'}, ITF_MATH1 >> 8, _fsin}, + {{'s','n','h'}, ITF_MATH1 >> 8, _fsinh}, + {{'a','s','n'}, ITF_MATH1 >> 8, _fasin}, + {{'c','o','s'}, ITF_MATH1 >> 8, _fcos}, + {{'c','s','h'}, ITF_MATH1 >> 8, _fcosh}, + {{'a','c','s'}, ITF_MATH1 >> 8, _facos}, + {{'t','a','n'}, ITF_MATH1 >> 8, _ftan}, + {{'t','n','h'}, ITF_MATH1 >> 8, _ftanh}, + {{'a','t','n'}, ITF_MATH1 >> 8, _fatan}, + {{'a','b','s'}, ITF_MATH1 >> 8, _fabs}, + {{'l','n',0}, ITF_MATH1 >> 8, _flog}, + {{'l','o','g'}, ITF_MATH1 >> 8, _flog10}, + {{'e','x','p'}, ITF_MATH1 >> 8, _fexp}, + {{'s','q','t'}, ITF_MATH1 >> 8, _fsqrt}, + {{'c','e','l'}, ITF_MATH1 >> 8, _fceil}, + {{'f','l','r'}, ITF_MATH1 >> 8, _floor}, + + {{'a','t','2'}, ITF_MATH2 >> 8, (Math1FuncPtr)_fatan2}, + {{'p','o','w'}, ITF_MATH2 >> 8, (Math1FuncPtr)_fpow}, + {{'f','m','d'}, ITF_MATH2 >> 8, (Math1FuncPtr)_fmod}, + + // type conversions + {{'i',0,0}, ITF_TYPE >> 8, (Math1FuncPtr)ITC_INT}, + {{'s',0,0}, ITF_TYPE >> 8, (Math1FuncPtr)ITC_STRING}, + {{'f',0,0}, ITF_TYPE >> 8, (Math1FuncPtr)ITC_FLOAT}, + {{'a',0,0}, ITF_TYPE >> 8, (Math1FuncPtr)ITC_ARRAY}, +#define ITFT_CARRAY_ID 23 + {{'c','a',0}, ITF_TYPE >> 8, (Math1FuncPtr)ITC_ARRAY}, + {{'f','f',0}, ITF_TYPE >> 8, (Math1FuncPtr)FTT_FLOATF}, + {{'l',0,0}, ITF_TYPE >> 8, (Math1FuncPtr)FTT_LEN}, + {{'c',0,0}, ITF_TYPE >> 8, (Math1FuncPtr)FTT_CHAR}, + + {{'f','e','x'}, ITF_MATH2 >> 8, (Math1FuncPtr)_frexp}, + {{'m','d','f'}, ITF_MATH2 >> 8, (Math1FuncPtr)_fmodf}, +}; + +void PlaceFunction(char *&vb, char *&sp, ParseInfo *pi) +{ + ExpressionItem *item = pi->item = AllocItem(); + *vb = 0; + + // check BUILTIN variables + for (int i = 0; i < MATHFUNCNUM; i++) + { + if (lstrcmpn(pi->valbuf, MathFunctions[i].name, 3) == 0) + { + item->type = IT_FUNCTION | (MathFunctions[i].type << 8) | i; + // get first argument + sp++; + ParseString(sp, *((ExpressionItem **)(&item->param1)), 0); + if (*sp == ',') + { + // get second argument + sp++; + ParseString(sp, *((ExpressionItem **)(&item->param2)), 0); + } + sp++; vb = pi->valbuf; + PlaceNewItem(pi); + return; + } + } + + // heh, may be it user function + for (int i = 0; i < UserFuncsCount; i++) + { + if (lstrcmp(pi->valbuf, UserFuncs[i].name) == 0) + { + item->type = IT_FUNCTION | ITF_USER | i; + // get arguments list + ExpressionItem **newplace = ((ExpressionItem **)(&pi->item->param1)); + while (*sp != ')') + { + *newplace = AllocItem(); + (*newplace)->type = IT_EXPRESSION; + sp++; + ParseString(sp, *((ExpressionItem **)(&(*newplace)->param1)), 0); + newplace = &((*newplace)->next); + } + sp++; vb = pi->valbuf; + PlaceNewItem(pi); + return; + } + } + + // oops, we need no item for function defenition + CleanupItems(item); + + // it's user function define + int flags = 0; + char buffer[128], *buf = buffer; + UserFunc *f = &UserFuncs[UserFuncsCount++]; + lstrcpy(f->name, pi->valbuf); + f->varflags = 0; + f->varsnum = 0; + do + { + sp++; + switch (*sp) + { + case ' ': + break; + case ',': + case ')': + if (buf > buffer) + { + *buf = 0; + // it should be user variable + ExpressionItem *it = FindVariable(buffer); + f->vars[f->varsnum++] = (it->type) & ITEMOPTIONS; + CleanupItems(it); + buf = buffer; + flags <<= 1; + } + break; + case '&': + flags |= 1; + break; + default: + *(buf++) = *sp; + break; + } + } + while (*sp != ')'); + + // prepare flag for fast analisys + for (int i = 0; i < f->varsnum; i++) + { + f->varflags <<= 1; + flags >>= 1; + f->varflags |= flags&1; + } + + sp++; + // now we are ready to parse function body + ParseString(sp, f->root, PSO_STOPATDELIMETER); + vb = pi->valbuf; + +#ifdef _DEBUG + // dump function (in debug mode) + char place[1024]; + wsprintf(place, "function %s(", f->name); + flags = f->varflags; + for (int i = 0; i < f->varsnum; i++) + { + if (flags&1) lstrcat(place, "&"); + lstrcat(place, UserVars[f->vars[i]].name); + if (i < f->varsnum-1) lstrcat(place, ", "); + flags >>= 1; + } + lstrcat(place, ")"); + PrintTree(f->root, place); +#endif +} + +// parsestring options +#define PSO_STOPATDELIMETER 0x1 + +// operator options +#define PO_UNARYPRE 0x1 // this operator can be uniary pre (--a) for ex +#define PO_UNARYPOST 0x2 // this op can be uniary post (a++) (couldn't be binary) +#define PO_PRENONCONST 0x4 // pre argument (a = b) -> a is non const +#define PO_POSTNONCONST 0x8 // post argument (b--) is non const +#define PO_LASTOP 0x10 // op should be the last item at expression (=, -=, etc) +#define PO_SET 0x20 // op will set new value to one of args +#define PO_USESPRE 0x40 // operator will use pre operand +#define PO_USESPOST 0x80 // operator will use post operan + +void PlaceOp(char *&vb, int type, ParseInfo *pi) +{ + if ((type & PO_UNARYPRE) && ((pi->SetupNewRoot) || (*pi->root == NULL) || (((*pi->root)->type & ITEMTYPE) == IT_OPERATOR))) + { + // uniary pre op + pi->item = AllocItem(); + pi->item->type = type; + pi->item->next = pi->OpsStack; + pi->OpsStack = pi->item; + } + else + { + // post operators + PlaceVariable(vb, pi); + pi->item = AllocItem(); + pi->item->type = type; + pi->item->param1 = (int) (*pi->root); + if (type & PO_UNARYPOST) + { + // uniary post op + PlaceNewItem(pi); + } else + { + // binary operator + pi->item->next = pi->OpsStack; + pi->OpsStack = pi->item; + } + } +} + +#define OPSNUM 35 +const OpStruct Operators[OPSNUM] = +{ +// three byte ops +{">>=", ITO_SHR | PO_PRENONCONST | PO_LASTOP | PO_SET | PO_USESPRE | PO_USESPOST}, +{"<<=", ITO_SHL | PO_PRENONCONST | PO_LASTOP | PO_SET | PO_USESPRE | PO_USESPOST}, + +// two byte ops +{"-=", ITO_MINUS | PO_PRENONCONST | PO_LASTOP | PO_SET | PO_USESPRE | PO_USESPOST}, +{"+=", ITO_PLUS | PO_PRENONCONST | PO_LASTOP | PO_SET | PO_USESPRE | PO_USESPOST}, +{"/=", ITO_DIV | PO_PRENONCONST | PO_LASTOP | PO_SET | PO_USESPRE | PO_USESPOST}, +{"*=", ITO_MUL | PO_PRENONCONST | PO_LASTOP | PO_SET | PO_USESPRE | PO_USESPOST}, +{"|=", ITO_OR | PO_PRENONCONST | PO_LASTOP | PO_SET | PO_USESPRE | PO_USESPOST}, +{"&=", ITO_AND | PO_PRENONCONST | PO_LASTOP | PO_SET | PO_USESPRE | PO_USESPOST}, +{"^=", ITO_XOR | PO_PRENONCONST | PO_LASTOP | PO_SET | PO_USESPRE | PO_USESPOST}, +{"%=", ITO_MOD | PO_PRENONCONST | PO_LASTOP | PO_SET | PO_USESPRE | PO_USESPOST}, +{"--", ITO_DEC | PO_POSTNONCONST | PO_PRENONCONST | PO_UNARYPRE | PO_UNARYPOST | PO_SET | PO_USESPRE | PO_USESPOST}, +{"++", ITO_INC | PO_POSTNONCONST | PO_PRENONCONST | PO_UNARYPRE | PO_UNARYPOST | PO_SET | PO_USESPRE | PO_USESPOST}, +{">>", ITO_SHR | PO_USESPRE | PO_USESPOST}, +{"<<", ITO_SHL | PO_USESPRE | PO_USESPOST}, + +// logical +{"&&", ITO_LAND | PO_USESPRE | PO_USESPOST}, +{"||", ITO_LOR | PO_USESPRE | PO_USESPOST}, + +// comparisons +{"<=", ITO_LE | PO_USESPRE | PO_USESPOST}, +{"=<", ITO_LE | PO_USESPRE | PO_USESPOST}, +{">=", ITO_GE | PO_USESPRE | PO_USESPOST}, +{"=>", ITO_GE | PO_USESPRE | PO_USESPOST}, +{"!=", ITO_NE | PO_USESPRE | PO_USESPOST}, +{"==", ITO_EQ | PO_USESPRE | PO_USESPOST}, + +// single byte ops +{"=", ITO_SET | PO_PRENONCONST | PO_LASTOP | PO_SET | PO_USESPOST}, +{"+", ITO_PLUS | PO_USESPRE | PO_USESPOST}, +{"-", ITO_MINUS | PO_USESPRE | PO_USESPOST}, +{"*", ITO_MUL | PO_USESPRE | PO_USESPOST | PO_UNARYPRE}, +{"/", ITO_DIV | PO_USESPRE | PO_USESPOST}, +{"%", ITO_MOD | PO_USESPRE | PO_USESPOST}, +{"<", ITO_LS | PO_USESPRE | PO_USESPOST}, +{">", ITO_GR | PO_USESPRE | PO_USESPOST}, +{"&", ITO_AND | PO_USESPRE | PO_USESPOST | PO_UNARYPRE}, +{"|", ITO_OR | PO_USESPRE | PO_USESPOST}, +{"^", ITO_XOR | PO_USESPRE | PO_USESPOST}, +{"~", ITO_NOT | PO_USESPOST | PO_UNARYPRE}, +{"!", ITO_LNOT |PO_USESPOST | PO_UNARYPRE} +}; + +void CheckForOperator(char *&sp, char *&vb, ParseInfo *pi) +{ + for (int op = 0; op < OPSNUM; op++) + { + int c = 0; + while ((Operators[op].name[c]) && (*(sp+c) == Operators[op].name[c])) c++; + if (Operators[op].name[c]) + { + // wrong - different op + continue; + } + // that is our op + sp += c; + PlaceOp(vb, ((int) Operators[op].type) | IT_OPERATOR, pi); + if (Operators[op].type & PO_LASTOP) + { + // this op should be last in a set of items + pi->item = NULL; + ParseString(sp, pi->item, PSO_STOPATDELIMETER); + PlaceNewItem(pi); + } + break; + } +} + +void ParseString(char *&sp, ExpressionItem* &itemplace, int options) +{ + ParseInfo pi = {0, NULL, NULL, itemplace, &itemplace}; + + char *vb = pi.valbuf; + // cycle until current expression end + while ((*sp != 0) && (*sp != ')') && (*sp != '}') && + (*sp != ']') && (*sp != ',')) + { + int processed = 1; + switch (*sp) + { + case ' ': + case '\t': + sp++; + break; + case ';': + // expression delimeter + PlaceVariable(vb, &pi); + pi.SetupNewRoot = 1; + // check stop at delimeter option + if (options & PSO_STOPATDELIMETER) return; + sp++; + break; + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + // variable & function names could contain numbers as non first chars + if (vb > pi.valbuf) + { + processed = FALSE; + break; + } + case '\'': case '\"': case '`': + // constant meet + pi.item = AllocItem(); + StringToItem(sp, pi.item, STI_STRING | STI_FLOAT | STI_INT); + PlaceNewItem(&pi); + break; + + case '(': // start of function or expression + if (vb > pi.valbuf) + { + // thats function + PlaceFunction(vb, sp, &pi); + } else + { + // expression + sp++; + ParseString(sp, pi.item, 0); + PlaceNewItem(&pi); + if (*sp == ')') sp++; + } + break; + + case '#': // start of one of logical expresions + sp++; + { + pi.item = AllocItem(); + // IF or WHILE + pi.item->type = ((*sp == '[')?(IT_LOGIC | ITL_IF):(IT_LOGIC | ITL_WHILE)); + // first expr - logic statement + sp++; + + ParseString(sp, *((ExpressionItem **)(&pi.item->param1)), 0); + // ok, second expr - then, third - else statement.. others??? + ExpressionItem **newplace = ((ExpressionItem **)(&pi.item->param2)); + while (*sp == ',') + { + *newplace = AllocItem(); + (*newplace)->type = IT_EXPRESSION; + sp++; + ParseString(sp, *((ExpressionItem **)(&(*newplace)->param1)), 0); + newplace = &((*newplace)->next); + } + } + PlaceNewItem(&pi); + sp++; + break; + + case '[': + // thats array access + PlaceOp(vb, IT_ARRAY | ITA_ACCESS, &pi); + sp++; + // item index + pi.item = NULL; + ParseString(sp, pi.item, 0); + if (*sp == ',') + { + // two indexes - string access + ExpressionItem *it = AllocItem(); + it->type = IT_EXPRESSION; + it->param1 = (int) pi.item; + pi.item = it; + it = it->next = AllocItem(); + it->type = IT_EXPRESSION; + sp++; + ParseString(sp, *((ExpressionItem **)(&it->param1)), 0); + } + PlaceNewItem(&pi); + sp++; + break; + + case '{': // start of array define + { + // array define - constists of array copy operator and array define itself + + // type conversion item (to create a copy of array) + pi.item = AllocItem(); + pi.item->type = IT_FUNCTION | ITF_TYPE | ITFT_CARRAY_ID | ITFA_COPY; + + // during first create our array descriptor and array pointers + ExpressionItem *ai = AllocArray(DEFAULT_ARRAY_SIZE); + pi.item->param1 = (int) ai; + ArrayDesc *ad = *((ArrayDesc**)&(ai->param1)); + + // parse array initializers + while (*sp != '}') + { + sp++; + ParseString(sp, ad->array[ad->count++], 0); + } + + PlaceNewItem(&pi); + sp++; + } + break; + case '-': case '+': case '<': case '=': case '>': + case '/': case '*': case '~': case '^': case '!': + case '&': case '|': case '%': + PlaceVariable(vb, &pi); + CheckForOperator(sp, vb, &pi); + break; + + // non expression? ok, then it should be common char, like function or var name + default: + processed = FALSE; + break; + } + if (!processed) *(vb++) = *(sp++); + } + PlaceVariable(vb, &pi); +} + +void CleanupArray(ArrayDesc *ad) +{ + if (!(--(ad->references))) + { + // last array reference, we could kill it + // cleanup array items + for (int i = 0; i < ad->count; i++) + { + ExpressionItem *aritem = ad->array[i]; + if (aritem) + CleanupItems(aritem); + } + // cleanup ptrs and descriptor + dbgGlobalFree(ad->array); + dbgGlobalFree(ad); + } + } + +void CleanupItems(ExpressionItem* &itemplace) +{ + if (itemplace == NULL) return; + ExpressionItem *item = itemplace, *itemnext; + do + { + if (((item->type & (ITEMTYPE|ITEMSUBTYPE)) == (IT_VARIABLE|ITV_ARRITEM)) + || + ((item->type & (ITEMTYPE|ITEMSUBTYPE)) == (IT_CONST|ITC_ARRAY))) + { + CleanupArray((ArrayDesc *)item->param1); + } + else + if ((item->type & ITEMTYPE) == IT_CONST) + { + if ((item->type & ITEMSUBTYPE) == ITC_STRING) + dbgGlobalFree((HGLOBAL) item->param1); + } else + { + CleanupItems(*((ExpressionItem**) &item->param1)); + CleanupItems(*((ExpressionItem**) &item->param2)); + } + // free the item itself + itemnext = item->next; + dbgGlobalFree((HGLOBAL) item); + item = itemnext; + } while (item != NULL); + itemplace = NULL; +} + +#ifdef _DEBUG +HANDLE myfile; + +char *opsnames[] = {"", "-", "+", "<<", ">>", "*", "/", "=", "&&", "||", "++", "--", +"=<", ">=", "!=", "==", "<", ">", "&", "%", "|", "^", "~", "!"}; + +void PrintNode(int index, int spaces, ExpressionItem* itemplace) +{ + if (itemplace == NULL) return; + + ExpressionItem *item = itemplace; + do + { + DWORD wrote; + char buffer[1024], *place = buffer; + for (int k = 0; k < spaces; k++) + *(place++) = 32; + *place = 0; + + switch (item->type & ITEMTYPE) + { + case IT_EXPRESSION: + wsprintf(place, "Expression Place-Holder "); + break; + case IT_CONST: + switch (item->type & ITEMSUBTYPE) + { + case ITC_STRING: + wsprintf(place, "String: \"%s\"", (char *) item->param1); + break; + case ITC_INT: + wsprintf(place, "Int: "); + itoa64(*((__int64*)&(item->param1)), place+5); + break; + case ITC_FLOAT: + wsprintf(place, "Float: "); + FloatFormat(place+7, *((double*)&(item->param1)), 6); + break; + case ITC_ARRAY: + ArrayDesc *ad = (ArrayDesc*) item->param1; + wsprintf(place, "Array, ptr %08X, size %d, count %d, references %d", + ad->array, ad->size, ad->count, ad->references); + break; + } + strcat(place, " "); + break; + case IT_OPERATOR: + wsprintf(place, "Op: %s%s ", opsnames[(item->type & ITEMSUBTYPE) >> 8], (item->type & PO_SET)?("(=)"):("")); + break; + case IT_VARIABLE: + switch (item->type & ITEMSUBTYPE) + { + case ITV_NSIS: + { + char buffer[128]; + buffer[0] = NSISVariablesNames[item->type & ITEMOPTIONS][0]; + buffer[1] = NSISVariablesNames[item->type & ITEMOPTIONS][1]; + buffer[2] = 0; + wsprintf(place, "Var: %s (%d) ", + buffer, + item->type & ITEMOPTIONS); + } + break; + case ITV_USER: + wsprintf(place, "Var: %s (%d) ", UserVars[item->type & ITEMOPTIONS].name, item->type & ITEMOPTIONS); + break; + case ITV_STACK: + wsprintf(place, "Plugin Stack "); + break; + case ITV_NSTACK: + wsprintf(place, "NSIS Stack "); + break; + } + break; + case IT_LOGIC: + if ((item->type & ITEMSUBTYPE) == ITL_IF) + wsprintf(place, "IF "); + else + wsprintf(place, "WHILE "); + break; + case IT_FUNCTION: + if (((item->type & ITEMSUBTYPE) == ITF_MATH1) || + ((item->type & ITEMSUBTYPE) == ITF_MATH2) || + ((item->type & ITEMSUBTYPE) == ITF_TYPE)) + { + char buffer[128]; + buffer[0] = (MathFunctions[item->type &ITEMOPTIONS].name)[0]; + buffer[1] = (MathFunctions[item->type &ITEMOPTIONS].name)[1]; + buffer[2] = (MathFunctions[item->type &ITEMOPTIONS].name)[2]; + buffer[3] = 0; + wsprintf(place, "Built-In Function %s() [%d] ", + buffer, + item->type &ITEMOPTIONS); + } + else + { + UserFunc *f = &(UserFuncs[item->type & ITEMOPTIONS]); + wsprintf(place, "User Function: %s(", f->name); + int flags = f->varflags; + for (int i = 0; i < f->varsnum; i++) + { + if (flags&1) lstrcat(place, "&"); + lstrcat(place, UserVars[f->vars[i]].name); + if (i < f->varsnum-1) lstrcat(place, ", "); + flags >>= 1; + } + lstrcat(place, ") "); + } + break; + case IT_ARRAY: + wsprintf(place, "Array access [] "); + break; + } + place += lstrlen(place); + wsprintf(place, "Addr: %08X Type: %08X Next: %08X Param1: %08X Param2: %08X", item, item->type, item->next, item->param1, item->param2); + lstrcat(place, "\n"); + WriteFile(myfile, buffer, lstrlen(buffer), &wrote, NULL); + if (((item->type & ITEMTYPE) != IT_CONST) && ((item->type & (ITEMTYPE|ITEMSUBTYPE)) != (IT_VARIABLE|ITV_ARRITEM))) + { + place = buffer; + for (int k = 0; k < spaces+2; k++) + *(place++) = 32; + int show = 0; + if (((item->param1 != NULL) && ((*((ExpressionItem**) &item->param1))->next != NULL)) || + ((item->param2 != NULL) && ((*((ExpressionItem**) &item->param2))->next != NULL))) + show = 1; + if (show) + { + wsprintf(place, "Sub1:\n"); + WriteFile(myfile, buffer, lstrlen(buffer), &wrote, NULL); + } + PrintNode(1, spaces + 4, *((ExpressionItem**) &item->param1)); + if (show) + { + wsprintf(place, "Sub2:\n"); + WriteFile(myfile, buffer, lstrlen(buffer), &wrote, NULL); + } + PrintNode(2, spaces + 4, *((ExpressionItem**) &item->param2)); + } else if ((item->type & (ITEMSUBTYPE|ITEMTYPE)) == (ITC_ARRAY|IT_CONST)) + { + ArrayDesc *ad = (ArrayDesc *) item->param1; + for (int i = 0; i < ad->count; i++) + { + ExpressionItem *aritem = ad->array[i]; + if (aritem) + PrintNode(2, spaces + 4, aritem); + } + } + item = item->next; + } while (item != NULL); +} + +void PrintTree(ExpressionItem *root, char *str) +{ + myfile = CreateFile("d:\\math.debug", GENERIC_ALL, FILE_SHARE_READ, NULL, OPEN_ALWAYS, 0, 0); + SetFilePointer(myfile, 0, NULL, FILE_END); + char buffer[1024]; DWORD wrote; + wsprintf(buffer, "New tree for \'%s\'\n", str); + WriteFile(myfile, buffer, lstrlen(buffer), &wrote, NULL); + + PrintNode(0, 4, root); + CloseHandle(myfile); + myfile = NULL; +} +#endif + +void CopyArray(ExpressionItem *&item) +{ + if (item == NULL) return; + // especial case - array to array conversion is requested array copy + ExpressionItem *olditem = item; + ArrayDesc *oad = (ArrayDesc *) (olditem->param1); + // Initialize the array of the same size + item = AllocArray(oad->size); + ArrayDesc *nad = (ArrayDesc *) (item->param1); + nad->count = oad->count; + // copy items + for (int i = 0; i < oad->count; i++) + nad->array[i] = CopyItem(oad->array[i], TRUE); + // cleanup old array pointer (may be array itself) + CleanupItems(olditem); +} + +void ItemToType(ExpressionItem* &item, int type) +{ + char *buffer; + if (item == NULL) return; + int itemt = item->type & ITEMTYPE, oldtype = item->type & ITEMSUBTYPE; + + if (((itemt == IT_CONST) && (oldtype == type)) + || (itemt != IT_CONST)) return; + + switch (type) + { + case ITC_STRING: + buffer = AllocString(); + ItemToString(buffer, item); + item->param1 = (int) buffer; + item->param2 = 0; + break; + case ITC_FLOAT: + if (oldtype == ITC_INT) + *((double *)&(item->param1)) = (double) *((__int64 *)&(item->param1)); + else + { + buffer = (char*) item->param1; + StringToItem(buffer, item, STI_FLOAT); + dbgGlobalFree(buffer); + } + break; + case ITC_INT: + if (oldtype == ITC_FLOAT) + *((__int64 *)&(item->param1)) = (__int64) *((double *)&(item->param1)); + else + { + buffer = (char*) item->param1; + StringToItem(buffer, item, STI_INT); + dbgGlobalFree(buffer); + } + break; + case ITC_ARRAY: + if (oldtype == ITC_STRING) + { + char *buf = (char*) item->param1; + int len = lstrlen(buf)+1; + ExpressionItem *ni = AllocArray(lstrlen(buf)+1); + ArrayDesc *ad = (ArrayDesc*) ni->param1; + for (int i = 0; i < len; i++) + { + ad->array[i] = AllocItem(); + *((__int64 *) &(ad->array[i]->param1)) = (__int64) buf[i]; + } + ad->count = len; + CleanupItems(item); + item = ni; + } + break; + } + item->type = IT_CONST | type; +} + +void SaveResult(ExpressionItem *var, ExpressionItem *result) +{ + if ((var->type & ITEMTYPE) != IT_VARIABLE) return; + + // result should be stored at variable to + int varindex = var->type & ITEMOPTIONS; + switch (var->type & ITEMSUBTYPE) + { + case ITV_NSIS: + { + // store string result direct to NSIS variable + char *ptr = g_variables + varindex*g_stringsize; + ItemToString(ptr, result); + } + break; + case ITV_USER: + { + CleanupItems(UserVars[varindex].item); + UserVars[varindex].item = CopyItem(result); + } + break; + case ITV_ARRITEM: + { + ExpressionItem *&ei = ((ArrayDesc*)(var->param1))->array[var->param2]; + CleanupItems(ei); + ei = CopyItem(result); + } + break; + case ITV_STACK: + { + ExpressionItem *newitem = CopyItem(result); + newitem->next = stack; + stack = newitem; + } + break; + case ITV_NSTACK: + { + char *buf = AllocString(); + ItemToString(buf, result); + pushstring(buf); + dbgGlobalFree(buf); + } + break; + } +} + +void RunAndGetConst(int from, ExpressionItem* &result, int type) +{ + RunTree(*((ExpressionItem**)&(from)), result, type | RTO_NEEDCONST); + ItemToType(result, type); +} + +void RunTree(ExpressionItem *from, ExpressionItem* &result, int options) +{ + ExpressionItem *item = from; + result = NULL; + while (item != NULL) + { + CleanupItems(result); + int type = item->type & ITEMTYPE, + subtype = item->type & ITEMSUBTYPE, + ioptions = item->type & ITEMOPTIONS; + switch (type) + { + case IT_EXPRESSION: + RunTree(*((ExpressionItem**)&(item->param1)), result, options); + break; + case IT_CONST: + result = CopyItem(item); + break; + case IT_VARIABLE: + if (options & RTO_NEEDCONST) + { + // we need const result - :) is it nsis or common variable + switch (subtype) + { + case ITV_NSIS: + { + // nsis + result = AllocItem(); + char *variable = getuservariable(ioptions); + StringToItem(variable, result, options); + } + break; + case ITV_USER: + { + // usual variable + if (UserVars[ioptions].item) + result = CopyItem(UserVars[ioptions].item); + else + result = AllocItem(); + } + break; + case ITV_ARRITEM: + { + // array item + ExpressionItem *ei = ((ArrayDesc*)(item->param1))->array[item->param2]; + if (ei) + result = CopyItem(ei); + else + result = AllocItem(); + } + break; + case ITV_STACK: + { + // pop from plugin stack + result = stack; + if (result == NULL) result = AllocItem(); + stack = result->next; + result->next = NULL; + } + break; + case ITV_NSTACK: + { + // NSIS stack + char buffer[1024], *buf = buffer; + result = AllocItem(); + popstring(buffer); + StringToItem(buf, result, options); + } + break; + } + } + else + // if we don't need const - we will just pass variable record + result = CopyItem(item); + break; + case IT_OPERATOR: + { + ExpressionItem *var = NULL, *item1 = NULL, *item2 = NULL; + // prepare arguments in case of SET operator + if (ioptions & PO_SET) + { + if ((item->param1) && (ioptions & PO_PRENONCONST)) + { + RunTree(*((ExpressionItem**)&(item->param1)), var, 0); + if (ioptions & PO_USESPRE) + RunTree(var, item1, RTO_NEEDCONST | STI_INT | STI_FLOAT | STI_STRING); + } else + if ((item->param2) && (ioptions & PO_POSTNONCONST)) + { + RunTree(*((ExpressionItem**)&(item->param2)), var, 0); + if (ioptions & PO_USESPOST) + RunTree(var, item2, RTO_NEEDCONST | STI_INT | STI_FLOAT | STI_STRING); + } + } + + // prepare arguments in case of any operator + int needmore = 1; + if ((!item1) && (item->param1) && (ioptions & PO_USESPRE)) + { + RunTree(*((ExpressionItem**)&(item->param1)), item1, RTO_NEEDCONST | STI_INT | STI_FLOAT | STI_STRING); + // logical expressions && and || can make decision on first arg basis + if ((subtype == ITO_LAND) || (subtype == ITO_LOR) ) + { + ItemToType(item1, ITC_INT); + int res = (int) *((__int64*) &(item1->param1)); + if (((res)&&(subtype==ITO_LOR)) || ((!res)&&(subtype==ITO_LAND))) + needmore = 0; + } + } + + if ((needmore) && (!item2) && (item->param2) && (ioptions & PO_USESPOST)) + RunTree(*((ExpressionItem**)&(item->param2)), item2, RTO_NEEDCONST | STI_INT | STI_FLOAT | STI_STRING); + + // reference operator + if ((!item1) && (subtype == ITO_MUL) && ((item2->type & ITEMTYPE) == (IT_VARIABLE))) + { + // ok, that's the result we need + if (options & RTO_NEEDCONST) + { + RunTree(item2, result, options); + CleanupItems(item2); + } else + result = item2; + break; + } + + // find the best type match for operation + int it1 = (item1 && (ioptions & PO_USESPRE))?(item1->type & ITEMSUBTYPE):(ITC_UNKNOWN), + it2 = (item2 && (ioptions & PO_USESPOST))?(item2->type & ITEMSUBTYPE):(ITC_UNKNOWN), + type = (it1 < it2)?(it1):(it2); + + // convert operands to desired type + ItemToType(item1, type); + ItemToType(item2, type); + + __int64 i1, i2, i3, i4; + switch (type) + { + case ITC_INT: + { + i1 = (item1)?(*((__int64*)&item1->param1)):(0); + i2 = (item2)?(*((__int64*)&item2->param1)):(0); + + switch (subtype) + { + case ITO_MINUS: i1 -= i2; break; // unary minus auto handled with NULL + case ITO_PLUS: i1 += i2; break; + case ITO_SHL: i1 <<= i2; break; + case ITO_SHR: i1 >>= i2; break; + case ITO_MUL: i1 *= i2; break; + case ITO_MOD: + case ITO_DIV: + if (i2 == 0) { i3 = 0; i4 = i1; } + else { i3 = i1 / i2; i4 = i1 % i2; } + if (subtype == ITO_DIV) i1 = i3; else i1 = i4; + break; + case ITO_SET: i1 = i2; break; + case ITO_LE: i1 = (i1 <= i2); break; + case ITO_GE: i1 = (i1 >= i2); break; + case ITO_NE: i1 = (i1 != i2); break; + case ITO_EQ: i1 = (i1 == i2); break; + case ITO_LS: i1 = (i1 < i2); break; + case ITO_GR: i1 = (i1 > i2); break; + case ITO_AND: i1 = (i1 & i2); break; + case ITO_OR: i1 = (i1 | i2); break; + case ITO_XOR: i1 = (i1 ^ i2); break; + case ITO_NOT: i1 = ~i2; break; + case ITO_LNOT: i1 = !i2; break; + case ITO_LAND: i1 = i1 && i2; break; + case ITO_LOR: i1 = i1 || i2; break; + case ITO_INC: + if (item1) i2 = i1++; + else i1 = ++i2; + break; + case ITO_DEC: + if (item1) i2 = i1--; + else i1 = --i2; + break; + } + result = AllocItem(); + *((__int64*)&result->param1) = i1; + } + break; + case ITC_FLOAT: + { + int ir = -666; + double i1 = (item1)?(*((double*)&item1->param1)):(0.0); + double i2 = (item2)?(*((double*)&item2->param1)):(0.0); + + switch (subtype) + { + case ITO_MINUS: i1 -= i2; break; // unary minus auto handled with NULL + case ITO_PLUS: i1 += i2; break; + case ITO_MUL: i1 *= i2; break; + case ITO_DIV: i1 /= i2; break; + case ITO_SET: i1 = i2; break; + case ITO_LE: ir = (i1 <= i2); break; + case ITO_GE: ir = (i1 >= i2); break; + case ITO_NE: ir = (i1 != i2); break; + case ITO_EQ: ir = (i1 == i2); break; + case ITO_LS: ir = (i1 < i2); break; + case ITO_GR: ir = (i1 > i2); break; + } + result = AllocItem(); + if (ir == -666) + { + // if ir value left intact - result is double + result->type = IT_CONST | ITC_FLOAT; + *((double*)&result->param1) = i1; + } else + *((__int64*)&result->param1) = (__int64) ir; + } + break; + case ITC_STRING: + { + int ir = -666; + char *i1 = (item1)?((char*)item1->param1):(NULL); + char *i2 = (item2)?((char*)item2->param1):(NULL); + int sc = lstrcmp(i1, i2); + + switch (subtype) + { + case ITO_PLUS: lstrcat(i1, i2); break; + case ITO_SET: i1 = i2; break; + case ITO_LE: ir = (sc <= 0); break; + case ITO_GE: ir = (sc >= 0); break; + case ITO_NE: ir = (sc != 0); break; + case ITO_EQ: ir = (sc == 0); break; + case ITO_LS: ir = (sc < 0); break; + case ITO_GR: ir = (sc > 0); break; + } + if (ir == -666) + { + result = CopyItem((item1)?(item1):(item2)); + } else + { + result = AllocItem(); + *((__int64*)&result->param1) = (__int64) ir; + } + } + break; + case ITC_ARRAY: + result = CopyItem(item2); + break; + } + + if (ioptions & PO_SET) + { + // Save our result in output variable + SaveResult(var, result); + } + + // Actual value to be returned as result is at i2 for ++ and -- ops + if ((subtype == ITO_DEC) || (subtype == ITO_INC)) + *((__int64*)&result->param1) = i2; + + CleanupItems(item1); CleanupItems(item2); CleanupItems(var); + } + break; + case IT_LOGIC: + { + int ifmode = (subtype == ITL_IF); + ExpressionItem *ifbr = *((ExpressionItem**)&(item->param1)), + *dobr = *((ExpressionItem**)&(item->param2)), + *thbr = NULL, *elbr = NULL; + // check do branche for existance + if (dobr && ifmode) + { + // then... + thbr = *((ExpressionItem**)&(dobr->param1)); + // ... and else branches + if (dobr->next) elbr = *((ExpressionItem**)&(dobr->next->param1)); + } + while (true) + { + RunAndGetConst((int) ifbr, result, ITC_INT); + if (ifmode) + { + // we need then or else branch? + if ((*((__int64*)&result->param1))) dobr = thbr; + else dobr = elbr; + } else + { + // while mode + if (!(*((__int64*)&result->param1))) break; + } + // ok, run the approtiate branch of if statement (if available) + if (dobr) + { + CleanupItems(result); + RunTree(dobr, result, options); + } + if (ifmode) break; + CleanupItems(result); + } + } + break; + case IT_FUNCTION: + if (subtype == ITF_USER) + { + UserFunc *f = &(UserFuncs[ioptions]); + int flags = f->varflags; + ExpressionItem *ip = *((ExpressionItem**)&(item->param1)); + ExpressionItem *si = AllocItem(), *var = AllocItem(); + ExpressionItem *vals[32]; + si->type = IT_VARIABLE | ITV_STACK; + for (int i = 0; i < f->varsnum; i++) + { + // push every variable + ExpressionItem *val; + var->type = (IT_VARIABLE | ITV_USER) + f->vars[i]; + RunTree(var, val, RTO_NEEDCONST | ITC_STRING | ITC_INT | ITC_FLOAT | ITC_ARRAY | ITC_VARPTR); + SaveResult(si, val); + CleanupItems(val); + // calculate argument value and for future + if (ip) + { + if (flags&1) + { + // var ptr required + RunTree(*((ExpressionItem**)&(ip->param1)), vals[i], 0); + } else + { + RunTree(*((ExpressionItem**)&(ip->param1)), vals[i], RTO_NEEDCONST | ITC_STRING | ITC_INT | ITC_FLOAT | ITC_ARRAY | ITC_VARPTR); + } + ip = ip->next; + } else vals[i] = AllocItem(); + + flags >>= 1; + } + + // now when all values got we could save them to variables + for (int i = 0; i < f->varsnum; i++) + { + var->type = (IT_VARIABLE | ITV_USER) + f->vars[i]; + SaveResult(var, vals[i]); + CleanupItems(vals[i]); + } + + + // ok, call the func + RunTree(f->root, result, RTO_NEEDCONST | ITC_STRING | ITC_INT | ITC_FLOAT | ITC_ARRAY | ITC_VARPTR); + + // pop original params + for (int i = f->varsnum-1; i >= 0; i--) + { + // pop every variable + ExpressionItem *val; + var->type = (IT_VARIABLE | ITV_USER) + f->vars[i]; + RunTree(si, val, RTO_NEEDCONST | ITC_STRING | ITC_INT | ITC_FLOAT | ITC_ARRAY | ITC_VARPTR); + SaveResult(var, val); + CleanupItems(val); + } + + // free used items + CleanupItems(si); CleanupItems(var); + } else if (subtype == ITF_TYPE) + { + int newtype = (int) MathFunctions[ioptions].fptr; + if (newtype < ITC_UNKNOWN) + { + // get as possibly close to ready expression + RunAndGetConst(item->param1, result, newtype); + if (ioptions == ITFT_CARRAY_ID) + CopyArray(result); + } else if (newtype == FTT_FLOATF) + { + // float format function + ExpressionItem *arg1, *arg2; + RunAndGetConst(item->param1, arg1, ITC_FLOAT); + double value = *((double*)&(arg1->param1)); + RunAndGetConst(item->param2, arg2, ITC_INT); + int format = (int) *((__int64*)&(arg2->param1)); + + result = AllocItem(); + result->type = IT_CONST | ITC_STRING; + result->param1 = (int) AllocString(); + FloatFormat((char*) result->param1, value, format); + CleanupItems(arg1); CleanupItems(arg2); + } else if (newtype == FTT_LEN) + { + // length function + RunTree(*((ExpressionItem **) &(item->param1)), result, RTO_NEEDCONST | ITC_STRING | ITC_ARRAY); + if ((result->type & (ITEMTYPE|ITEMSUBTYPE)) == (IT_CONST|ITC_ARRAY)) + { + int len = ((ArrayDesc*)(result->param1))->count; + CleanupItems(result); + result = AllocItem(); + *((__int64*)&(result->param1)) = (__int64) len; + break; + } else + if ((result->type & (ITEMTYPE|ITEMSUBTYPE)) != (IT_CONST|ITC_STRING)) + ItemToType(result, ITC_STRING); + + if ((result->type & (ITEMTYPE|ITEMSUBTYPE)) == (IT_CONST|ITC_STRING)) + { + // ok, that's string + int len = lstrlen((char*)result->param1); + dbgGlobalFree((HGLOBAL) result->param1); + *((__int64*)&(result->param1)) = (__int64) len; + result->type = IT_CONST | ITC_INT; + } else CleanupItems(result); + } else + { + // only one left - c() - char/int/char conversion + RunTree(*((ExpressionItem **) &(item->param1)), result, RTO_NEEDCONST | ITC_STRING | ITC_INT); + if ((result->type & (ITEMTYPE|ITEMSUBTYPE)) == (IT_CONST|ITC_STRING)) + { + // ok, that's string - convert first char to int + int chr = (*((char*)result->param1)) & 0xFF; + dbgGlobalFree((HGLOBAL) result->param1); + *((__int64*)&(result->param1)) = (__int64) chr; + result->type = IT_CONST | ITC_INT; + break; + } + if ((result->type & (ITEMTYPE|ITEMSUBTYPE)) == (IT_CONST|ITC_INT)) + { + // ok, that's int - convert to new string (char+0) + int chr = (int) (*((__int64*)&(result->param1))) & 0xFF; + result->param1 = (int) AllocString(); + *((char*)result->param1) = (char) chr; + *((char*)(result->param1+1)) = (char) 0; + result->type = IT_CONST | ITC_STRING; + break; + } else CleanupItems(result); + } + } else + { + // oops :-o function call :) + RunAndGetConst(item->param1, result, ITC_FLOAT); + double &value = *((double*)&(result->param1)); + if (subtype == ITF_MATH1) + { + // Built-In math function with 1 arg + value = MathFunctions[ioptions].fptr(value); + } else + if (subtype == ITF_MATH2) + { + // Built-In math function with 2 args + if (ioptions >= MATHFUNCNUM-2) + { + // specific function - we need second arg as out + ExpressionItem *arg2, *res2 = AllocItem(); + RunTree(*((ExpressionItem**)&(item->param2)), arg2, 0); + if (ioptions == MATHFUNCNUM-1) + { + // fmodf function - second arg is ptr to double + res2->type = IT_CONST | ITC_FLOAT; + double &v = *((double*)&(res2->param1)); + value = ((Math2dFuncPtr)(MathFunctions[ioptions].fptr))(value, &v); + } else + { + // frexp function - second arg is ptr to int + int v = 0; + value = ((Math2iFuncPtr)(MathFunctions[ioptions].fptr))(value, &v); + *((__int64 *)&(res2->param1)) = (__int64) v; + } + SaveResult(arg2, res2); + CleanupItems(arg2); CleanupItems(res2); + } else + { + // normal 2-arg math function + ExpressionItem *arg2; + RunAndGetConst(item->param2, arg2, ITC_FLOAT); + double value2 = *((double*)&(arg2->param1)); + value = ((Math2FuncPtr)(MathFunctions[ioptions].fptr))(value, value2); + CleanupItems(arg2); + } + } + } + break; + case IT_ARRAY: + { + // currently only array access is used + ExpressionItem *index, *aritem; + RunTree(*((ExpressionItem **) &(item->param1)), aritem, RTO_NEEDCONST | ITC_STRING | ITC_ARRAY); + + if ((aritem->type & (ITEMTYPE|ITEMSUBTYPE)) == (IT_CONST | ITC_STRING)) + { + // argument is string + char *str = (char*)(aritem->param1); + int len = lstrlen(str); + // have we two indexes or one? + if ((*((ExpressionItem **) &(item->param2)))->type != IT_EXPRESSION) + { + // one index - user need a char + RunAndGetConst(item->param2, index, ITC_INT); + + int pos = (int) *((__int64*)&(index->param1)); + if (pos < 0) pos += len; // -index - means from end + if ((pos > len) || (pos < 0)) + *str = 0; // index is accross string boundaries + else + { + // new string - just a single char + *str = *(str+pos); + *(str+1) = 0; + } + } else + { + // two indexes + ExpressionItem *index2; + // if first index is skipped -> 0 (from first char) + if ((*((ExpressionItem **) &(item->param2)))->param1 == 0) + index = AllocItem(); + else + RunAndGetConst((*((ExpressionItem **) &(item->param2)))->param1, index, ITC_INT); + if ((*((ExpressionItem **) &(item->param2)))->next->param1 == 0) + { + // if second index is skipped -> -1 (till last char) + index2 = AllocItem(); + *((__int64*)&(index2->param1)) = -1; + } + else + RunAndGetConst((*((ExpressionItem **) &(item->param2)))->next->param1, index2, ITC_INT); + + // ok, we've got two indexes + int pos1 = (int) *((__int64*)&(index->param1)); + int pos2 = (int) *((__int64*)&(index2->param1)); + if (pos1 < 0) pos1 += len; // -index - means from end + if (pos2 < 0) pos2 += len; // -index - means from end + // limit start/stop positions + if (pos1 < 0) pos1 = 0; + if (pos2 < 0) pos2 = 0; + if (pos1 > len) pos1 = len; + if (pos2 >= len) pos2 = len-1; + + // copy string part + char* lpos = str + (pos2-pos1); + while (str <= lpos) + { + *str = *(str + pos1); + str++; + } + // null-termiante + *str = 0; + + CleanupItems(index2); + } + + } else + { + // argument is array + RunAndGetConst(item->param2, index, ITC_INT); + + // convert array pointer to array item pointer + aritem->type = IT_VARIABLE | ITV_ARRITEM; + aritem->param2 = (int) *((__int64*)&(index->param1)); + + ArrayDesc *ad = (ArrayDesc*)aritem->param1; + if (aritem->param2 > ad->count) + { + ad->count = aritem->param2+1; + while (ad->count > ad->size) + { + // resize array + ExpressionItem **oldei = ad->array; + ad->array = (ExpressionItem**) dbgGlobalAlloc(GPTR, 2*ad->size*sizeof(ExpressionItem*)); + for (int i = 0; i < ad->size; i++) + ad->array[i] = oldei[i]; + ad->size*=2; + dbgGlobalFree(oldei); + } + } + } + + CleanupItems(index); + + // we need constant result? + if (options & RTO_NEEDCONST) + { + RunTree(aritem, result, options); + CleanupItems(aritem); + } else result = aritem; + } + break; + } + item = item->next; + } +} + +extern "C" +void __declspec(dllexport) Script(HWND hwndParent, int string_size, + char *variables, stack_t **stacktop) +{ + Math_INIT(); + char *buffer = AllocString(), *buf = buffer; + ExpressionItem *root = NULL; // root of current tree + + // pop script string + popstring(buffer); + + // parse it + ParseString(buf, root, 0); + +#ifdef _DEBUG + // dump + PrintTree(root, buffer); +#endif + + ExpressionItem *result; + RunTree(root, result, 0); + CleanupItems(result); + + CleanupItems(root); + dbgGlobalFree((HGLOBAL) buffer); +} + +double _infinity; + +void CleanAll(int init) +{ + if (init) + { + unsigned char _infinity_base[8] = {0, 0, 0, 0, 0, 0, 0xf0, 0x7f}; + _fltused = 0; + _infinity = *((double*)(_infinity_base)); + _fpreset(); + + stack = NULL; + UserVarsCount = 0; + UserFuncsCount = 0; + } else + { + // cleanup stack + CleanupItems(stack); stack = NULL; + // cleanup user vars + for (int i = 0; i < UserVarsCount; i++) + CleanupItems(UserVars[i].item); + // cleanup user funcs + for (int i = 0; i < UserFuncsCount; i++) + CleanupItems(UserFuncs[i].root); + UserVarsCount = 0; + UserFuncsCount = 0; + + dbgGlobalCheck(); + } +} + +BOOL WINAPI _DllMainCRTStartup(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved) +{ + CleanAll(ul_reason_for_call == DLL_PROCESS_ATTACH); + return TRUE; +} + +#ifdef _DEBUG +BOOL WINAPI DllMain( + HANDLE hDllHandle, + DWORD dwReason, + LPVOID lpreserved + ) +{ + CleanAll(dwReason == DLL_PROCESS_ATTACH); + return TRUE; +} +#endif \ No newline at end of file diff --git a/Contrib/Math/Source/Math.h b/Contrib/Math/Source/Math.h new file mode 100644 index 00000000..23dad925 --- /dev/null +++ b/Contrib/Math/Source/Math.h @@ -0,0 +1,87 @@ +#pragma once + +#ifdef _DEBUG +//#define _DEBUG_LEAKS +#endif + +#ifdef _DEBUG_LEAKS + +#define dbgGlobalAlloc(a, b) watchGlobalAlloc(a, b) +#define dbgGlobalFree(a) watchGlobalFree(a) +#define dbgGlobalCheck() watchGlobal(); +void watchGlobal(); +void watchGlobalFree(HGLOBAL block); +HGLOBAL watchGlobalAlloc(UINT Flags, UINT size); + +#else + +#define dbgGlobalAlloc(a, b) GlobalAlloc(a, b) +#define dbgGlobalFree(a) GlobalFree(a) +#define dbgGlobalCheck() {}; + +#endif + + + +// only include this file from one place in your DLL. +// (it is all static, if you use it in two places it will fail) + +#define Math_INIT() { \ + g_stringsize=string_size; \ + g_stacktop=stacktop; \ + g_variables=variables; } + +// For page showing plug-ins +#define WM_NOTIFY_OUTER_NEXT (WM_USER+0x8) +#define WM_NOTIFY_CUSTOM_READY (WM_USER+0xd) +#define NOTIFY_BYE_BYE 'x' + +typedef struct _stack_t { + struct _stack_t *next; + char text[1]; // this should be the length of string_size +} stack_t; + +extern unsigned int g_stringsize; +extern stack_t **g_stacktop; +extern char *g_variables; + +enum +{ +INST_0, // $0 +INST_1, // $1 +INST_2, // $2 +INST_3, // $3 +INST_4, // $4 +INST_5, // $5 +INST_6, // $6 +INST_7, // $7 +INST_8, // $8 +INST_9, // $9 +INST_R0, // $R0 +INST_R1, // $R1 +INST_R2, // $R2 +INST_R3, // $R3 +INST_R4, // $R4 +INST_R5, // $R5 +INST_R6, // $R6 +INST_R7, // $R7 +INST_R8, // $R8 +INST_R9, // $R9 +INST_CMDLINE, // $CMDLINE +INST_INSTDIR, // $INSTDIR +INST_OUTDIR, // $OUTDIR +INST_EXEDIR, // $EXEDIR +INST_LANG, // $LANGUAGE +__INST_LAST +}; + + +// utility functions (not required but often useful) +int popstring(char *str); +void pushstring(char *str); +char *getuservariable(int varnum); +void setuservariable(int varnum, char *var); +char *AllocString(); +ExpressionItem *AllocItem(); +ExpressionItem *AllocArray(int size); +ExpressionItem *CopyItem(ExpressionItem *item, int NeedConst = 0); diff --git a/Contrib/Math/Source/Math.sln b/Contrib/Math/Source/Math.sln new file mode 100644 index 00000000..dd4a0de2 --- /dev/null +++ b/Contrib/Math/Source/Math.sln @@ -0,0 +1,21 @@ +Microsoft Visual Studio Solution File, Format Version 8.00 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Math", "Math.vcproj", "{0825E168-A8AD-4110-B35A-52B078C39C2A}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + Debug = Debug + Release = Release + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {0825E168-A8AD-4110-B35A-52B078C39C2A}.Debug.ActiveCfg = Debug|Win32 + {0825E168-A8AD-4110-B35A-52B078C39C2A}.Debug.Build.0 = Debug|Win32 + {0825E168-A8AD-4110-B35A-52B078C39C2A}.Release.ActiveCfg = Release|Win32 + {0825E168-A8AD-4110-B35A-52B078C39C2A}.Release.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/Contrib/Math/Source/Math.vcproj b/Contrib/Math/Source/Math.vcproj new file mode 100644 index 00000000..2da2c74a --- /dev/null +++ b/Contrib/Math/Source/Math.vcproj @@ -0,0 +1,240 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Contrib/Math/Source/MyMath.c b/Contrib/Math/Source/MyMath.c new file mode 100644 index 00000000..72047e79 --- /dev/null +++ b/Contrib/Math/Source/MyMath.c @@ -0,0 +1,353 @@ +#include +#include "Mathcrt.h" +#include "MyMath.h" +#include "Math.h" + +// Converts String to Int (Dec, Hex) or Float value +void StringToItem(char *&s, ExpressionItem *item, int options) +{ + item->type = IT_CONST | ITC_INT; + __int64 &v=*((__int64*)&(item->param1)); + v = 0; + + // Check for right input + if (!s) return; + + // String-value + if ((((options & (STI_FLOAT | STI_INT)) == 0) || *s == '\'' || *s == '\"' || *s == '`' || + ((*s != '+') && (*s != '-') && ((*s < '0') || (*s > '9')))) + && (options & STI_STRING)) + { + // end of string char + char eol = 0; + if (*s == '\'' || *s == '\"' || *s == '`') eol = *s; + else s--; + + item->type = IT_CONST | ITC_STRING; + // allocate memory buffer for string + char *sp; + sp = *((char**)&(item->param1)) = AllocString(); + while (*(++s) && (*s != eol)) + { + *(sp++) = *s; + } + if (*s == eol) s++; + *sp = 0; + } else + { + // strip leading spaces and tabs + while ((*s == ' ') || (*s == '\t')) s++; + // Hex-value + if ((options & STI_INT) && *s == '0' && (s[1] == 'x' || s[1] == 'X')) + { + s++; + while (*(s+1) == '0') *s++; + for (;;) + { + int c=*(++s); + if (c >= '0' && c <= '9') c-='0'; + else if (c >= 'a' && c <= 'f') c-='a'-10; + else if (c >= 'A' && c <= 'F') c-='A'-10; + else break; + v<<=4; + v+=c; + } + } + // Dec-value, possible floating-point + else + { + int sign=0, numsignif = 0; + if (*s == '-') sign++; else s--; + while (*(s+1) == '0') *s++; + for (;;) + { + int c=*(++s) - '0'; numsignif++; + if ((options & STI_FLOAT) && + ((c == ('e'-'0')) || (c==('E'-'0')) || (c==('.'-'0')) + || (numsignif > 18))) + { + // Switch to floating point conversion rountine + item->type = IT_CONST | ITC_FLOAT; + double& d = *((double*)&(item->param1)); + d = (double) v; + + while ((c >= 0) && (c <= 9)) + { + d = d*10.0 + (double) c; + c=*(++s) - '0'; + } + + // sub-decimal part + if (c == ('.'-'0')) + { + double pwr = 1.0, dec = 0.0; + for (;;) + { + c=*(++s) - '0'; + if ((c < 0) || (c > 9)) break; + dec = dec*10.0 + (double) c; + pwr *= 10.0; + } + d += dec/pwr; + } + // exponental part + if ((c == ('E'-'0')) || (c == ('e'-'0'))) + { + int expc = 0, esign = 0; + s++; + // detect exponential sign + if ((*s == '+') || (*s == '-')) + esign = (*s == '-'); + else s--; + + // detect exp value + for (;;) + { + c=*(++s) - '0'; + if ((c < 0) || (c > 9)) break; + expc = expc*10 + c; + } + + if (expc >= DBL_MAX_EXP) + { + d = HUGE_VAL; + expc = 0; + } + + double pwr = 1; + while (expc > 99) { pwr *= 1.0e100; expc -= 100; } + while (expc > 9) { pwr *= 1.0e10; expc -= 10; } + while (expc) { pwr *= 10.0; expc--; } + if (esign) d /= pwr; + else d *= pwr; + } + if (sign) d = -d; + return; + } + if (c < 0 || c > 9) break; + v*=10; + v+=c; + } + if (sign) v = -v; + if ((options & STI_FLOAT) && ((options & STI_INT) == 0)) + { + double& d = *((double*)&(item->param1)); + d = (double) v; + item->type = IT_CONST | ITC_FLOAT; + } + } + } +} + +void ItemToString(char *sbuf, ExpressionItem *item) +{ + if ((item == NULL) || ((item->type & ITEMTYPE) != IT_CONST)) + { + *sbuf = 0; + return; + } + + switch (item->type & ITEMSUBTYPE) + { + case ITC_STRING: + { + char *ptr = *((char**)&(item->param1)); + while (*(sbuf++) = *(ptr++)); + } + break; + case ITC_ARRAY: + { + ArrayDesc *ad = (ArrayDesc *) item->param1; + for (int index = 0; index < ad->count; index++) + if ((ad->array[index]) && + ((ad->array[index]->type & (ITEMTYPE|ITEMSUBTYPE)) == (IT_CONST | ITC_INT))) + if ((*(sbuf++) = (char) *((__int64*)&(ad->array[index]->param1))) == 0) + break; + } + break; + case ITC_FLOAT: + FloatFormat(sbuf, *((double*)&(item->param1)), 6); + break; + case ITC_INT: + itoa64(*((__int64*)&(item->param1)), sbuf); + break; + } +} + +void itoa64(__int64 i, char *buffer) +{ + char buf[128], *b = buf; + + if (i < 0) + { + *(buffer++) = '-'; + i = -i; + } + if (i == 0) *(buffer++) = '0'; + else + { + while (i > 0) + { + *(b++) = '0' + ((char) (i%10)); + i /= 10; + } + while (b > buf) *(buffer++) = *(--b); + } + *buffer = 0; +} + +#define _FLOAT_ROUND_ADJUST (double)5e-15 +extern "C" + int _floatp10(double *fnum, int *fsign, int prec); +extern "C" + int _ftol(double num); + +#define POS_INFINITY "#INF" +#define NEG_INFINITY "-#INF" + +void FloatFormatF(char *s, double value, int prec) +{ + int fpower, fsign, fdigit, fprec = 0, fzfill = 0; + + fpower = _floatp10(&value, &fsign, prec); + if(fsign < 0) *s++ = '-'; + if(fpower < 0) + { + *s++ = '0'; + fpower++; + fzfill++; + } else { + while(fpower >= 0) + { + if(fprec < 16) + { + fdigit = (int)value; + *s++ = (char)((char)fdigit + (char)48); + value -= (double)fdigit; + value *= (double)10; + value += _FLOAT_ROUND_ADJUST; + fprec++; + } else { + *s++ = '0'; + } + fpower--; + } + fpower = 0; + } + if(prec) + { + *s++ = '.'; + while(prec) + { + if(fzfill && fpower < 0) + { + *s++ = '0'; + fpower++; + } else { + if(fprec < 16) + { + fdigit = (int)value; + *s++ = (unsigned char)((unsigned char)fdigit + + (unsigned char)48); + + value -= (double)fdigit; + value *= (double)10; + value += _FLOAT_ROUND_ADJUST; + fprec++; + } else { + *s++ = '0'; + } + } + prec--; + } + } + *s = '\0'; +} + +void FloatFormatE(char *s, double fnum, int options) +{ + int fpower, fsign, fdigit, fprec = 0, prec, fzfill = 0; + double sfnum; + + prec = options & 0xF; + + sfnum = fnum; + fpower = _floatp10(&sfnum, &fsign, -999); + fpower = _floatp10(&fnum, &fsign, prec - fpower); + if(fsign < 0) *s++ = '-'; + fdigit = (int)fnum; + *s++ = (char)((char)fdigit + (char)48); + fnum -= (double)fdigit; + fnum *= (double)10; + fnum += _FLOAT_ROUND_ADJUST; + if(prec) + { + *s++ = '.'; + while(prec) + { + if(fprec < 16) + { + fdigit = (int)fnum; + *s++ = (unsigned char)((unsigned char)fdigit + + (unsigned char)48); + fnum -= (double)fdigit; + fnum *= (double)10; + fnum += _FLOAT_ROUND_ADJUST; + fprec++; + } else *s++ = '0'; + prec--; + } + } + *s++ = ((options & FF_LEXP)?('E'):('e')); + if(fpower >= 0) + { + *s++ = '+'; + } else { + *s++ = '-'; + fpower = -fpower; + } + if(fpower < 10) *s++ = '0'; + itoa64(fpower, s); +} + +void FloatFormat(char *s, double value, int options) +{ + int prec = options & 0xF; + + if(value == HUGE_VAL) + { + lstrcpy(s, POS_INFINITY); + return; + } else if(value == -HUGE_VAL) { + lstrcpy(s, NEG_INFINITY); + return; + } + + if (options & FF_NOEXP) FloatFormatF(s, value, prec); + else + if (options & FF_EXP) FloatFormatE(s, value, options); + else + { + double sfnum = value; + int fsign, fpower; + fpower = _floatp10(&sfnum, &fsign, -999); + sfnum = value; + fpower = _floatp10(&sfnum, &fsign, prec - fpower); + + if((value != 0.0) && ((fpower < -4) || (fpower >= prec))) + FloatFormatE(s, value, options); + else + { + prec -= (fpower + 1); + if(prec <= 0) prec = 1; + FloatFormatF(s, value, prec); + } + } +} + +int lstrcmpn(char *s1, const char *s2, int chars) +{ + while ((chars > 0) && (*s1) && (*s2) && (*(s1) == *(s2))) chars--, s1++, s2++; + if ((chars == 0) || (*s1 == *s2)) return 0; + return (*s1 - *s2); +} \ No newline at end of file diff --git a/Contrib/Math/Source/MyMath.h b/Contrib/Math/Source/MyMath.h new file mode 100644 index 00000000..2eb520de --- /dev/null +++ b/Contrib/Math/Source/MyMath.h @@ -0,0 +1,164 @@ +#pragma once + +#define DEFAULT_ARRAY_SIZE 1024 + +#define ITEMTYPE 0xFF0000 +// items classes +#define IT_CONST 0x000000 +#define IT_EXPRESSION 0x010000 +#define IT_OPERATOR 0x020000 +#define IT_VARIABLE 0x030000 +#define IT_LOGIC 0x040000 // flow control items +#define IT_FUNCTION 0x050000 +#define IT_ARRAY 0x060000 // array operation + +#define ITEMSUBTYPE 0x00FF00 +// const items +#define ITC_STRING 0x000100 +#define ITC_FLOAT 0x000200 +#define ITC_INT 0x000400 +#define ITC_ARRAY 0x000800 +#define ITC_VARPTR 0x001000 +#define ITC_UNKNOWN 0x002000 + +// type function +#define FTT_FLOATF (ITC_UNKNOWN << 0) +#define FTT_LEN (ITC_UNKNOWN << 1) +#define FTT_CHAR (ITC_UNKNOWN << 2) + +// additional option - for "ca" function +#define ITFA_COPY 0x000001 + +// ops items +#define ITO_MINUS 0x000100 +#define ITO_PLUS 0x000200 +#define ITO_SHL 0x000300 +#define ITO_SHR 0x000400 +#define ITO_MUL 0x000500 +#define ITO_DIV 0x000600 +#define ITO_SET 0x000700 +#define ITO_LAND 0x000800 +#define ITO_LOR 0x000900 +#define ITO_INC 0x000A00 +#define ITO_DEC 0x000B00 +#define ITO_LE 0x000C00 +#define ITO_GE 0x000D00 +#define ITO_NE 0x000E00 +#define ITO_EQ 0x000F00 +#define ITO_LS 0x001000 +#define ITO_GR 0x001100 +#define ITO_AND 0x001200 +#define ITO_MOD 0x001300 +#define ITO_OR 0x001400 +#define ITO_XOR 0x001500 +#define ITO_NOT 0x001600 +#define ITO_LNOT 0x001700 + +// variables sub-types +#define ITV_NSIS 0x000100 +#define ITV_USER 0x000200 +#define ITV_ARRITEM 0x000400 +#define ITV_STACK 0x000800 // plugin specific stack +#define ITV_NSTACK 0x001000 // nsis stack + +// logic sub-types +#define ITL_IF 0x000100 +#define ITL_WHILE 0x000200 + +// function sub-types +#define ITF_MATH1 0x000100 +#define ITF_MATH2 0x000200 +#define ITF_TYPE 0x000300 +#define ITF_USER 0x000400 + +// array items sub-types +#define ITA_DEFINE 0x000100 +#define ITA_ACCESS 0x000200 + +#define ITEMOPTIONS 0x0000FF + +// 16 bytes structure +typedef struct __ExpressionItem ExpressionItem; +typedef struct __ExpressionItem +{ + int type; + int param1; + int param2; + ExpressionItem *next; +} ExpressionItem; + +typedef struct __ParseInfo +{ +int SetupNewRoot; +ExpressionItem *item; +ExpressionItem *OpsStack; +ExpressionItem* &place; +ExpressionItem **root; +char valbuf[108]; +} ParseInfo; + +typedef struct __OpStruct +{ + char name[4]; + unsigned short int type; +} OpStruct; + +#define MAX_USER_VARS 256 +typedef struct __UserVar +{ + char name[28]; + ExpressionItem *item; +} UserVar; + +#define MAX_USER_FUNCS 256 +typedef struct __UserFunc +{ + char name[20]; + unsigned char vars[31]; + unsigned char varsnum; + unsigned int varflags; + ExpressionItem *root; +} UserFunc; + +typedef struct __ArrayDesc +{ + ExpressionItem **array; + int size; // size of allocated items pool + int count; // max number of item accessed + int references; // array will be killed at CleanupItems only when references == 0 +} ArrayDesc; + +typedef double (*Math1FuncPtr)(double arg); +typedef double (*Math2FuncPtr)(double arg, double arg2); +typedef double (*Math2iFuncPtr)(double arg, int *arg2); +typedef double (*Math2dFuncPtr)(double arg, double *arg2); + +typedef struct __MathFunction +{ + char name[3]; + unsigned char type; + Math1FuncPtr fptr; +} MathFunction; + +#define STI_STRING 0x0100 +#define STI_FLOAT 0x0200 +#define STI_INT 0x0400 + +#define FF_DEFAULT 0x00 // uses default mode: if available noexp, else exp +#define FF_NOEXP 0x10 // uses noexp mode +#define FF_EXP 0x20 // uses exp mode (small e) +#define FF_LEXP 0x40 // uses exp mode (large E) + +// parsestring options +#define PSO_STOPATDELIMETER 0x1 + +// RunTree options +#define RTO_NEEDCONST 0x0001 +#define RTO_PREFFEREDTYPE 0xFF00 +void RunTree(ExpressionItem *from, ExpressionItem* &result, int type); + +void StringToItem(char *&sbuf, ExpressionItem *item, int options); +void ItemToString(char *sbuf, ExpressionItem *item); +void FloatFormat(char *sbuf, double value, int options); +void itoa64(__int64 i, char *buffer); +int lstrcmpn(char *s1, const char *s2, int chars); \ No newline at end of file diff --git a/Contrib/Math/Source/mathcrt.h b/Contrib/Math/Source/mathcrt.h new file mode 100644 index 00000000..7762036f --- /dev/null +++ b/Contrib/Math/Source/mathcrt.h @@ -0,0 +1,87 @@ +/*---------------------------------------------------------------------------*/ +/* math.h - mathematics header file */ +/*---------------------------------------------------------------------------*/ +#pragma once + +extern "C" +{ + +extern double _infinity; +#define HUGE_VAL _infinity + +#define acos(x) _facos(x) +#define asin(x) _fasin(x) +#define atan(x) _fatan(x) +#define atan2(x, y) _fatan2(x, y) +#define ceil(x) _fceil(x) +#define cos(x) _fcos(x) +#define cosh(x) _fcosh(x) +#define exp(x) _fexp(x) +#define fabs(x) _fabs(x) +#define floor(x) _floor(x) +#define fmod(x, y) _fmod(x, y) +#define frexp(x, n) _frexp(x, n) +#define ldexp(x, n) _fldexp(x, n) +#define log(x) _flog(x) +#define log10(x) _flog10(x) +#define modf(x, y) _fmodf(x, y) +#define pow(x, y) _fpow(x, y) +#define sin(x) _fsin(x) +#define sinh(x) _fsinh(x) +#define sqrt(x) _fsqrt(x) +#define tan(x) _ftan(x) +#define tanh(x) _ftanh(x) + +/*---------------------------------------------------------------------------*/ +/* function prototpes */ +/*---------------------------------------------------------------------------*/ +double acos(double x); +double asin(double x); +double atan(double x); +double atan2(double x, double y); +double ceil(double x); +double cos(double x); +double cosh(double x); +double exp(double x); +double fabs(double x); +double floor(double x); +double fmod(double x, double y); +double frexp(double x, int *n); +double ldexp(double x, int n); +double log(double x); +double log10(double x); +double modf(double x, double *y); +double pow(double x, double y); +double sin(double x); +double sinh(double x); +double sqrt(double x); +double tan(double x); +double tanh(double x); + +/*---------------------------------------------------------------------------*/ +/* float.h - floating point include file */ +/*---------------------------------------------------------------------------*/ +#define FLT_RADIX 2 +#define FLT_ROUNDS 1 +#define FLT_DIG 6 +#define FLT_EPSILON 1.192092896e-07F +#define FLT_MANT_DIG 24 +#define FLT_MAX 3.402823466e+38F +#define FLT_MAX_EXP 38 +#define FLT_MIN 1.175494351e-38F +#define FLT_MIN_EXP (-37) + +#define DBL_DIG 15 +#define DBL_EPSILON 2.2204460492503131e-016 +#define DBL_MANT_DIG 53 +#define DBL_MAX 1.7976931348623158e+308 +#define DBL_MAX_EXP 308 +#define DBL_MIN 2.2250738585072014e-308 +#define DBL_MIN_EXP (-307) + +/*---------------------------------------------------------------------------*/ +/* function prototpes */ +/*---------------------------------------------------------------------------*/ +void _fpreset(void); + +} \ No newline at end of file diff --git a/Contrib/Math/Source/mathcrt.lib b/Contrib/Math/Source/mathcrt.lib new file mode 100644 index 0000000000000000000000000000000000000000..4dcadc5f78695536b097abb9a4f344bd9a532f27 GIT binary patch literal 25562 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%0ADQk0AC^x0ADH(0#BAl zfTzg60Z)||fTzh`;9U6)uut9r_RBHgJUIazkRf=>0=ZbdYd&zXlmX9_8sMvNo*lnW zYy>Wm`+;Z61HjkFH-P8J)4(Bl33$F70KQfZ179a6fv=ZSz-2NFb+<^y0WX#u;BwW| zEyep2-kxRLvr3FR*8CqNJT>dXSx?MAxc=^0)8>rte52rkSu^hQy>b7#(A7`3@A<-a zU-|Ntfyci2+QolbP<+*6N9X=|_#=5=`NDlKrTyThOXf6wJ3Kk{1#@bfW2XB-<9^Q$ zb-();UzjZ$4-~H3F?-IKh9Tek-nP%od*{TgH&YfBmE~RNJ8|u8cinj9k6QkrW6`KK zqg)4Op|eFzj2d+?RQ%5PXZ)dU`<8$Ct$W)ZXM2p!sH|yfs+`!+Qrj}IvYzMo)wkdS zqT0cVyDzsXT#a3wdT!6csnr@!VlYx;FW<0-{~1J+zchpAjkf0TFD@>r^EivO6U2{H{*jDDnbFjD-g)z zl)~V}CPf}g%51XmAIVZf5n|=__51Grj3u>%!}`6NwuxN25nB4u#qAF@;Ie9 zX0)Vcblw!Tq&`j&0C}8JtZGZ@!ZqW^SW;i52!K3JsSJczQWd|r^+ikS`xF6?$0@}z zswFk)Tj3H*>LtVx$m5hc7a^9^*Ei4EYf1fvA^`F@rPx?4sTXH=ZnC6KQv^UBrxe?f zB{lKM4L4d+V`x|a@;Ie9(zc`uz7|AvYkf^OrO4xy;w;0G`ov?8V6dj8=9^OFaZ0h* zv7|2fKwaIVXOs}rG&kHE&nd^w>!xWSSt9I6r(&iEA{X5*)aT2oaFyx+(TJPy- z_8yLM@9F6Go{oy|>1g>Lj-v1B=z3odN8SG)JmTHddzT~dqjjxqEs?OKn`gl5Mclr> z!gO;^8+T2x#~|3p6MSa>9427{`*%3T6IyVsQIv(aiu=&s|Jp&GBWCb)SL-{*k?3cz zDBsPPr@IrA6DvyQNx5dewsm=HEZ6gi$+`}#H}5)fWBqgmE$LA{hu#HNZ=MJL7uhA~ ARR910 literal 0 HcmV?d00001 diff --git a/Contrib/Math/Source/plugin.c b/Contrib/Math/Source/plugin.c new file mode 100644 index 00000000..1f1ef62e --- /dev/null +++ b/Contrib/Math/Source/plugin.c @@ -0,0 +1,135 @@ +#include +#include +#include "MyMath.h" +#include "Math.h" + +unsigned int g_stringsize; +stack_t **g_stacktop; +char *g_variables; + +#ifdef _DEBUG_LEAKS + +int blocksnum = 0; +HGLOBAL blocks[100000]; + +HGLOBAL watchGlobalAlloc(UINT Flags, UINT size) +{ + HGLOBAL block = GlobalAlloc(Flags, size); + blocks[blocksnum++] = block; + return block; +} + +void watchGlobalFree(HGLOBAL block) +{ + for (int i = 0; i < blocksnum; i++) + if (blocks[i] == block) blocks[i] = NULL; + GlobalFree(block); +} + +void watchGlobal() +{ + for (int i = 0; i < blocksnum; i++) + if (blocks[i] != NULL) + { + _RPT2(_CRT_WARN, "Memory leak %d at %8X\n", i, blocks[i]); + } +} + +#endif + +// utility functions (not required but often useful) +int popstring(char *str) +{ + stack_t *th; + if (!g_stacktop || !*g_stacktop) return 1; + th=(*g_stacktop); + lstrcpy(str,th->text); + *g_stacktop = th->next; + dbgGlobalFree((HGLOBAL)th); + return 0; +} + +void pushstring(char *str) +{ + stack_t *th; + if (!g_stacktop) return; + th=(stack_t*)dbgGlobalAlloc(GPTR,sizeof(stack_t)+g_stringsize); + lstrcpyn(th->text,str,g_stringsize); + th->next=*g_stacktop; + *g_stacktop=th; +} + +char *getuservariable(int varnum) +{ + if (varnum < 0 || varnum >= __INST_LAST) return NULL; + return g_variables+varnum*g_stringsize; +} + +void setuservariable(int varnum, char *var) +{ + if (var != NULL && varnum >= 0 && varnum < __INST_LAST) + lstrcpy(g_variables + varnum*g_stringsize, var); +} + +char *AllocString() +{ + return (char*) dbgGlobalAlloc(GPTR,g_stringsize); +} + +ExpressionItem *AllocItem() +{ + ExpressionItem *item = (ExpressionItem*)dbgGlobalAlloc(GPTR,sizeof(ExpressionItem)); + item->next = NULL; + item->type = IT_CONST | ITC_INT; + item->param1 = item->param2 = 0; + return item; +} + +ExpressionItem *AllocArray(int s) +{ + int size = DEFAULT_ARRAY_SIZE; + while (s > size) size*=2; + + ExpressionItem *ai = (ExpressionItem*)dbgGlobalAlloc(GPTR,sizeof(ExpressionItem)); + ai->type = IT_CONST | ITC_ARRAY; + ai->param1 = (int) dbgGlobalAlloc(GPTR, sizeof(ArrayDesc)); + + ArrayDesc *ad = *((ArrayDesc**)&(ai->param1)); + // initialize and clear the array memory + ad->array = (ExpressionItem**) dbgGlobalAlloc(GPTR, size*sizeof(ExpressionItem*)); + ad->size = size; + ad->count = 0; + ad->references = 1; + return ai; +} + +ExpressionItem *CopyItem(ExpressionItem *citem, int NeedConst) +{ + if (!citem) return NULL; + ExpressionItem *item = NULL; + if ((NeedConst) && ((citem->type & ITEMTYPE) != IT_CONST)) + { + // in case of non constant expression - flat it to const + RunTree(citem, item, RTO_NEEDCONST | ITC_INT | ITC_STRING | ITC_FLOAT | ITC_ARRAY); + if (item) return item; + } + + item = AllocItem(); + item->type = citem->type; + if ((item->type & (ITEMTYPE | ITEMSUBTYPE)) == (IT_CONST | ITC_STRING)) + { + item->param1 = (int) AllocString(); + lstrcpy((LPSTR) item->param1, (LPSTR) citem->param1); + } else if (((item->type & (ITEMTYPE | ITEMSUBTYPE)) == (IT_CONST | ITC_ARRAY)) + || + ((item->type & (ITEMTYPE | ITEMSUBTYPE)) == (IT_VARIABLE | ITV_ARRITEM))) + { + item->param1 = citem->param1; + ArrayDesc *ad = (ArrayDesc*) item->param1; + ad->references++; + } + else item->param1 = citem->param1; + item->param2 = citem->param2; + item->next = NULL; + return item; +} \ No newline at end of file diff --git a/Contrib/Math/math.nsi b/Contrib/Math/math.nsi new file mode 100644 index 00000000..bde76168 --- /dev/null +++ b/Contrib/Math/math.nsi @@ -0,0 +1,39 @@ +; This is just an example of Math plugin +; +; (c) brainsucker, 2002 +; (r) BSForce + +Name "Math Plugin Example" +OutFile "math.exe" +SetPluginUnload alwaysoff +ShowInstDetails show +XPStyle on + +Section "ThisNameIsIgnoredSoWhyBother?" + Math::Script 'SaR(s,fa,ra, i,f,r,e,p) (i=0;#{i=0, (NS=s[p+4,]; NS=#[p>0,s[,p-1],'']), (NS='';NS=s)])" + + Math::Script "a = 'Hello \r\n World \r\n!!!'; a = SaR(a,{'\r','\n'},{'$\r','$\n'}); R0 = a" + Math::Script "NS = '$\"In quotes$\"'; TQ(); R1=NS; R3=P(s(R1),'qu')" + Math::Script "NS = 'No quotes'; TQ(); R2=NS" + Math::Script "NS='123\r\n456\r\n789'; DL(); R4=NS; DL(); R5=NS; DL(); R6=NS; R7=NS" + + + DetailPrint "'$R0'" + DetailPrint "'$R1'" + DetailPrint "'$R2'" + DetailPrint "'$R3'" + DetailPrint "'$R4'" + DetailPrint "'$R5'" + DetailPrint "'$R6'" + DetailPrint "'$R7'" + + ; last plugin call must not have /NOUNLOAD so NSIS will be able to delete the temporary DLL + SetPluginUnload manual + ; do nothing + Math::Script "" +SectionEnd + +; eof diff --git a/Contrib/Math/mathtest.ini b/Contrib/Math/mathtest.ini new file mode 100644 index 00000000..e968034b --- /dev/null +++ b/Contrib/Math/mathtest.ini @@ -0,0 +1,101 @@ +[Settings] +NumFields=10 +NextButtonText=Execute +CancelButtonText=Quit +BackButtonText=Readme + +[Field 1] +Type=label +Text=Enter your script here: +Left=0 +Right=-1 +Top=0 +Bottom=8 + +[Field 2] +Type=text +Left=0 +Right=-1 +Top=9 +Bottom=55 +flags=MULTILINE|WANTRETURN|HSCROLL|VSCROLL +State="" + +[Field 3] +Type=text +Left=53 +Right=175 +Top=56 +Bottom=140 +flags=MULTILINE|READONLY +State="" + +[Field 4] +Type=text +Left=175 +Right=-1 +Top=56 +Bottom=140 +flags=MULTILINE|READONLY +State="" + +[Field 5] +Type=RadioButton +Left=0 +Right=-1 +Top=70 +Bottom=80 +flags=GROUP +Text="Your script" +State=1 + +[Field 6] +Type=RadioButton +Left=0 +Right=-1 +Top=80 +Bottom=90 +flags= +Text="Sample 1" +State=0 + +[Field 7] +Type=RadioButton +Left=0 +Right=-1 +Top=90 +Bottom=100 +flags= +Text="Sample 2" +State=0 + +[Field 8] +Type=RadioButton +Left=0 +Right=-1 +Top=100 +Bottom=110 +flags= +Text="Sample 3" +State=0 + +[Field 9] +Type=RadioButton +Left=0 +Right=-1 +Top=110 +Bottom=120 +flags= +Text="Sample 4" +State=0 + +[Field 10] +Type=RadioButton +Left=0 +Right=-1 +Top=120 +Bottom=130 +flags= +Text="Sample 5" +State=0 + diff --git a/Contrib/Math/mathtest.nsi b/Contrib/Math/mathtest.nsi new file mode 100644 index 00000000..500ae0f1 --- /dev/null +++ b/Contrib/Math/mathtest.nsi @@ -0,0 +1,171 @@ +;NSIS Modern User Interface version 1.65 +;InstallOptions Example Script +;Written by Joost Verburg + + !define MUI_BUTTONTEXT_NEXT "Execute" + +;--------------------- +;Include Modern UI + + !include "MUI.nsh" + +;-------------------------------- +;Product Info + +Name "Math::Script Test" + +;-------------------------------- +;Configuration + + ;General + OutFile "MathTest.exe" + +;-------------------------------- +;Variables + + Var TEMP1 + Var TEMP2 + Var TEMP3 + +;-------------------------------- +;Pages + + !insertmacro MUI_PAGE_LICENSE "mathtest.txt" + Page custom ScriptPageEnter + Page instfiles + +;-------------------------------- +;Modern UI Configuration + +; !define MUI_ABORTWARNING + +;-------------------------------- +;Languages + + !insertmacro MUI_LANGUAGE "English" + +;-------------------------------- +;Reserve Files + + ;Things that need to be extracted on first (keep these lines before any File command!) + ;Only for BZIP2 compression + + ReserveFile "MathTest.ini" + !insertmacro MUI_RESERVEFILE_INSTALLOPTIONS + +;-------------------------------- +;Installer Functions + +LangString SCRIPTSAMPLE0 ${LANG_ENGLISH} "r0 = 'Hello'; r1 = 'Math::Script'\r\nr0 += ' from the ' + r1 + '!'; r1=''" +LangString SCRIPTSAMPLE1 ${LANG_ENGLISH} "a =0; b=1.0\r\n#{a++ < 100, b *= a}\r\nr0 = a; R0 = b; R1 = ff(b, 15)\r\nr1 = (a-1) + '! = ' + b" +LangString SCRIPTSAMPLE2 ${LANG_ENGLISH} 'pi=3.14159; \r\nangle = pi/4;\r\ntext = "x = " + ff(angle,16+3) \r\nr0 = text += ", sin x = " + sin(angle)' +LangString SCRIPTSAMPLE3 ${LANG_ENGLISH} "v1 = 123.456; v2 = 123456789.1011\r\nr0 = v1; r1 = v2\r\nr2 = ff(v1, 3); r3 = ff(v2, 3); r4 = ff(v1, 3+16); r5 = ff(v2, 3+16)\r\nr6 = ff(v1, 3+32); r7 = ff(v2, 3+32); r8 = ff(v1, 3+32+64); r9 = ff(v2, 3+32+64)\r\n" +LangString SCRIPTSAMPLE4 ${LANG_ENGLISH} "a = 10000; b = 0; #{--a > 0, b+= a}; r0 = a; r1 = b\r\nz = 1.55; r2 = #[z > 1.5, 'Its greater', 'Its lower']\r\nz = 1.45; r3 = #[z > 1.5, 'Its greater', 'Its lower']" +LangString SCRIPTSAMPLE5 ${LANG_ENGLISH} 'r0 = "123a123"\r\nr1 = r0; \r\nr2 = s(r0); r3 = f(r0); r4 = i(r0); r5 = len(r0)' + +Function .onInit + + ;Extract InstallOptions INI files + !insertmacro MUI_INSTALLOPTIONS_EXTRACT "MathTest.ini" + + Strcpy "$TEMP1" "$(SCRIPTSAMPLE0)" + !insertmacro MUI_INSTALLOPTIONS_WRITE "MathTest.ini" "Field 2" "State" $TEMP1 + +FunctionEnd + +LangString TEXT_IO_TITLE ${LANG_ENGLISH} "MathTest Script Page" +LangString TEXT_IO_SUBTITLE ${LANG_ENGLISH} "Try your scripting capapibilites or test one of sample scripts" + + +Function DumpVariables + Strcpy "$TEMP1" "$$0='$0'\r\n$$1='$1'\r\n$$2='$2'\r\n$$3='$3'\r\n$$4='$4'\r\n$$5='$5'\r\n$$6='$6'\r\n$$7='$7'\r\n$$8='$8'\r\n$$9='$9'" + !insertmacro MUI_INSTALLOPTIONS_WRITE "MathTest.ini" "Field 3" "State" $TEMP1 + Strcpy "$TEMP1" "$$R0='$R0'\r\n$$R1='$R1'\r\n$$R2='$R2'\r\n$$R3='$R3'\r\n$$R4='$R4'\r\n$$R5='$R5'\r\n$$R6='$R6'\r\n$$R7='$R7'\r\n$$R8='$R8'\r\n$$R9='$R9'" + !insertmacro MUI_INSTALLOPTIONS_WRITE "MathTest.ini" "Field 4" "State" $TEMP1 +FunctionEnd + +Function ClearVariables + Math::Script "r0=r1=r2=r3=r4=r5=r6=r7=r8=r9=R0=R1=R2=R3=R4=R5=R6=R7=R8=R9=''" +FunctionEnd + +Function GetLine + push $TEMP1 + Math::Script /NOUNLOAD "DL()" + pop $TEMP2 + pop $TEMP1 +FunctionEnd + +Function ExecuteScript + !insertmacro MUI_INSTALLOPTIONS_READ $TEMP1 "MathTest.ini" "Field 2" "State" + + Math::Script /NOUNLOAD "TQ(s) (s = s(NS); #[s[0]=='$\"',s=s[1,]]; #[s[-1]=='$\"',s=s[,-2]]; NS = s)" + Math::Script /NOUNLOAD "P(s,e, p,i) (p=-1;i=0; #{(i=0, (NS=s[p+4,]; NS=#[p>0,s[,p-1],'']), (NS='';NS=s)])" + + push $TEMP1 + ; remove "" + Math::Script /NOUNLOAD "TQ()" + pop $TEMP1 + + ; script at $TEMP1 +Go: + StrLen $TEMP3 $TEMP1 + IntCmp $TEMP3 0 End + ; get single line to $TEMP2 + Call GetLine +; MessageBox MB_OK "'$TEMP2' '$TEMP1'" + Math::Script /NOUNLOAD "$TEMP2" + goto Go +End: + Math::Script "" +FunctionEnd + +Function ScriptPageEnter + + !insertmacro MUI_HEADER_TEXT "$(TEXT_IO_TITLE)" "$(TEXT_IO_SUBTITLE)" + +Again: + Call ClearVariables + Call ExecuteScript + Call DumpVariables + + !insertmacro MUI_INSTALLOPTIONS_DISPLAY_RETURN "mathtest.ini" + pop $TEMP3 + + !insertmacro MUI_INSTALLOPTIONS_READ $TEMP1 "MathTest.ini" "Field 5" "State" + IntCmp $TEMP1 1 Test + + Strcpy "$TEMP2" "$(SCRIPTSAMPLE1)" + !insertmacro MUI_INSTALLOPTIONS_READ $TEMP1 "MathTest.ini" "Field 6" "State" + IntCmp $TEMP1 1 Write + + Strcpy "$TEMP2" "$(SCRIPTSAMPLE2)" + !insertmacro MUI_INSTALLOPTIONS_READ $TEMP1 "MathTest.ini" "Field 7" "State" + IntCmp $TEMP1 1 Write + + Strcpy "$TEMP2" "$(SCRIPTSAMPLE3)" + !insertmacro MUI_INSTALLOPTIONS_READ $TEMP1 "MathTest.ini" "Field 8" "State" + IntCmp $TEMP1 1 Write + + Strcpy "$TEMP2" "$(SCRIPTSAMPLE4)" + !insertmacro MUI_INSTALLOPTIONS_READ $TEMP1 "MathTest.ini" "Field 9" "State" + IntCmp $TEMP1 1 Write + + Strcpy "$TEMP2" "$(SCRIPTSAMPLE5)" + +Write: + !insertmacro MUI_INSTALLOPTIONS_WRITE "MathTest.ini" "Field 2" "State" "$TEMP2" + !insertmacro MUI_INSTALLOPTIONS_WRITE "MathTest.ini" "Field 5" "State" "1" + !insertmacro MUI_INSTALLOPTIONS_WRITE "MathTest.ini" "Field 6" "State" "0" + !insertmacro MUI_INSTALLOPTIONS_WRITE "MathTest.ini" "Field 7" "State" "0" + !insertmacro MUI_INSTALLOPTIONS_WRITE "MathTest.ini" "Field 8" "State" "0" + !insertmacro MUI_INSTALLOPTIONS_WRITE "MathTest.ini" "Field 9" "State" "0" + !insertmacro MUI_INSTALLOPTIONS_WRITE "MathTest.ini" "Field 10" "State" "0" + +Test: + Strcmp $TEMP3 "success" Again + +FunctionEnd + +Section "Dummy Section" SecDummy +SectionEnd diff --git a/Contrib/Math/mathtest.txt b/Contrib/Math/mathtest.txt new file mode 100644 index 00000000..f911e66d --- /dev/null +++ b/Contrib/Math/mathtest.txt @@ -0,0 +1,7 @@ +Math Tester. + +This demo allows you to test your Math::Script expressions without need to compile anything. Just enter your expressions into multiline editbox (every single line is a separate call to Math::Script) or select one of sample expressions and press Execute. + +Every call to Math::Script can accept up to 1kb of script, but this demo is limited to the summ of 1 kb at all lines. And... watch your scripts. No.... Watch your errors at scripts! + +(c) Brainsucker, 2003. diff --git a/Plugins/Math.dll b/Plugins/Math.dll new file mode 100644 index 0000000000000000000000000000000000000000..c8c4d4a86a0dc48af2cf8e17b50b9be3798996bd GIT binary patch literal 14336 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 E13?Opwg3PC literal 0 HcmV?d00001