Welcome to the Second Life Forums Archive

These forums are CLOSED. Please visit the new forums HERE

Very-Calc - Programmable RPN calculator

Very Keynes
LSL is a Virus
Join date: 6 May 2006
Posts: 484
08-12-2007 14:36
Very-Calc is a minimalist yet powerful calculator script.

Minimalist in that only the operators + - * / ^ and the functions log, ln, sin, cos, tan, asin, and acos are directly coded into the calculator.

However, it is powerful in that the user may define functions, in terms of these operators, that become part of the calculator and may, in turn, be used in future definitions.

The benefit is a small footprint script that may be tailored to the users needs, by the user, without any knowledge of LSL and the 25 basic functions are easy to learn (RPN may take a little longer but is worth it). Over time very-calc becomes more personalised and more powerful. Including the sampel definitans and variables over 10K is still available to the user, this should be enougth for quite a few user defined functions.

It is stack based and uses RPN (reverse polish notation), in addition to the stack users may declare variables by using the command sto name, and may reference such variables by using the name in place of a value.

It listens on /7 for user commands this may be changed in the first line of code pubch = 7;

More complete documentation follows the listing.

CODE

//Very-Calc - Programmable RPN calculator - Very Keynes 2007.08.12

integer pubch = 7;
integer opcode;
integer prgctr;
integer crdlin;
float x; float y; float z; float t; float s;
string curcmd;
list cmdlin;
list sysdic = ["+","-","*","/","^","def","sto","swap","dup","cat","mem","lbl","gto","ifx",
"log","ln","sin","cos","tan","asin","acos","stk","x=y","x<y","x>y"];
list usrdic = [];
list varlst = ["pi",PI,"e",2.71828182845904523536];
define(string name){usrdic = [name] + llDumpList2String(llList2List(cmdlin,prgctr,-1)," ") + usrdic;}
store(string name)
{
integer pos=llListFindList(varlst,[name]);
if(!~pos){varlst = [name,x] + varlst;}
else {varlst = llListReplaceList(varlst,[x],++pos,pos);}
}

string stack(){return "x="+(string)x+" y="+(string)y+" z="+(string)z+" t="+(string)t+" prgctr="+(string)prgctr +" opcode="+(string)opcode;}

exe(integer opcode)
{
if(0 == opcode){x=y+x;y=z;z=t;return;} //add
if(1 == opcode){x=y-x;y=z;z=t;return;} //sub
if(2 == opcode){x=y*x;y=z;z=t;return;} //mul
if(3 == opcode){x=y/x;y=z;z=t;return;} //div
if(4 == opcode){x=llPow(y,x);y=z;z=t;return;} //pow
if(5 == opcode){define(llList2String(cmdlin,prgctr++));return;} //def
if(6 == opcode){store( llList2String(cmdlin,prgctr++));return;} //sto
if(7 == opcode){s=x;x=y;y=s;return;} //swp
if(8 == opcode){t=z;z=y;y=x;return;} //dup
if(9 == opcode){output("user code = "+llList2CSV(usrdic));return;}//cat
if(10== opcode){output("variables = "+llList2CSV(varlst)+" Heap="+(string)llGetFreeMemory());return;} //mem
if(11== opcode){prgctr++;return;} //lbl
if(12== opcode){prgctr=llListFindList(cmdlin,["lbl",llList2String(cmdlin,prgctr)])+2;return;} //gto
if(13== opcode){prgctr++;if(x)prgctr=llListFindList(cmdlin,["lbl",llList2String(cmdlin,--prgctr)])+2;return;} //ifx
if(14== opcode){x=llLog10(x);return;} //log
if(15== opcode){x=llLog(x);return;} //ln
if(16== opcode){x=llSin(x);return;} //sin
if(17== opcode){x=llCos(x);return;} //cos
if(18== opcode){x=llTan(x);return;} //tan
if(19== opcode){x=llAsin(x);return;} //asin
if(20== opcode){x=llAcos(x);return;} //acos
if(21== opcode){output(stack());return;} //stk
if(22== opcode){prgctr++;if(x==y)prgctr=llListFindList(cmdlin,["lbl",llList2String(cmdlin,--prgctr)])+2;return;} //x=y
if(23== opcode){prgctr++;if(x<y)prgctr=llListFindList(cmdlin,["lbl",llList2String(cmdlin,--prgctr)])+2;return;} //x<y
if(24== opcode){prgctr++;if(x>y)prgctr=llListFindList(cmdlin,["lbl",llList2String(cmdlin,--prgctr)])+2;return;} //x>y
}

string result(string input)
{
prgctr=0;
cmdlin = llParseString2List(input,[" "],[""]);
while(prgctr < llGetListLength(cmdlin))
{
curcmd = llList2String(cmdlin,prgctr++);
opcode = llListFindList(sysdic,[curcmd]);
if(~opcode){exe(opcode);}
else if(~(opcode = llListFindList(varlst,[curcmd]))){t=z;z=y;y=x;x=llList2Float(varlst,++opcode);}
else if(~(opcode = llListFindList(usrdic,[curcmd])))
{cmdlin=llListReplaceList(cmdlin,llParseString2List(llList2String(usrdic,++opcode),[" "],[""]),--prgctr,prgctr);}
else{t=z;z=y;y=x;x=(float)curcmd;}
//output(stack());
}
return (string)x;
}

output(string message){llSay(0,message);}

default
{
state_entry()
{
llListen(pubch,"",NULL_KEY,"");
llGetNotecardLine("calcdef",crdlin++);
}
listen(integer channel, string name, key id, string message)
{
output(result(message));
}
dataserver(key requested, string data)
{
output("reading notecard line = "+data);
if(data != "EOF")
{
if(llGetSubString(data,0,1)!="//")output(result(data));
llGetNotecardLine("calcdef",crdlin++);
}
}
}


//Very-Calc - Programmable RPN calculator - Very Keynes 2007.08.12

Function List:

Maths Functions:

+ consumes the top 2 stack elements and returns the result (y x -- y+x)
- consumes the top 2 stack elements and returns the result (y x -- y-x)
* consumes the top 2 stack elements and returns the result (y x -- y*x)
/ consumes the top 2 stack elements and returns the result (y x -- y/x)
^ consumes the top 2 stack elements and returns the result (y x -- y^x)

log returns the log base 10 of x
ln returns the natural log of x

sin returns sin(x)
cos returns cos(x)
tan returns tan(x)

asin returns the arcsine of x
acos returns the arccosine of x

Stack Functions:

swap exchanges the x and y values ( y x – x y )
dup duplicates the x value ( y x – y x x)

Flow control:

lbl label set a branch target
gto label always branch to label
if label if x not = zero branch to label
x=y label if x = y branch to label
x<y label if x < y branch to label
x>y label if x > y branch to label

Programming:

sto name creates or sets a variable of name with value in x
def name creates a user defined function, name, containing the rest of the current command line. To use a function or variable just execute the name i.e.

5 sto radius
def area 2 ^ pi *
radius area
results in 78.53982

output:
stk display the stack contents in the format –
x=2.00000 y=3.00000 z=4.00000 t=5.00000 prgctr=1 opcode=21

mem display user variables in the format –
variables = radius, 5.00000, pi, 3.14159265358979, e, 2.71828182845905 Heap=10667

cat display the user definitions in the format –
user code = area, 2 ^ pi *, 1/x, 1 swp /, area, sqr pi *, sqr, 2 ^, root, .5 ^

Initialisation:
On start-up or reset the calculator will read a notecard called calcdef and execute all lines up to an EOF statement or mark.
Comment lines starting with // are ignored. This allows the user to customise the calculator such that only the functions required for a task are loaded, or to avoid having to retype frequently used functions.
I have included some basic Math functions and some Time Value of Money functions (as I am not an accountant I cant personaly vouch for the accuarcy of the latter, they are for example only)
Example:

CODE

//as definitions are executed at compile time pre initalise the stack
1 1 1 1
//basic functions
def root .5 ^
def sqr 2 ^
def area sqr pi *
def 1/x 1 swap /
//financial functions
// declare Financial Variables
1 sto pv sto fv sto r sto n sto A sto g
def % 100 /
// Rule of 72
def R72 % 1 + ln 2 ln swap /
// Present value of a future sum
def PV fv r % 1 + n ^ / sto pv
// Future value of a present sum
def FV pv r % 1 + n ^ * sto fv
// Present value of an annuity
def PVA 1 1 r % + n ^ 1/x - r / A *
// Future value of an annuity
def FVA 1 r % + n ^ 1 - r / A *
// Present value of a growing annuity
def PVGA 1 g + 1 r % + / n ^ 1 swap - r g - A swap / *
//engineering functions
//clear the stack
0 0 0 0
EOF
Card is not read beyond the line above so the rest of the card can be used to document the functions.

example of use:
Example 1: Present value
One hundred euros to be paid 1 year from now, where the expected rate of return is 5% per year, is worth in today's money:

5 sto r 100 sto fv PV
very-calc: 95.238098

So the present value of €100 one year from now at 5% is €95.23.





I hope you have as much fun using it as I did developing it.
Any feedback regarding optimization and application packs (user functions) would be greatly appreciated.
Lyn Mimistrobell
(waiting)
Join date: 11 Jan 2007
Posts: 179
08-13-2007 10:18
I created a calculator the cheapest way possible... Send the formula to a PHP script on a server, and have it eval'd... I added some filters to prevent code injection, but it allows all of the math functions, brackets etc.

serverside PHP code:
CODE

<?php
$constants = array(
'!PI!i' => M_PI,
'!DEG_TO_RAD!i' => (M_PI / 180),
'!RAD_TO_DEG!i' => (180 / M_PI),
);
$query = strtolower($_SERVER['QUERY_STRING']);
$query = preg_replace(array_keys($constants), array_values($constants), $query);
$query = preg_replace('![^a-z0-9\.\-\+\*/\(\)]!', '', $query);
$slcalc = false;
$test = eval('$slcalc=(' . $query . ');');
if ($test === false || $slcalc === FALSE)
{
exit('INVALID QUERY');
}
print $slcalc;
?>


LSL code:
CODE

integer LISTEN_CHANNEL = 15;
string gQuery = "";
default
{
state_entry()
{
llListen(LISTEN_CHANNEL, "", llGetOwner(), "");
llOwnerSay("Listening for you on channel " + (string)LISTEN_CHANNEL);
}
listen(integer channelId, string avatarName, key avatarKey, string message)
{
if (channelId == LISTEN_CHANNEL && avatarKey == llGetOwner())
{
gQuery = message;
llHTTPRequest("http://www.webfabriek.nl/slcalc.php?" + message, [HTTP_METHOD, "GET"], "");
}
}
http_response(key requestKey, integer status, list metadata, string body)
{
llOwnerSay(gQuery + "=" + body);
}
}


Use like:
/15 (31.5+7.85)*RAD_TO_DEG

Lyn