%MM% v0.2a - a full-featured WinNT math macro

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

Message
Author
CirothUngol
Posts: 46
Joined: 13 Sep 2017 18:37

Re: %MM% - a full-featured WinNT math macro

#16 Post by CirothUngol » 10 Aug 2018 16:34

dbenham wrote:
10 Aug 2018 13:12
:shock: That doesn't look right :?
It looks like you initialize the return value to 0 with SET dc=0, and then conditionally set the return value to 1, but using n0 instead of dc. What am I missing :?:
It would probably help if I bothered to delineate exactly what each variable is, I should actually do this for all variables before releasing v0.2, so that improvements could be easier to spot by others:
n0 = final answer
n1,n2,n3 = values from operands, padded with zeros both left and right of the decimal. For add/subtract/compare.
o1,o2,o3 = values from operands, not padded. For multiply/divide.
dc = decimal place for n0 (counted from right to left). This is initially set for add/subtract (because it saved a few characters, probably not anymore) and needs to be cleared because the answer will have 0 decimal places to the right.

Code: Select all

)ELSE IF %%b=="||" (IF !o1! NEQ 0 SET o1=1^&IF !o2! NEQ 0 SET o2=1^&SET /A n0=o1^^o2)%\n%%= 'logical exclusive or /xor =%
It's a bit smaller than my version... but won't it fail if o1=0 because it won't execute the remainder of the line?

dbenham
Expert
Posts: 2461
Joined: 12 Feb 2011 21:02
Location: United States (east coast)

Re: %MM% - a full-featured WinNT math macro

#17 Post by dbenham » 10 Aug 2018 18:46

So n0 must be initialized to 0 before entering the IF, yes?
CirothUngol wrote: It's a bit smaller than my version... but won't it fail if o1=0 because it won't execute the remainder of the line?
Doh - Of course. And I probably need to clear dc as well.

My formula needs parens around each of the IF statements

Code: Select all

)ELSE IF %%b=="||" ((IF !o1! NEQ 0 SET o1=1)^&(IF !o2! NEQ 0 SET o2=1)^&SET /A dc=0,n0=o1^^o2)%= 'logical exclusive or /xor =%%\n%
Or better yet, use multiple lines. It looks longer, but actually is smaller

Code: Select all

%=  =%)ELSE IF %%b=="||" (%= 'logical exclusive or /xor =%%\n%
%=    =%IF !o1! NEQ 0 SET o1=1%\n%
%=    =%IF !o2! NEQ 0 SET o2=1%\n%
%=    =%SET /A dc=0,n0=o1^^o2%\n%
%=  =%)%\n%
Not sure it is smaller anymore. :?

Note - I like putting the %\n% after the comment - it makes it easier to scan and verify each line is terminated properly.


Dave Benham

thefeduke
Posts: 211
Joined: 05 Apr 2015 13:06
Location: MA South Shore, USA

Re: %MM% - a full-featured WinNT math macro

#18 Post by thefeduke » 15 Aug 2018 14:00

The two main posters have me awed. My compliments!

If I understand correctly, the name of the script that creates the macro variable %MM% is of little importance. I observed that it uses no input arguments and that to set up the macro, a minimum of two console interactions are needed after the macro is defined. It struck me that I would like to set up and use this macro in a script while avoiding the need for those two interactions.

To do that, I added this line just before the label :mm_help after the macro was just created:

Code: Select all

If NOT "%~1."=="." GoTo :Eof 
So the demo is bypassed if any argument is passed. Would that indeed cause no problems? It appeared to work for me.

John A.

CirothUngol
Posts: 46
Joined: 13 Sep 2017 18:37

Re: %MM% - a full-featured WinNT math macro

#19 Post by CirothUngol » 15 Aug 2018 17:57

Thanks for the compliments! ^_^

Yup, that should work just fine. Only the 'SET \n=' and the 'SET MM=' definitions are required, the macro is otherwise completely self-contained. You could also just copy/paste it into your batchfile removing the need for multiple batch scripts.

The interactive bit is just to display the help, show it's abilities, and allow the user to experiment the some expressions (for me, that's used for testing/troubleshooting).

CirothUngol
Posts: 46
Joined: 13 Sep 2017 18:37

Re: %MM% v0.2 - a full-featured WinNT math macro

#20 Post by CirothUngol » 17 Aug 2018 18:04

I've updated the OP with MM v0.2, I was able to add logical, unary, and bitwise operations. The bitwise ops are all passed directly to SET/A, which treats values above 2147483647 as 2147483647 and below -2147483648 as -2147483648 (because they're contained in variables?). I've reduced most of the variables in the Math portion to single letters and eliminated the huge IF...ELSE structure to reduce the size and have cleaned-up all of the remarks so that they're more legible and have included a list of variables used in the script comments.

All of the operators are the same as SET/A except for:
'=Logical Not - exclamation point is just too troublesome, and the tiny apostrophe was the best choice available.
|&=Logical Xor - caret is also far too problematic. A single ^ is OK, but double ^^ is probably a troubleshooting nightmare.
I've used this reference to create/correct the operator precedence. Beyond the operators listed in the docs I also use "{ \ , ` }" as internal substitutes for "^ * ? - ~" so I've quite literally used all of the special characters except for "[ _ ]" which are the only ones allowed in user variables.

My testing was mediocre at best, so again please post if you try it and find any issues.

CirothUngol
Posts: 46
Joined: 13 Sep 2017 18:37

Re: %MM% v0.2 - a full-featured WinNT math macro

#21 Post by CirothUngol » 18 Aug 2018 13:48

I was scanning the final v0.2 macro and cleaning up the various versions I went through over the last couple of months (about 12) and think that, other than any corrections for discovered errors, %MM% is done. It might be possible to squeeze out a few dozen more characters, but what else is there?
The idea of inserting strings into error messages has merit. There are 9 ECHOs where errors occur, any of the variables listed in the comments could be included with the error to help illustrate what went wrong. This is originally how I planned the errors messages, but because of size constraint and my desire to have it appear just like SET/A I removed them. Other than that, I think bugfix will be the only other release.

Oh, thanks Dave for archiving v0.1 on the second post of this thread. In my zeal to post v0.2 I simply clobbered on top of it.^_^
Just for grins, I thought I'd post an early version of MM, from when I though I was just writing a quick integer-only macro to add and divide large cumulative file sizes. This is the 4th version I have saved named iMath2.cmd. I didn't change a thing, so the code may be a bit messy:

Code: Select all

:: %iMath% [var=] num|var +|-|x|/|@|GT|LS|EQ|GE|LE|NE num|var [;...]
:: Macro for mathmatical and comparison operations of large integers.
:: Operands containing only -0123456789. are input as integers, else variables.
:: Decimals are truncated and undefined variables are treated as zero.
:: The value of each operation is always returned in variable %iMath_%.
:: Comparison operations will also return ERRORLEVEL 0=false, 1=true.
:: Multiple expressions may be separated by comma[,] or semicolon[;] and
:: spaces must separate all operands from operators other than [=,;].
:: Variable names beginning with 'iMath*' are reserved and should not be used.
:: SET mx=maximum # of digits up to ~256, larger numbers=slower runtimes.
:: Divisor for / and @ is limited to 8 digits, the dividend has no such limit.
::
::      add +: %iMath% 123456789 + 987654321;sets iMath_=1111111110, always
:: subtract -: %iMath% var1=9876543210 - -1234567890;sets var1=1111111110
:: multiply x: %iMath% var2=777.777 x var1;truncate decimal, var2=863333332470
::   divide /: %iMath% var3=var2 / 777.0123456789;divides var2 by 777
::  modulus @: %iMath% var4=var1 @ var3;sets var4=0 as var1=var3=1111111110
:: comparison: %iMath% var4=var1 EQU var3;sets both var4 and ERRORLEVEL = 1
::
@SET mx=16

@ECHO OFF
SETLOCAL EnableExtensions EnableDelayedExpansion
:: make zeros for padding
SET z0=0000000000000000
FOR %%A IN (1 2 3) DO CALL SET z0=%%z0%%%%z0%%%%z0%%%%z0%%
CALL SET z0=%%z0:~-%mx%%%

:: setup macro constants
SET ^"LF=^

^"
SET ^"\n=^^^%LF%%LF%^%LF%%LF%^^"
IF "!!"=="" ( SET "b=^^^^^^^!" & SET "q=^^^!"
) ELSE SET "b=!" & SET "q=!"

SET iMath=FOR %%# IN (1 2) DO IF %%#==2 ( SETLOCAL EnableExtensions EnableDelayedExpansion%\n%
	FOR %%A IN (C E0 E1 N0 N1 N2 OP R U0 U1 U2 V) DO SET iMath%%A=%\n%
	SET "iMathH=[var=] num|var +|-|x|/|@|GT|LS|EQ|GE|LE|NE num|var [;...]"%\n%
	IF NOT DEFINED iMathP SET iMathP=A%\n%
	SET iMathP=%b%iMathP:^"=%b%%\n%
	SET iMathP=%b%iMathP:\=/%b%%\n%
	SET iMathP=%b%iMathP:,=;%b%%\n%
	SET iMathP=%b%iMathP:;=^" ^"%b%%\n%
	FOR /F "tokens=1* delims=*" %%A IN ("%q%iMathP%q%") DO IF "%%B" NEQ "" SET iMathP=^& SET iMathE1= 00^& ECHO unknown operator *: %b%iMathH%b%^>^&2%\n%
	FOR %%: IN ("%q%iMathP%q%") DO IF %%: NEQ "" ( SETLOCAL%\n%
		SET iMathT=%%~:%\n%
		FOR /F "tokens=1* delims==" %%A IN (%%:) DO IF "%%B" NEQ "" FOR %%C IN (%%A) DO SET iMathV=%%C^& SET iMathT=%%B%\n%
		FOR /F "tokens=1-3" %%A IN ("%q%iMathT%q%") DO SET iMathOP=%%B^& SET iMathT=%%A %%C%\n%
		FOR %%A IN (%b%iMathT%b%) DO FOR /F "tokens=* delims=-0123456789." %%B IN ("%%A") DO ( SET /A iMathC+=1%\n%
			IF "%%B"=="" ( SET iMathT=%%A%\n%
			) ELSE SET iMathT=%b%%%A%b%%\n%
			IF NOT DEFINED iMathT SET iMathT=0%\n%
			IF "%q%iMathT:~0,1%q%"=="-" SET iMathT=%b%iMathT:~1%b%^& SET iMathU%b%iMathC%b%=-%\n%
			FOR /F "tokens=1 delims=." %%C IN ("%q%iMathT%q%.") DO SET iMathN%b%iMathC%b%=%z0%%%C)%\n%
		SET /A f0=t0=t1=t2=t3=0%\n%
		FOR %%A IN (e0 n0 n1 n2 op u0 u1 u2) DO SET %%A=%b%iMath%%A%b%%\n%
		IF "%q%op%q%"=="+" IF "%q%u2%q%" NEQ "%q%u1%q%" SET u2=%b%u1%b%^& SET op=-%\n%
		IF "%q%op%q%"=="-" IF "%q%u2%q%" NEQ "%q%u1%q%" SET u2=%b%u1%b%^& SET op=+%\n%
		IF "%q%op%q%"=="@" SET op=/^& SET f0=1%\n%
		IF %b%iMathC%b% LSS 2 ( SET e0= 00%\n%
			ECHO missing parameters: %b%iMathH%b%^>^&2%\n%
		) ELSE IF "%q%op%q%"=="+" ( SET u0=%b%u1%b%%\n%
			FOR /L %%A IN (1,1,%mx%) DO ( SET /A t1=%b%n1:~-%%A,1%b%+%b%n2:~-%%A,1%b%+t0,t0=0%\n%
				IF %b%t1%b% GTR 9 SET /A t1-=10,t0=1%\n%
				SET n0=%b%t1%b%%b%n0%b%)%\n%
		) ELSE IF "%q%op%q%"=="-" (%\n%
			FOR /L %%A IN (1,1,%mx%) DO ( SET /A t1=%b%n1:~-%%A,1%b%,t2=%b%n2:~-%%A,1%b%%\n%
				IF %b%t1%b% GTR %b%t2%b% SET t3=1%\n%
				IF %b%t1%b% LSS %b%t2%b% SET t3=2)%\n%
			IF %b%t3%b%==2 ( SET t1=%b%n1%b%^& SET n1=%b%n2%b%^& SET n2=%b%t1%b%%\n%
				IF "%q%u1%q%"=="" SET u0=-%\n%
			) ELSE SET u0=%b%u1%b%%\n%
			IF %b%t3%b% NEQ 0 FOR /L %%A IN (1,1,%mx%) DO (%\n%
				SET /A t1=%b%n1:~-%%A,1%b%,t2=%b%n2:~-%%A,1%b%+t0,t0=0%\n%
				IF %b%t1%b% LSS %b%t2%b% SET /A t1+=10,t0=1%\n%
				SET /A t1-=t2%\n%
				SET n0=%b%t1%b%%b%n0%b%)%\n%
		) ELSE IF /I "%q%op%q%"=="x" ( IF "%q%u1%q%" NEQ "%q%u2%q%" SET u0=-%\n%
			FOR /L %%A IN (1,1,%mx%) DO SET _%%A=0%\n%
			FOR /L %%A IN (1,1,%mx%) DO ( SET /A t0=0,c0=%%A%\n%
				FOR /L %%B IN (1,1,%mx%) DO SET /A t1=%b%n1:~-%%A,1%b%*%b%n2:~-%%B,1%b%+t0,t0=t1/10,_%b%c0%b%+=t1 %% 10,c0+=1%\n%
				SET _%b%c0%b%=%b%t0%b%)%\n%
			FOR /L %%A IN (1,1,%b%c0%b%) DO (%\n%
				SET /A _%%A+=t2,t2=_%%A/10%\n%
				SET n0=%b%_%%A:~-1%b%%b%n0%b%)%\n%
			SET n0=%b%t2%b%%b%n0%b%%\n%
		) ELSE IF "%q%op%q%"=="/" ( IF "%q%u1%q%" NEQ "%q%u2%q%" SET u0=-%\n%
			FOR %%A IN (1 2) DO ( FOR /F "tokens=* delims=0" %%B IN ("%q%n%%A%q%") DO SET n%%A=%%B%\n%
				IF NOT DEFINED n%%A SET n%%A=0)%\n%
			IF %b%n2%b%==1 ( SET n0=%b%n1%b%%\n%
			) ELSE IF %b%n2%b%==0 ( SET e0= 00%\n%
				ECHO Divide by zero error.^>^&2%\n%
			) ELSE IF %b%n1%b% NEQ 0 FOR /L %%A IN (0,1,%mx%) DO IF "%q%n1:~%%A,1%q%" NEQ "" ( SET t1=0%\n%
				IF %b%t0%b%==0 SET t0=%\n%
				SET t0=%b%t0%b%%b%n1:~%%A,1%b%%\n%
				IF %b%t0%b% GEQ %b%n2%b% FOR /L %%B IN (1,1,9) DO IF %b%t0%b% GEQ %b%n2%b% SET /A t1+=1,t0-=n2%\n%
				SET n0=%b%n0%b%%b%t1%b%)%\n%
			IF %b%f0%b% NEQ 0 SET u0=%b%u1%b%^& SET n0=%b%t0%b%%\n%
		) ELSE ( SET /A n0=0,gt=1,lt=-1%\n%
			IF "%q%u1%q%"==""  IF "%q%u2%q%"=="-" SET t0=1%\n%
			IF "%q%u1%q%"=="-" IF "%q%u2%q%"==""  SET t0=-1%\n%
			IF "%q%u1%q%"=="-" IF "%q%u2%q%"=="-" SET /A gt=-1,lt=1%\n%
			IF %b%t0%b%==0 FOR /L %%A IN (1,1,%mx%) DO ( SET /A t1=%b%n1:~-%%A,1%b%,t2=%b%n2:~-%%A,1%b%%\n%
				IF %b%t1%b% GTR %b%t2%b% SET t0=%b%gt%b%%\n%
				IF %b%t1%b% LSS %b%t2%b% SET t0=%b%lt%b%)%\n%
			IF /I "%q%op:~0,2%q%"=="GT" ( IF %b%t0%b% GTR 0 SET n0=1%\n%
			) ELSE IF /I "%q%op:~0,2%q%"=="LS" ( IF %b%t0%b% LSS 0 SET n0=1%\n%
			) ELSE IF /I "%q%op:~0,2%q%"=="EQ" ( IF %b%t0%b% EQU 0 SET n0=1%\n%
			) ELSE IF /I "%q%op:~0,2%q%"=="GE" ( IF %b%t0%b% GEQ 0 SET n0=1%\n%
			) ELSE IF /I "%q%op:~0,2%q%"=="LE" ( IF %b%t0%b% LEQ 0 SET n0=1%\n%
			) ELSE IF /I "%q%op:~0,2%q%"=="NE" ( IF %b%t0%b% NEQ 0 SET n0=1%\n%
			) ELSE SET e0= 00^& ECHO unknown operator %b%op:~0,2%b%: %b%iMathH%b%^>^&2%\n%
			IF %b%n0%b%==1 SET e0= 00)%\n%
		FOR /F "tokens=* delims=0" %%A IN ("%q%n0%q%") DO SET n0=%%A%\n%
		IF NOT DEFINED n0 SET n0=0%\n%
		IF %b%n0%b% NEQ 0 SET n0=%b%u0%b%%b%n0%b%%\n%
		FOR /F "tokens=1-3 delims=;" %%A IN (^""%q%iMathV%q%";"%q%n0%q%";"%q%e0%q%"^") DO (%\n%
			ENDLOCAL%\n%
			IF %%A NEQ "" SET %%~A=%%~B^& SET iMathR=%b%iMathR%b%" "%%~A=%%~B%\n%
			SET iMath_=%%~B%\n%
			SET iMathE1=%%~C))%\n%
	FOR /F "tokens=1-3 delims=;" %%A IN (^""%q%iMathR%q%";"%q%iMath_%q%";"COLOR%q%iMathE1%q%"^") DO (%\n%
		ENDLOCAL%\n%
		FOR %%: IN (%%A) DO IF %%: NEQ "" SET %%:%\n%
		SET iMath_=%%~B%\n%
		%%~C)%\n%
) ELSE SET iMathP=

GOTO :m2

:m1
SET xx=Q
SET/P xx=
IF /I "%xx%" NEQ "Q" (
	%iMath% %xx%
	IF ERRORLEVEL 1 ( ECHO Yup ) ELSE ( ECHO Nerp )
	CALL ECHO %%iMath_%% : %%ERRORLEVEL%%
	GOTO :m1
) ELSE SET
IF "!!"=="" ENDLOCAL
EXIT /B 0

:m2
SET rn1=%RANDOM%
SET rn2=%RANDOM%
SET rn3=7%rn1%%RANDOM%
SET rn4=4%rn2%%RANDOM%
ECHO %rn3:~0,9% : %rn4:~0,9%
SET /A x1=%rn3:~0,9% + %rn4:~0,9%
%iMath% x2=%rn3:~0,9% + %rn4:~0,9%
ECHO.%x1% : %x2%
IF "%x1%" NEQ "%x2%" EXIT /B 0
SET /A x1=%rn3:~0,9% - %rn4:~0,9%
%iMath% x2=%rn3:~0,9% - %rn4:~0,9%
ECHO.%x1% : %x2%
IF "%x1%" NEQ "%x2%" EXIT /B 0
SET /A x1=%rn3:~0,6% * %rn4:~0,3%
%iMath% x2=%rn3:~0,6% x %rn4:~0,3%
ECHO.%x1% : %x2%
IF "%x1%" NEQ "%x2%" EXIT /B 0
SET /A x1=%rn3:~0,9% / %rn4:~0,8%
%iMath% x2=%rn3:~0,9% / %rn4:~0,8%
ECHO.%x1% : %x2%
IF "%x1%" NEQ "%x2%" EXIT /B 0
SET /A x1=%rn3:~0,9% %% %rn4:~0,8%
%iMath% x2=%rn3:~0,9% @ %rn4:~0,8%
ECHO.%x1% : %x2%
IF "%x1%" NEQ "%x2%" EXIT /B 0
GOTO :m2
EXIT /B 0
It's the last version before I added the parser and it's much faster without the decimal+shunting-yard overhead. Just thought I'd post it as a curiosity before I eventually delete the earlier versions.

dbenham
Expert
Posts: 2461
Joined: 12 Feb 2011 21:02
Location: United States (east coast)

Re: %MM% v0.2a - a full-featured WinNT math macro

#22 Post by dbenham » 18 Aug 2018 16:48

Regarding v0.2 - Great job, especially with the extensive documentation and in-line comments :!: :D

The addition of the major section header comments at the beginning of the lines really helps get your bearings. And the variable documentation is also great.

All in all - very impressive.


Dave Benham

Meerkat
Posts: 89
Joined: 19 Jul 2015 02:27
Location: Philippines

Re: %MM% v0.2a - a full-featured WinNT math macro

#23 Post by Meerkat » 01 Sep 2018 21:52

Hello! Impressive work!

Maybe this was already addressed. When I open the script from CMD, the nth root works. However, when I open the script from clicking, it aborts.
Output wrote:Finding 2th root of 10 to 4 decimals starting at 3
guess=(((2-1)*3)+(10/3$(2-1)))/2
guess#1=3.1666
guess#2=3.1622
guess#3=3.1622
Error : Abort
Meerkat

Meerkat
Posts: 89
Joined: 19 Jul 2015 02:27
Location: Philippines

Re: %MM% v0.2a - a full-featured WinNT math macro

#24 Post by Meerkat » 22 Jan 2021 03:34

Hi everyone! Using the ideas from the thread viewtopic.php?f=3&t=9872, I propose an edition for the source code of %MM%:

Code: Select all

:: %MM% is a WinNT batch macro that performs mathematical and relational
:: operations on large integers and decimals. It will accept either numerals
:: or variables as operands, supports the parsing of multiple complex in-line
:: expressions, and provides the following operators in order of precedence:
::         |( ) Grouping
:: Highest | '  LogicalNot | ~  BitwiseNot  | -  Negative
::         | $  PowerOf
::         | *  Multiply   | /  Division    | @  Modulo
::         | +  Addition   | -  Subtraction
::         | << LeftShift  | >> RightShift
::         |<=> 3-way Comparison
::         | <  LessThan   | >  GreaterThan | <= LessOrEqual | >= GreaterOrEqual
::         | ## IsEqualTo  | <> NotEqualTo
::         | &  BitwiseAnd > ^  BitwiseXor  > |  BitwiseOr
::         | && LogicalAnd > |& LogicalXor  > || LogicalOr
::         | ?: TernaryIf
:: Lowest  | = += -= *= /= @= $= |= ^= &= <<= >>= Equals/Compound Assignment
::         | ;, Expression Separators
:: 
:: Relation & Logical ops return both value and ERRORLEVEL of 1=True, 0=False.
:: 3-way Comparison operator returns 1 if n1>n2, 0 if n1=n2, or -1 if n1<n2.
:: TernaryIf(?:) = boolean ? returnIfBoolean<>0 : returnIfBoolean==0.
:: Bitwise ops are passed to SET/A, which allows signed 32-bit integers only.
:: Modulo(@) is integer only. PowerOf($) exponent is integer and positive only.
:: Variables may contain 0-9, A-z, []_ only, and first letter can't be 0-9.
:: If a variable's value is undefined or non-numerical, it's treated as 0.
:: To display result use "echo#=" in the expression, where #=num of linefeeds.
:: The result of each expression is always returned in the variable %MM_%.
:: IF ERRORLEVEL 1 IF %MM_%==0 then an error has occurred.
:: 
:: Constants: set these prior to invoking the macro, default if undefined.
:: SET $M#= # of asterisks, tildes, and equal-signs to scan for, default is 16.
::          if insufficient macro will fail without warning, ERRORLEVEL=MM_=0.
:: SET $MD= the maximum number of decimals to return, 2 if undefined.
::          macro is most efficient when $MD+2 is a multiple of 8.
:: SET $MM= expression to execute if %MM% is invoked without parameters. Line
::          input is limited to ~350 characters, use this to input up to ~8000.
:: 
::

@ECHO OFF
SET $M#=
SET $MD=
SET $MM=

:: setup macro
(SET \n=^^^
%= This defines an escaped Line Feed - DO NOT ALTER =%
)

SET MM=FOR %%# IN (1 2)DO IF %%#==2 (%=                      'v0.2a 2018/08/18 =%%\n%
%= =%FOR %%A IN (D G N T U U1 U2 X Y)DO SET $%%A=%=          '$ is used as prefix on all first-tier variables as these can't be passed into the macro, clear intitial values =%%\n%
%= =%SET $Y=4096 2048 1024 512 256 128 64 32 16 8 4 2 1%=    'binary regression for sizeOf/trailingZeros functions =%%\n%
%= =%SET $Z=0000000000000000%=                               'create constants, 9-digit controls, 4096 zeros, operator-precedence table =%%\n%
%= =%FOR %%A IN (1 2 3 4)DO SET $%%A=%%A00000000^&SET $Z=!$Z!!$Z!!$Z!!$Z!%\n%
%= =%FOR %%A IN ("( ," ": )" "T # +# -# \# /# @# $# |# {# &# <<# >>#" "||" "|&" "&&" "|" { "&" "## <>" "< > ># <#" "<#>" "<< >>" "+ -" "\ / @" $ "' } `")DO SET/A $T+=1^&SET "$O!$T!= %%~A "%\n%
%= =%IF "!$MD!"=="" SET $MD=2%=                              '$MD/maxDecimal=maximum #of decimals to return =%%\n%
%= =%IF "!$M#!"=="" SET $M#=16%=                             '$M#=maximum# of asterisks/equal-signs/tildes allowed in input, else macro will fail without warning and MM_=ERRORLEVEL=0, be sure to set this value high enough for your usage =%%\n%
%= =%IF "!$P: =!"==";" SET $P=;!$MM!%=                       'if no input (or only spaces), copy parameters from variable $MM =%%\n%
%= =%SET "$P=!$P:^={!"%=                                     'replace ^ carets(problem char#1) with { left-brace =%%\n%
%= =%SET $P=!$P:^"= !%=                                      'remove double-quotes =%%\n%
%= =%SET $P=!$P:\=/!%=                                       'assume \ backslash is / forwardslash(division), \ backslash is then used for * multiplication =%%\n%
%= =%SET $P=!$P:,=;!%=                                       'assume all expression separators are ; semi-colons, commas are then used as ? ternary-open =%%\n%
%= =%SET $P=!$P:?= , ! %=                                    'replace ? question-marks(problem char#2) with , commas =%%\n%
%= =%FOR %%A IN (+ - / @ $ # : ' { "&" "|" "<" ">" "(" ")" ";")DO SET "$P=!$P:%%~A= %%~A !"%\n%
%= =%FOR /L %%A IN (0,1,!$M#!)DO (%=                         'separate all other operators from operands =%%\n%
%=    =%FOR /F "tokens=1* delims=*" %%B IN ("!$P!")DO IF "%%C" NEQ "" SET "$P=%%B \ %%C"%= 'replace * asterisks(problem char#3) with \ backslash =%%\n%
%=    =%FOR /F "tokens=1* delims==" %%B IN ("!$P!")DO IF "%%C" NEQ "" SET "$P=%%B # %%C"%= 'replace = equal-signs(problem char#4) with # hashmark =%%\n%
%=    =%FOR /F "tokens=1* delims=~" %%B IN ("!$P!")DO IF "%%C" NEQ "" SET "$P=%%B } %%C"%= 'replace ~ tilde(problem char#5) with } right-brace =%%\n%
%=    =%SET $P=!$P:  = !)%=                                  'reduce double-spaces, replace numeric negation(-) with ` and remove unary(+), reassemble multi-char operators =%%\n%
%= =%FOR %%A IN (+ - \ / @ $ # : ' { } "&" "|" ">" "<" "(" ";" ",")DO SET "$P=!$P: %%~A - = %%~A ` !"^&SET "$P=!$P: %%~A + = %%~A !"%\n%
%= =%FOR %%A IN ("< <=<<" "> >=>>" "< >=<>" "< #=<#" "# >=#>" "> #=>#" "# #=##" "+ #=+#" "- #=-#" "\ #=\#" "/ #=/#" "@ #=@#" "$ #=$#" "| #=|#" "& #=&#" "{ #={#" "& &=&&" "| &=|&" "| |=||" "{ {={" "{ {={")DO SET "$P=!$P:%%~A!"%\n%
%= =%SET $R=MM_=%=                                           'initialize $R=return-variable-queue and feed expressions one-at-a-time into loop =%%\n%
%= =%FOR %%: IN (^"!$P: ;=^" ^"!^")DO IF %%: NEQ "" IF DEFINED $R (SET "$E=(%%~: + 0 )"%=  'begin shunting-yard(ish) parser =%%\n%
%=    =%FOR %%A IN (C Q S T V)DO SET $%%A=%=                                               'small cheat by seeding expression with '( exp + 0 )' to greatly simplify parser =%%\n%
%=    =%FOR %%A IN (^"!$E: =^" ^"!^")DO IF %%A NEQ "" IF DEFINED $R (SET/A $O=$K=0%=       'feed ops one-at-a-time into loop =%%\n%
%=       =%FOR /F %%B IN ("!$V!")DO SET $K=%%B%=                                           '$K=peek at value on top of $V=precedence-value-of-operator stack =%%\n%
%=       =%FOR /L %%B IN (1,1,17)DO IF "!$O%%B!" NEQ "!$O%%B: %%~A =!" SET $O=%%B%=        'find $O=operator-precedence of current op =%%\n%
%=       =%IF !$O!==0 (SET $Q=%%~A !$Q!%=                                                  'if no match, then it's an operand, place on top of queue =%%\n%
%=       =%)ELSE IF !$O!==3 (IF !$K! GTR 3 SET $R=^&ECHO Assignment error.^>^&2%=          'only ternary, assignments, and parenthesis are allowed below assignments =%%\n%
%=       =%)ELSE IF !$O! NEQ 1 IF !$O! LSS 16 IF !$O! LEQ !$K! (SET $T=%=                  'if not openParenthesis/openTernary(lowest) nor powerOf/unary(highest) but LEQ top of stack then process stack =%%\n%
%=          =%IF "!$S!"=="!$S:(=!" SET $R=^&ECHO Missing open parenthesis.^>^&2%=          'there should always be open-parenthesis in the stack =%%\n%
%=          =%FOR %%B IN (^"!$S: =^" ^"!^")DO IF %%B NEQ "" IF DEFINED $R (%=              'feed operators from stack one-at-a-time into loop =%%\n%
%=             =%IF !$O! LEQ !$K! (SET/A $C+=1%=                                           '$C=counter for next-queued-variable,if current operator-precedence LEQ top of stack process next operator =%%\n%
%=                =%FOR /F "tokens=1-3*" %%C IN ("!$Q!")DO (%=                             'pop operands from the top of queue =%%\n%
%=                   =%IF !$K!==17 (SET $A=%%C %%C^&SET $B=%%D %%E %%F^&IF "%%C"=="" SET $R=%=   'if unary operator =%%\n%
%=                   =%)ELSE IF %%~B==T (SET $A=%%D %%C %%E^&SET $B=%%F^&IF "%%E"=="" SET $R=%=  'if ternary operator =%%\n%
%=                   =%)ELSE SET $A=%%D %%C^&SET $B=%%E %%F^&IF "%%D"=="" SET $R=%=              'if binary operator =%%\n%
%= Start Math        =%IF DEFINED $R (SETLOCAL%=                                           'if operands are good,SETLOCAL and start math sequence =%%\n%
%= Capture Operands     =%FOR %%I IN (!$A!)DO FOR /F "tokens=* delims=-0123456789." %%J IN ("%%I")DO (SET/A $D+=1%= '$D=counter for operands =%%\n%
%=                         =%IF "%%J"=="" (SET $N!$D!=%%I)ELSE SET $N!$D!=!%%I!)%=         'determine if number or variable,capture values =%%\n%
%=                      =%FOR %%I IN (g n n1 n2 n3 u u1 u2 v)DO SET %%I=!$%%I!%=           'now it's OK to use variables other than $, set/clear new values =%%\n%
%=                      =%SET/A i=1,a=f=f1=q=s11=s12=s21=s22=t=t1=t2=t3=w=0%=              'clear start values =%%\n%
%=                      =%SET "p=%%~B"%=                                                   'capture current operator-symbol =%%\n%
%= Discover Operands    =%FOR /L %%I IN (1,1,!$D!)DO (IF "!n%%I:~0,1!"=="-" SET n%%I=!n%%I:~1!^&SET u%%I=-%=                 'separate unary from value =%%\n%
%=                         =%FOR /F "tokens=* delims=0123456789." %%J IN ("!n%%I!")DO IF "%%J" NEQ "" SET n%%I=!n%%I:%%J=!%= 'remove non-numerical portion of value =%%\n%
%=                         =%FOR /F "tokens=1-2 delims=." %%J IN ("0!n%%I!")DO FOR /F "tokens=* delims=0" %%L IN ("%%J")DO SET o%%I1=%%L^&SET o%%I2=%%K%\n%
%=                         =%IF "!o%%I1!"=="" SET o%%I1=0%=                                'capture integer+decimal portions of value, remove leading zeros, check for zero =%%\n%
%=                         =%SET o%%I=!o%%I1!!o%%I2!%=                                     'set o1/o2/o3=assembled non-padded values =%%\n%
%=                         =%FOR %%J IN (1 2)DO (SET t=!o%%I%%J!0%=                        'find length of integer/decimal portion of each value =%%\n%
%=                            =%FOR %%K IN (!$Y!)DO IF "!t:~%%K,1!" NEQ "" SET/A s%%I%%J+=%%K^&SET t=!t:~%%K!)%\n%
%=                         =%SET/A s%%I=s%%I1+s%%I2)%=                                     'set total length(s=size) of each value, then set padded values(m1=maxIntLength,m2=maxDecLength,n1/n2=padded-values) =%%\n%
%=                      =%FOR %%I IN (1 2)DO SET/A"m%%I=(s1%%I+7)/8*8"^&IF !s2%%I! GTR !s1%%I! SET/A"m%%I=(s2%%I+7)/8*8"%\n%
%=                      =%FOR %%I IN (1 2)DO FOR /F "tokens=1*" %%J IN ("!m1! !m2!")DO SET n%%I=!$Z!!o%%I1!^&SET t=!o%%I2!!$Z!^&SET n%%I=!n%%I:~-%%J!!t:~0,%%K!%\n%
%=                      =%IF "!n1!" GTR "!n2!" (SET q=1)ELSE IF "!n1!" LSS "!n2!" SET q=-1%= 'determine q=3-way compare=if abs(n1) GTR abs(n2) =%%\n%
%= Compound Assignment  =%FOR %%I IN (+ - \ / @ $ { "|" "&" "<<" ">>")DO IF %%B=="%%~I#" SET y=%%D^&SET "p=%%~I"%= 'check for compound assignments, if found, correct op and set y=return variable =%%\n%
%=                      =%SET/A d=m2,h=m1+m2,z=$MD+2%=                                     'd=decimalPlace=max padded fractional length,h=maxLength of padded int + fraction values =%%\n%
%=                      =%IF !p!==+ IF "!u2!" NEQ "!u1!" SET u2=!u1!^&SET p=-%\n%
%=                      =%IF !p!==- IF "!u2!" NEQ "!u1!" SET u2=!u1!^&SET p=+%=            'only add/subtract values with matching unaries =%%\n%
%= (`)Negative          =%IF !p!==` SET n=!n1!^&IF "!u1!"=="" SET u=-%=                    'negative - numeric negation =%%\n%
%= (#)Assignment        =%IF !p!==# SET y=%%D^&SET u=!u2!^&SET n=!n2!%=                    'assignment - set y=return variable=operand2,u=unary of answer=same as value2,n=final answer=same as value2 =%%\n%
%= (T)Ternary If        =%IF !p!==T SET u=!u1!^&SET n=!n1!^&IF !o3! EQU 0 SET u=!u2!^&SET n=!n2!%= 'ternary - set u=n=value1, if third-value=0, set u=n=value2 =%%\n%
%= (+)Addition          =%IF !p!==+ (SET u=!u1!%=                                          'addition - group values by 8 digits, add values, collect carry, assemble n=answer =%%\n%
%=                         =%FOR /L %%I IN (8,8,!h!)DO SET/A t=1!n1:~-%%I,8!+1!n2:~-%%I,8!+w,w=t/$3^&SET n=!t:~1!!n!%\n%
%=                         =%SET n=!w!!n!)%=                                               'collect carry =%%\n%
%= (-)Subtraction       =%IF !p!==- (%=                                                    'subtraction - only subtract lesser from greater, if n1>=n2 set u=u1=unary of value1, else swap n1 with n2 and set u=negative if u1=positive, then same procedure as addition without the carry =%%\n%
%=                         =%IF !q! GEQ 0 (SET u=!u1!)ELSE SET t=!n1!^&SET n1=!n2!^&SET n2=!t!^&IF "!u1!"=="" SET u=-%\n%
%=                         =%IF !q! NEQ 0 FOR /L %%I IN (8,8,!h!)DO SET/A t=3!n1:~-%%I,8!-1!n2:~-%%I,8!+w,w=t/$2-1^&SET n=!t:~1!!n!)%\n%
%=                      =%IF "!n!"=="" SET d=0%=                 'above operations require d=m2, all others require d=0, if n="" then no operations have been performed =%%\n%
%= (@)Modulo            =%IF !p!==@ SET p=/^&SET f1=1%=          'modulo - set op=division and f1/MODflag=1 =%%\n%
%= (/)Division          =%IF !p!==/ (SET/A s11+=s22,s12-=s22%=   'division+modulo - set s11/size1_int+=size2_dec,s12/size1_dec-=size2_dec =%%\n%
%=                         =%IF !s12! LSS 0 SET s12=0%=          'if size1_dec<0 set to 0 =%%\n%
%=                         =%SET/A s1=s11+s12,j=s11+z%=          's1/length_of_value1=size1_int+size1_dec,j/#of divisions=size1+$maxDecimal =%%\n%
%=                         =%SET w=!$Z!%=                        'w=remainder=$zeros =%%\n%
%=                         =%SET o1=0!o1!!$Z!%=                  'o1/non-padded value1=lead0~o1~$zeros =%%\n%
%=                         =%IF !f1!==1 SET j=0%=                'above will move decimalPlace and set j=#of divisions, if f1/MODflag=1 set j=0 because integer only =%%\n%
%=                         =%IF !j! LSS !s1! SET/A j=s1%=        'if j LSS s1/size1=length of value1 then j=size1 ie. no fraction/integer only =%%\n%
%=                         =%IF "!u1!" NEQ "!u2!" SET u=-%=      'if unaries don't match answer is negative =%%\n%
%=                         =%FOR %%I IN (!h!)DO SET n2=!$Z!!o2!^&SET n2=!n2:~-%%I!%=       'set n2/divisor=corrected/padded value2 =%%\n%
%=                         =%IF !q!==0 (SET n=1)ELSE IF !n2! EQU 0 (SET g=X^&ECHO Divide by zero error.^>^&2%\n%
%=                         =%)ELSE IF !o1! NEQ 0 FOR /L %%I IN (1,1,!j!)DO (SET t=0%=      'reset t/answer digit=0 and d/decimalPlace+=1 if GTR lengthOfDividend =%%\n%
%=                            =%IF %%I GTR !s11! SET/A d+=1%=                              'add next digit from dividend to remainder and crop to maxSize for comparison with divisor =%%\n%
%=                            =%FOR %%J IN (!h!)DO SET w=!w!!o1:~%%I,1!^&SET w=!w:~-%%J!%\n%
%=                            =%IF "!w!" GEQ "!n2!" FOR /L %%J IN (1,1,9)DO IF "!w!" GEQ "!n2!" (SET/A t+=1,t2=0%\n%
%=                               =%SET t3=!w!%=                                            'if w/remainder GTR n2/divisor then w-=n2 until it's not, t=answer digit=#of subtractions until w LSS n2 =%%\n%
%=                               =%SET w=%\n%
%=                               =%FOR /L %%K IN (8,8,!h!)DO SET/A t1=3!t3:~-%%K,8!-1!n2:~-%%K,8!+t2,t2=t1/$2-1^&SET w=!t1:~1!!w!)%\n%
%=                            =%SET n=!n!!t!)%=                                            'add t=answer digit to n/quotient/final answer =%%\n%
%=                         =%IF !f1!==1 SET u=!u1!^&SET n=!w!)%=                           'if f1/MODflag=1 set u=unary of dividend,n/answer=w/integer remainder =%%\n%
%= ($)Power Of          =%IF !p!==$ (SET p=\^&SET u2=%=                                    'powerOf - set op=multiply, check for integer/0/1 exponent =%%\n%
%=                         =%IF !s22! NEQ 0 SET o2=0^&SET g=X^&ECHO Non-integer exponent.^>^&2%\n%
%=                         =%IF !o1! LEQ 1 SET o2=0%=                                      'i=o2=0 will effectively cancel multiplication routine =%%\n%
%=                         =%SET/A i=o2,a=i/3+1,t=i%%2,n=o2=s2=1%=                         'set i=exponent,a=#of loop iterations,t=even/odd exponent,answer=non-padded value2=length of value2=1 =%%\n%
%=                         =%IF !t!==0 SET u1=)%=                                          'if exponent is even clear unary of value1, as answer will be positive =%%\n%
%= (\)Multiplication    =%IF !p!==\ (IF "!u1!" NEQ "!u2!" SET u=-%=                        'multiplication+powerOf - if unaries don't match answer is negative =%%\n%
%=                         =%FOR /L %%H IN (0,1,!a!)DO IF !i! NEQ 0 (SET/A t2=i%%2,i/=2%=  'if exponent NEQ 0 capture odd/even and halve exponent =%%\n%
%=                            =%IF !t2!==1 (SET n=%=                                       'n/answer=NULL =%%\n%
%=                               =%SET n2=0000000!o2!%=                                    'n2=0000000non-padded value2 =%%\n%
%=                               =%SET/A"d=s12+s22,h=(s2+7)/8*8"%=                         'set decimalPlace=size1_dec+size2_dec,maxSize=largest group of 8 =%%\n%
%=                               =%FOR /L %%I IN (1,1,!h!)DO SET _%%I=0%=                  'clear carry columns =%%\n%
%=                               =%FOR /L %%I IN (1,1,!s1!)DO (SET/A w=t1=0,c=%%I%=        'multiply each digit of o1 by n2 grouped by 8 =%%\n%
%=                                  =%FOR /L %%J IN (8,8,!h!)DO SET/A t=!o1:~-%%I,1!,t=t*1!n2:~-%%J,8!+w-t*$1,w=t/$1,_!c!+=t%%$1,c+=8%\n%
%=                                  =%SET _!c!=!w!)%=                                      'collect carry into the next column =%%\n%
%=                               =%FOR /L %%I IN (1,1,!c!)DO SET/A _%%I+=t1,t1=_%%I/10^&SET n=!_%%I:~-1!!n!%\n%
%=                               =%SET n=!t1!!n!)%=                                        'add all columns together, attach remainder to product =%%\n%
%=                            =%IF !i! NEQ 0 (SET n1=%=                                    'if exponent NEQ 0 square n1 by setting n2=n1 and repeating as above saving product in n1 =%%\n%
%=                               =%SET n2=0000000!o1!%\n%
%=                               =%SET/A"s12*=2,s22=d,h=(s1+7)/8*8"%\n%
%=                               =%FOR /L %%I IN (1,1,!h!)DO SET _%%I=0%\n%
%=                               =%FOR /L %%I IN (1,1,!s1!)DO (SET/A w=t1=0,c=%%I%\n%
%=                                  =%FOR /L %%J IN (8,8,!h!)DO SET/A t=!o1:~-%%I,1!,t=t*1!n2:~-%%J,8!+w-t*$1,w=t/$1,_!c!+=t%%$1,c+=8%\n%
%=                                  =%SET _!c!=!w!)%\n%
%=                               =%FOR /L %%I IN (1,1,!c!)DO SET/A _%%I+=t1,t1=_%%I/10^&SET n1=!_%%I:~-1!!n1!%\n%
%=                               =%SET n1=!t1!!n1!^&SET n2=!n!%=                           'n1=new square,n2=current answer, remove leading zeros =%%\n%
%=                               =%FOR %%I IN (1 2)DO FOR /F "tokens=* delims=0" %%J IN ("!n%%I!")DO (SET o%%I=%%J%\n%
%=                                  =%SET/A s%%I=0,t=s%%I2-z%=                             'clear size,set t=size#_dec - $maxDecimal =%%\n%
%=                                  =%IF !t! GTR 0 FOR %%K IN (!t!)DO SET/A s%%I2=z^&SET o%%I=!o%%I:~0,-%%K!%\n%
%=                                  =%SET t=!o%%I!0%=                                      'crop values to $maxDecimal,determine new length =%%\n%
%=                                  =%FOR %%K IN (!$Y!)DO IF "!t:~%%K,1!" NEQ "" SET/A s%%I+=%%K^&SET t=!t:~%%K!))))%\n%
%= Relational Ops       =%IF DEFINED u1 (IF DEFINED u2 (SET/A q*=-1)ELSE SET q=-1)ELSE IF DEFINED u2 SET q=1%= 'q=abs(n1)>abs(n2), correct using unaries u1+u2 =%%\n%
%=                      =%IF !p!==## IF !q!==0 SET f=1%=                       'relational operators =%%\n%
%=                      =%IF %%B==">" IF !q! GTR 0 SET f=1%=                   'f=flag=only set to 1(true) by logic and relational operators =%%\n%
%=                      =%IF %%B=="<" IF !q! LSS 0 SET f=1%\n%
%=                      =%IF %%B==">#" IF !q! GEQ 0 SET f=1%\n%
%=                      =%IF %%B=="<#" IF !q! LEQ 0 SET f=1%\n%
%=                      =%IF %%B=="<>" IF !q! NEQ 0 SET f=1%\n%
%=                      =%IF %%B=="<#>" SET/A n=q%\n%
%= Logical Ops          =%IF !p!==' IF !o1! EQU 0 SET f=1%=                    'logical operators - not =%%\n%
%=                      =%IF %%B=="||" IF !o1!!o2! NEQ 0 SET f=1%=             'or =%%\n%
%=                      =%IF %%B=="&&" IF !o1! NEQ 0 IF !o2! NEQ 0 SET f=1%=   'and =%%\n%
%=                      =%IF %%B=="|&" IF !o1! NEQ 0 (IF !o2! EQU 0 SET f=1)ELSE IF !o2! NEQ 0 SET f=1%= 'xor =%%\n%
%=                      =%IF !f!==1 SET n=1^&SET g=ER0%=                       'if flag1=1 set n=answer=true=1 and ErrLvl=1 =%%\n%
%= Bitwise Ops          =%SET o1=!u1!!o1!^&SET o2=!u2!!o2!%=                   'bitwise operators =%%\n%
%=                      =%IF !p!==} SET/A n=~o1%=                              'passed directly to SET/A using variables as operands, so it never errors? =%%\n%
%=                      =%IF !p!=={ SET/A"n=o1^o2"%=                           'supports 32-bit signed integers only =%%\n%
%=                      =%IF %%B=="|" SET/A"n=o1|o2"%=                         'values above  2147483647 are treated as  2147483647 =%%\n%
%=                      =%IF %%B=="&" SET/A"n=o1&o2"%=                         'values below -2147483648 are treated as -2147483648 =%%\n%
%=                      =%IF %%B=="<<" SET/A"n=o1<<o2"%\n%
%=                      =%IF %%B==">>" SET/A"n=o1>>o2"%\n%
%=                      =%SET/A z-=2%\n%
%= Finalize Value       =%IF !d! NEQ 0 (FOR %%I IN (!d!)DO FOR %%J IN (!z!)DO SET n=!n:~0,-%%I!.!n:~-%%I,%%J!%\n%
%=                         =%FOR %%I IN (!$Y!)DO IF "!n:~-%%I!"=="!$Z:~-%%I!" SET n=!n:~0,-%%I!%= 'remove trailing zeros =%%\n%
%=                         =%IF "!n:~-1!"=="." SET n=!n:~0,-1!)%=                          'if d GTR 0 answer is decimal, place decimal in n cropping to $maxDecimal, if last character is decimal remove it =%%\n%
%=                      =%FOR /F "tokens=* delims=0" %%I IN ("!n!")DO SET n=%%I%=          'remove leading zeros =%%\n%
%=                      =%IF "!n!"=="" SET n=0%=                                           'if n=NULL set to 0 =%%\n%
%=                      =%IF !n! NEQ 0 SET n=!u!!n!%=                                      'if n/answer=non-zero attach unary =%%\n%
%=                      =%IF "!g!" NEQ "X" IF /I "!y:~0,4!"=="ECHO" (^<NUL SET/P=!n!%=     'if no error check for ECHO command =%%\n%
%=                         =%FOR /L %%J IN (1,1,!y:~4!)DO ECHO.%\n%
%=                         =%SET y=)%=                                         'return values from 2nd-tier SETLOCAL =%%\n%
%= Return Value         =%FOR /F "tokens=1-3 delims=;" %%I IN (^""!y!";"!n!";"!g!"^")DO (ENDLOCAL%\n%
%=                         =%IF %%I NEQ "" SET %%~I=%%~J^&SET $R=!$R!" "%%~I=%%~J%=        'if return variable present SET value and add to return-varible-queue =%%\n%
%=                         =%SET $_!$C!=%%~J%=                                             'SET value of current queued variable =%%\n%
%=                         =%SET $X=%%~K)%=                                                'SET exitcode =%%\n%
%= Resume Parser        =%IF "!$X!"=="X" SET $R=%=                             'if exitcode=X then ERROR, clear $R as halt-flag for FOR loops =%%\n%
%=                      =%SET $Q=$_!$C! !$B!%=                                 'push newly computed value on top of queue =%%\n%
%=                   =%)ELSE ECHO Missing operand.^>^&2)%=                     'otherwise operand not found =%%\n%
%=                =%FOR /F "tokens=2*" %%C IN ("!$V!")DO SET $K=%%C^&SET $V=%%C %%D%= 'pop and peek at the value stack =%%\n%
%=             =%)ELSE SET "$T=!$T! %%~B")%=                                   'rebuild remainder of symbol stack =%%\n%
%=          =%SET $S=!$T!)%=                                                   'reset stack to remainder of stack =%%\n%
%=       =%IF !$O!==2 (FOR /F "tokens=1*" %%C IN ("!$V!")DO SET $V=%%D%=       'if current op is close parenthesis/ternary =%%\n%
%=          =%FOR /F "tokens=1*" %%C IN ("!$S!")DO (SET "$S=%%D"%=             'pop both stacks, check for ternary(push new operator onto stacks) or type mismatch(error if found) =%%\n%
%=             =%IF "%%C"=="," (IF %%A==":" (SET $S=T !$S!^&SET $V=3 !$V!)ELSE SET $R=^&ECHO Missing ternary close.^>^&2)ELSE IF %%A==":" SET $R=^&ECHO Missing ternary open.^>^&2)%\n%
%=       =%)ELSE IF !$O! GTR 0 SET "$S=%%~A !$S!"^&SET $V=!$O! !$V!)%=         'else if current op is not an operand, push to top of stacks =%%\n%
%=    =%IF DEFINED $R (SET $S= !$S!%=                                          'if no errors check for empty stack and single item in operand queue =%%\n%
%=       =%IF "!$S: =!" NEQ "" (SET $R=^&ECHO Missing close parenthesis.^>^&2%='if symbol stack is not empty =%%\n%
%=       =%)ELSE FOR /F "tokens=1*" %%A IN ("!$Q!")DO (%\n%
%=          =%IF "%%~B" NEQ "" SET $R=^&ECHO Missing operator.^>^&2%=          'if more than one item in queue =%%\n%
%=          =%SET MM_=!%%~A!)))%=                                              'set MM_=final value for expression =%%\n%
%= =%IF "!$R!"=="" (SET $R=MM_=^&SET MM_=0^&SET $X=%=                          'if error set ErrLvl=1 and MM_=0 =%%\n%
%= =%) ELSE IF "!$X!"=="ER0" (SET $X=) ELSE (SET $X= )%=                       'added by Meerkat =%%\n%
%= =%FOR /F "tokens=1-3 delims=;" %%A IN (^""!$R!";"!MM_!";"CALL!$X!"^")DO (ENDLOCAL%= 'exit macro and return values =%%\n%
%=    =%FOR %%: IN (%%A)DO SET %%:%=                                           'process return-variable-queue =%%\n%
%=    =%SET MM_=%%~B%=                                                         'set MM_=final value of last expression =%%\n%
%=    =%%%~C)%=                                                                'set ERRORLEVEL=0/1 =%%\n%
)ELSE SETLOCAL EnableDelayedExpansion^&SET $P= ;%= [returnVar[+,-,*,/,@,$,|,^,&,<<,>>]= [ ...]] [boolean ?] ['~-]operand1 [+,-,*,/,@,$,<,>,<#,>#,##,<>,<#>,|,^,&,<<,>>,||,|&,&& operand2] [;exp2 [,exp3 [ ...up to ~300 characters]]] =%

:: Parser Variables:
::  $#= 9-digit control numbers used by +-*/ to stack 8-digits per iteration using SET/A
::  $A= operands that have been pulled from the queue for the current operation
::  $B= remainder of the operand queue
::  $C= variable counter for newly-generated operands, as in SET "$_!$C!=new value"
::  $D= variable counter for the currently-captured operands
::  $E= current expression being parsed, padded with "($E+0)" to simplify the parser
::  $K= precedence value of operator on top of stack
:: $N#= value of each currently-captured operand
::  $O= precedence value of current operator
:: $O#= operator symbols numbered by precedence, used to identify the current operator
::  $P= input parameters, replaced with contents of $MM if empty (or only spaces)
::  $Q= current operand queue
::  $R= return variable queue, list of double-quote separated "variable=value" pairs, also used as a flag to halt macro
::  $S= current operator stack, symbols
::  $T= temp variable
::  $V= current operator stack, precedence values
::  $X= exit code, space=0, empty=1, X=error
::  $Y= binary regression from 4096 to 1 used to find string-length and remove trailing zeros
::  $Z= 4096 zeros used to pad values to equal lengths, needed for compare, add, and subtract
:: 
:: Math Variables:
::   a= # of iterations to perform in multiplication/powerOf, starting at 0
::   c= column counter used during multiplication, steps by 8
::   d= decimal position for the final return value
::   f= boolean flags, f=logical/relational op is True, f1=Modulo in Division
::   g= exit code for 2nd-tier SETLOCAL, undefined=0, ER0=1, X=error
::   h= maximum size of zero-padded values, always a multiple of 8, used to group SET/A operations together (8x fewer iterations)
::   i= powerOf exponent, initialize i=1 to allow multiplication
::   j= # of divisions to perform, based on position of decimal and $MD=maxDecimal to return
::  m#= maximum size of zero-padded values, always a multiple of 8, 1=integer portion, 2=decimal portion
::   n= final value for the current operation
::  n#= zero-padded values for the current operands, decimal removed
::  o#= non-padded values for the current operands, decimal removed
:: o##= non-padded values, 2nd # is 1=integer portion, 2=decimal portion
::   p= current operand symbol, used to select routine
::   q= 3-way compare of absolute values of n1<=>n2, for subtraction and relational
::  s#= size (length) of the current operands
:: s##= size (length), 2nd # is 1=integer portion, 2=decimal portion
::   t= temp variables, t,t1,t2,t3
::   u= operand unaries (only -), u=current result, u#=current operands
::   w= also a temp variable, changed to a single letter to save ~70 characters
::   y= name of user return variable
::   z= $MD+2 adjusted maxDecimal. Used to increase accuracy of truncated decimal operations, better approximates rounding.
::

:mm_help
TITLE Press ENTER to skip...
SET base=1
SET "EKO=<NUL SET/P="
FOR %%# IN ($MD last root guess cnt xx)DO SET %%#=
FOR /F "usebackq tokens=* delims=:" %%# IN ("%~f0")DO IF "%%#"=="" (SET base=)ELSE IF DEFINED base ECHO. %%#
ECHO example: NthRoot convergence (((root-1)*guess)+(base/guess$(root-1)))/root
SET /P "base=enter a base to find the root of (positive decimal): "
IF DEFINED base SET /P "root=enter the NthRoot to find (positive integer): "
IF DEFINED root SET /P "$MD=enter the accuracy (# of decimal places): "
IF DEFINED $MD SET /P "guess=enter your best guess (positive decimal): "
IF NOT DEFINED guess SET $MD=& ECHO. & ECHO okay, maybe try some expressions? max decimal is 8. & GOTO :mm_h2
ECHO. & ECHO Finding %root%th root of %base% to %$MD% decimals starting at %guess%
ECHO guess=(((%root%-1)*%guess%)+(%base%/%guess%$(%root%-1)))/%root%
TITLE Finding %root%th root of %base% to %$MD% decimals starting at %guess%

:mm_h1
SET /A cnt+=1
SET last=%guess%
%EKO%guess#%cnt%=
%MM% "echo1=guess=(((root-1)*guess)+(base/guess$(root-1)))/root,guess<>last"
IF ERRORLEVEL 1 IF %MM_% NEQ 0 ( GOTO :mm_h1 )ELSE ECHO Error : Abort & GOTO :mm_h2
ECHO. & ECHO checking:
%EKO%guess$%root%=
%MM% cnt-=1,echo1=guess$root
ECHO That's %$MD% decimals of accuracy in %cnt% trys.
ECHO.
ECHO now, try some expressions. max decimal is %$MD%.

:mm_h2
TITLE Press ENTER to exit...
SET xx=
SET/P xx=
IF NOT DEFINED xx (IF "!!"=="" ENDLOCAL) & TITLE & EXIT /B 0
%MM% %xx%
IF ERRORLEVEL 1 ( IF %MM_%==0 ( ECHO Error : MM_=%MM_%
	)ELSE ECHO True : MM_=%MM_%
)ELSE ECHO False : MM_=%MM_%
GOTO :mm_h2
The edit is about my comment just above this: when I open the script from clicking, it aborts. My main change is found at the very end of the macro:

Code: Select all

%= =%IF "!$R!"=="" (SET $R=MM_=^&SET MM_=0^&SET $X=%=                          'if error set ErrLvl=1 and MM_=0 =%%\n%
%= =%) ELSE IF "!$X!"=="ER0" (SET $X=) ELSE (SET $X= )%=                       'added by Meerkat =%%\n%
%= =%FOR /F "tokens=1-3 delims=;" %%A IN (^""!$R!";"!MM_!";"CALL!$X!"^")DO (ENDLOCAL%= 'exit macro and return values =%%\n%
%=    =%FOR %%: IN (%%A)DO SET %%:%=                                           'process return-variable-queue =%%\n%
%=    =%SET MM_=%%~B%=                                                         'set MM_=final value of last expression =%%\n%
%=    =%%%~C)%=                                                                'set ERRORLEVEL=0/1 =%%\n%
The CALL command is now used to set errorlevel, instead of the COLOR command used previously. This also comes with the added benefit of speed; see the above linked thread for more details.

Meerkat

Post Reply