Welcome to the Second Life Forums Archive

These forums are CLOSED. Please visit the new forums HERE

XyCalc v1.0

Xylor Baysklef
Scripting Addict
Join date: 4 May 2003
Posts: 109
02-12-2004 01:57
Here is an in-game calculator designed to be put on an attachment. It temporarily renames your attachment while giving output.

To use it, put the following scripts on an object/attachement, and give commands like this:

xc 1+1
XyCalc v1.0: 2

You can also use parenthesis to input more complex commands:

xc sqrt(3^2 + 4^2)
XyCalc v1.0: 5.0

XyCalc handles variables, and has a few constants of its own. You can assign a variable using the '=' operator:

xc W = 5.0
XyCalc v1.0: 5.0
xc L = 4.0
XyCalc v1.0: 4.0
xc A = W * L
XyCalc v1.0: 20.0

You can list which variables are set, by using the "xc vars" command:

xc vars
XyCalc v1.0: a = 20.0
XyCalc v1.0: w = 5.0
XyCalc v1.0: l = 4.0


Note: XyCalc is not case sensitive. It converts all input to lowercase before processing.

You can also look at the constants available with "xc constants":

xc constants
XyCalc v1.0: pi = 3.141593
XyCalc v1.0: e = 2.718282
XyCalc v1.0: ans = 20.0


Notice the constant 'ans'. This is updated to the previous result each time you enter a command. This can be used to chain commands together:

xc sin(45)
XyCalc v1.0: 0.707107
xc asin(ans)
XyCalc v1.0: 45.000019

You can also assign a variable embedded in your command:

xc sin( theta=asin( sqrt(2) / 2) )
XyCalc v1.0: 0.707107
xc theta
XyCalc v1.0: 45.000019

You can switch between Radian and Degree mode for trig commands by saying:

xc radians
XyCalc v1.0: Trig is now in radian mode.
or
xc degrees
XyCalc v1.0: Trig is now in degree mode.


XyCalc understands the following operations:

+, -, /, *, ^, %, &, |,
abs, rand, sqrt, round, floor, ceil,
sin, cos, tan, asin, acos, atan,
sinh, cosh, tanh, asinh, acosh, atanh,
ln, log


Future versions may include vector types, to handle vector manipulation.

Here is the code (Split into different posts since it is too large... check the replies):

XyCalc v1.0
CODE

////////////////////////////////////////////
// XyCalc Script
//
// Written by Xylor Baysklef
////////////////////////////////////////////

/////////////// CONSTANTS ///////////////////
// Channels to communicate with the processor.
integer NEW_COMMAND = 14000;
integer PUSH_OPERAND = 14001;
integer EXECUTE_OPERATOR = 14002;
integer DISPLAY_RESULTS = 14003;
integer XYCALC_SAY = 14004;
integer XYCALC_RESULTS = 14005;

// OpHandler channels.
integer OP_HANDLER_REQUEST = 14100;
integer OP_HANDLER_RETURN = 14101;

// Bit-wise operand types.
integer OP_INTEGER = 1;
integer OP_FLOAT = 2;
integer OP_NUMBER = 3;
integer OP_VECTOR = 4;
integer OP_ROTATION = 8;
integer OP_STRING = 16;
integer OP_PARAMS = 32;
//integer OP_RESERVED = 64;
integer OP_ANY = 127;
integer OP_MULTIPLIER = 128;

integer TOKEN_OPERATOR = 0;
integer TOKEN_OPERAND = 1;

// Number of operands and types each operator uses.
list OPERAND_INFO;

// Used for error handling.
integer NO_ERROR = FALSE;
integer ERROR = TRUE;

// Number of ops in operand info list.
integer OP_KNOWN = 13;
// Value to assume for the rest.
list OP_UNKNOWN_INFO = [1, OP_FLOAT];

///////////// END CONSTANTS ////////////////

///////////// GLOBAL VARIABLES ///////////////
// This is the post-fix stack
list gStack;
integer gStackSize;

// This is whether or not an error was encountered.
integer gHadError;
// This whether or not we are currently waiting
// for an op handler return.
integer gWaitingForOpHandler;
// These are tokens waiting to be
// processed once the current op handler is done.
list gPendingTokens;
// This is whether or not to display results
// once we are done waiting for the op handler.
integer gPendingDisplayResults;
/////////// END GLOBAL VARIABLES ////////////

Say(string mesg) {
// Switch names temporarily.
string OldName = llGetObjectName();
llSetObjectName(llGetScriptName());
llSay(0, llGetSubString(mesg, 0, 254));
llSetObjectName(OldName);
}

Error(string operator, string mesg) {
// Report the error.
Say("Error executing '" + operator + "' operator: " + mesg + ".");
}


//////////// STACK FUNCTIONS //////////////////

ClearStack() {
// This clears the stack, and stack size.
gStack = [];
gStackSize = 0;
}

Push(string operand, integer type) {
// Just put this on the end of the list.
gStack += [operand, type];
gStackSize++;
}

PushList(list operand) {
// Just put this on the end of the list.
gStack += operand;
gStackSize++;
}

list Pop() {
// Remove the last item from the list.
list Operand = llList2List(gStack, -2, -1);
gStack = llDeleteSubList(gStack, -2, -1);
gStackSize--;
return Operand;
}


integer IsStackEmpty() {
// Check the stack size.
if (gStackSize)
return FALSE;
else
return TRUE;
}

////////// END STACK FUNCTIONS ///////////////


string TypeToString(integer type) {
if (type == OP_NUMBER)
return "number";
if (type == OP_INTEGER)
return "integer";
if (type == OP_FLOAT)
return "float";
if (type == OP_VECTOR)
return "vector";
if (type == OP_ROTATION)
return "rotation";
if (type == OP_STRING)
return "string";
if (type == OP_PARAMS)
return "params";

return "unknown";
}

integer ExecuteOperator(string operator, integer operator_index) {
// See how many operatand to extract, and what
// types they should be.
integer NumOperands;
integer OperandTypes;

// Check if this operator is in the operand info list.
if (operator_index > OP_KNOWN) {
NumOperands = llList2Integer(OP_UNKNOWN_INFO, 0);
OperandTypes = llList2Integer(OP_UNKNOWN_INFO, 1);
}
else {
NumOperands = llList2Integer(OPERAND_INFO, operator_index * 2);
OperandTypes = llList2Integer(OPERAND_INFO, operator_index * 2 + 1);
}

// Make sure there are enough operands.
if (gStackSize < NumOperands) {
Error(operator, "Not enough operands");
return ERROR;
}

// Build the operand list, and check types.
list OperandList = [];
integer i;
list Operand;
integer TypeMultiplier = 1;
string OpToken;
integer OpType;
integer ExpectedType;
for (i = 0; i < NumOperands; i++) {
// Extract the operand from the post fix stack.
Operand = Pop();

// Figure out the expected operand type.
ExpectedType = (OperandTypes / TypeMultiplier) & OP_ANY;

// Check type info.
OpToken = llList2String (Operand, 0);
OpType = llList2Integer(Operand, 1);

//Say("types: " + llList2CSV([OperandTypes,
// TypeMultiplier, ExpectedType, OpType]));

// First check for the case this is unknown.
if (OpType == OP_STRING) {
// See if we need to determine if this is a float or
// an integer.
if (ExpectedType == OP_NUMBER) {
// If there is a '.' in the string, assume
// it is a float.
if (llSubStringIndex(OpToken, ".") != -1)
// Convert to a float.
Operand = [(float) OpToken, OP_FLOAT];
else
// Convert to an integer.
Operand = [(integer) OpToken, OP_INTEGER];
}
// Otherwise convert to the expected type.
else if (ExpectedType == OP_INTEGER)
Operand = [(integer) OpToken, OP_INTEGER];
else if (ExpectedType == OP_FLOAT)
Operand = [(float) OpToken, OP_FLOAT];
else if (ExpectedType == OP_VECTOR)
Operand = [(float) OpToken, OP_VECTOR];
else if (ExpectedType == OP_ROTATION)
Operand = [(float) OpToken, OP_ROTATION];
}

// Now check if casting is needed.
else if (OpType == OP_INTEGER && ExpectedType == OP_FLOAT)
// Convert to float.
Operand = [(float) OpToken, OP_FLOAT];
else if (OpType == OP_FLOAT && ExpectedType == OP_INTEGER)
// Convert to integer.
Operand = [(integer) OpToken, OP_INTEGER];

// Otherwise, compare type info (OpType != OP_UNKNOWN)
else if ( OpType & ExpectedType == 0) {
Error(operator, "Invalid type; Cannot cast from (" +
TypeToString(OpType) + ") to (" +
TypeToString(ExpectedType) + ")");
return ERROR;
}

// Add the operand and update the type multiplier.
OperandList += Operand;
TypeMultiplier *= OP_MULTIPLIER;
}

//llSay(0, llList2CSV([operator, operator_index] + OperandList));

// Send this off the the op handler.
llMessageLinked(LINK_SET, OP_HANDLER_REQUEST,
llList2CSV([operator] + OperandList), "");
// Set this flag while we wait for the op handler.
gWaitingForOpHandler = TRUE;

return NO_ERROR;
}

ShowResults() {
// Send the results off for processing.
llMessageLinked(LINK_SET, XYCALC_RESULTS, llList2CSV(gStack), "");
}

default {
state_entry() {
// Number of operands and types each operator uses.
OPERAND_INFO = [
1, OP_INTEGER, // ~
2, OP_NUMBER + OP_NUMBER * OP_MULTIPLIER, // +
2, OP_NUMBER + OP_NUMBER * OP_MULTIPLIER, // -
2, OP_NUMBER + OP_NUMBER * OP_MULTIPLIER, // /
2, OP_NUMBER + OP_NUMBER * OP_MULTIPLIER, // *
-1, 0, // ( -- special
-1, 0, // ) -- special
2, OP_STRING + OP_ANY * OP_MULTIPLIER, // =
2, OP_NUMBER + OP_NUMBER * OP_MULTIPLIER, // ^
2, OP_INTEGER + OP_INTEGER * OP_MULTIPLIER, // %
2, OP_INTEGER + OP_INTEGER * OP_MULTIPLIER, // &
2, OP_INTEGER + OP_INTEGER * OP_MULTIPLIER, // |
1, OP_NUMBER, // abs
1, OP_NUMBER, // rand
//1, OP_FLOAT, // sqrt
//1, OP_FLOAT, // round
//1, OP_FLOAT, // asinh
//1, OP_FLOAT, // acosh
//1, OP_FLOAT, // atanh
//1, OP_FLOAT, // sinh
//1, OP_FLOAT, // cosh
//1, OP_FLOAT, // tanh
//1, OP_FLOAT, // asin
//1, OP_FLOAT, // acos
//1, OP_FLOAT, // atan
//1, OP_FLOAT, // sin
//1, OP_FLOAT, // cos
//1, OP_FLOAT, // tan
//1, OP_FLOAT, // ln
//1, OP_FLOAT, // log
//1, OP_FLOAT, // floor
//1, OP_FLOAT, // ceil
-1];

Say("Free Memory: " + (string) llGetFreeMemory());
}

link_message(integer sender, integer channel, string data, key id) {
if (channel == NEW_COMMAND) {
// Clear the stack.
ClearStack();
// Clear the error flag.
gHadError = FALSE;
// Clear the pending tokens list.
gPendingTokens = [];
// Clear the waiting for op handler flag.
gWaitingForOpHandler = FALSE;
// Clear the pending display results flag.
gPendingDisplayResults = FALSE;
return;
}

// Check for a previous error.
if (gHadError)
return;

if (channel == PUSH_OPERAND) {
// If we are waiting for an op handler, add this
// to the pending tokens list.
list Operand = llCSV2List(data);
if (gWaitingForOpHandler)
gPendingTokens += Operand + [TOKEN_OPERAND];
else
// Just push this onto the stack.
PushList(Operand);
return;
}
if (channel == DISPLAY_RESULTS) {
// If we are waiting for an op handler, set the
// pending display results flags for now.
if (gWaitingForOpHandler) {
gPendingDisplayResults = TRUE;
}
else
// Otherwise show the results now.
ShowResults();

return;
}
if (channel == EXECUTE_OPERATOR) {
list Parsed = llCSV2List(data);

// If we are waiting for an op handler, just
// add this to the pending tokens list.
if (gWaitingForOpHandler) {
gPendingTokens += Parsed + [TOKEN_OPERATOR];
}
else
// Otherwise execute the operator now.
gHadError = ExecuteOperator(llList2String(Parsed, 0),
(integer)llList2String(Parsed, 1));

return;
}
if (channel == OP_HANDLER_RETURN) {
// Unset the wait flag.
gWaitingForOpHandler = FALSE;

// Check the results for an error.
list Parsed = llCSV2List(data);
if (llList2String(Parsed, 0) == "ERROR") {
// Display the error.
string Operator = llList2String(Parsed, 1);
string ErrorMsg = llList2String(Parsed, 2);
Error(Operator, ErrorMsg);

// Throw the error flag, and quit.
gHadError = TRUE;
return;
}

// Push the results onto the stack.
PushList(Parsed);

// If there are no more pending tokens, and we
// are waiting to display results, do it now.
if (gPendingDisplayResults &&
llGetListLength(gPendingTokens) == 0) {
ShowResults();
return;
}

// If there are pending tokens, push all operands until
// we get to an operator to execute.
integer Found = FALSE;
list Token;
while (llGetListLength(gPendingTokens) > 0 && !Found) {
Token = llList2List(gPendingTokens, 0, 2);
gPendingTokens = llDeleteSubList(gPendingTokens, 0, 2);

// See if this is an operand.
if (llList2Integer(Token, 2) != TOKEN_OPERATOR) {
// Just push this onto the stack.
PushList(llList2List(Token, 0, 1));
}
else {
// Execute this operator.
Found = TRUE;
gHadError = ExecuteOperator(llList2String(Token, 0),
(integer)llList2String(Token, 1));
}
}

return;
}
if (channel == XYCALC_SAY) {
// Used to re-route output through the xy calc script.
Say(data);
return;
}
}
}


Since only 20,000 characters can be sent per post, I will post the rest of the scripts once this post shows up in the script library.

Xylor
Xylor Baysklef
Scripting Addict
Join date: 4 May 2003
Posts: 109
02-14-2004 17:01
XyCalc Parser
CODE

////////////////////////////////////////////
// XyCalc Parser Script
//
// Written by Xylor Baysklef
////////////////////////////////////////////

/////////////// CONSTANTS ///////////////////
// Channels to communicate with the processor.
integer NEW_COMMAND = 14000;
integer PUSH_OPERAND = 14001;
integer EXECUTE_OPERATOR = 14002;
integer DISPLAY_RESULTS = 14003;

// Channels to send mode info.
integer MODE_DEGREE = 14200;
integer MODE_RADIAN = 14201;

// Variable Handler.
integer VARIABLE_LOOKUP = 14300;
integer PIPE_RESULTS = 14301;
integer PIPE_OPERAND = 14302;
integer CMD_LIST_VARS = 14303;
integer CMD_CLEAR_VARS = 14304;
integer CMD_LIST_CONST = 14305;

// Prefix before all calculation commands.
string INFIX_PREFIX = "xc ";
string RPN_PREFIX = "rpn ";

// Command names.
list COMMAND_NAMES = [ "degrees", "radians" ,
"vars", "clear", "constants"];
// Command channels.
list COMMAND_CHANS = [ MODE_DEGREE, MODE_RADIAN,
CMD_LIST_VARS, CMD_CLEAR_VARS, CMD_LIST_CONST];

// Bit-wise operand types.
//integer OP_OPERATOR = 0;
//integer OP_INTEGER = 1;
//integer OP_FLOAT = 2;
//integer OP_NUMBER = 3;
//integer OP_VECTOR = 4;
//integer OP_ROTATION = 8;
integer OP_STRING = 16;
//integer OP_PARAMS = 32;
//integer OP_RESERVED = 64;
//integer OP_ANY = 127;
//integer OP_MULTIPLIER = 128;

// Seperators used while tokenizing.
list OPERATORS =
[ "~", "+", "-", "/", "*", "(", ")", "=",
"^", "%", "&", "|",
"abs", "rand", "sqrt", "round",
"asinh", "acosh", "atanh", "sinh", "cosh", "tanh",
"asin", "acos", "atan", "sin", "cos", "tan",
"ln", "log", "floor", "ceil"];

// Operator precedence levels.
list OPERATOR_PRECEDENCE = [
6, // ~
3, // +
3, // -
4, // /
4, // *
-1, // ( -- special
-1, // ) -- special
1, // =
7, // ^
5, // %
2, // &
2, // |
// Assume everything higher than
// this is '6' to save memory.
//6, // abs
//6, // rand
//6, // sqrt
//6, // round
//6, // sin
//6, // cos
//6, // tan
//6, // asin
//6, // acos
//6, // atan
//6, // sinh
//6, // cosh
//6, // tanh
//6, // asinh
//6, // acosh
//6, // atanh
//6, // ln
//6, // log
//6, // floor
//6, // ceil
-1];

// Number of ops in precedence list.
integer OP_KNOWN = 11;
// Precedence to assume for the rest.
integer OP_UNKNOWN_VAL = 6;
///////////// END CONSTANTS ////////////////

///////////// GLOBAL VARIABLES ///////////////
// Current object owner.
key gOwner;

// Used to pre-calculate the prefix lengths.
integer gInfixPrefixLen;
integer gRpnPrefixLen;

// This is the operator stack
list gStack;
integer gStackSize;

// Whether or not the previous token was
// an operator.
integer gLastWasOperator;
/////////// END GLOBAL VARIABLES ////////////

Say(string mesg) {
// Switch names temporarily.
string OldName = llGetObjectName();
llSetObjectName(llGetScriptName());
llSay(0, llGetSubString(mesg, 0, 254));
llSetObjectName(OldName);
}

integer IsOperand(string token) {
// Check if this is an operator. If it isn't
// then it is an operand.
if (llListFindList(OPERATORS, [token]) == -1)
return TRUE;
else
return FALSE;
}

list ParseTokens(string data) {
// This repeatedly calls llParseString2List, since
// doing it in one call will not work (LSL bug).
integer MaxOperatorsPerCall = 8;

// Do this loop until we have processed all operators.
integer Block;
integer NumOperands = llGetListLength(OPERATORS);
list Previous = [data];
list Next;
for (Block = 0; Block < NumOperands; Block += MaxOperatorsPerCall) {
// Parse each entry currently in the previous list.
integer i;
for (i = 0; i < llGetListLength(Previous); i++) {
string Entry = llList2String(Previous, i);
// Don't process this entry if it is an operator.
if (IsOperand(Entry))
// Parse this entry, and put it in the Next list.
Next += llParseString2List(Entry, [" "],
llList2List(OPERATORS, Block, Block + MaxOperatorsPerCall - 1));
else
Next += [Entry];
}
// Update previous.
Previous = Next;
Next = [];
}
// Return the results.
return Previous;
}

//////////// STACK FUNCTIONS //////////////////

ClearStack() {
// This clears the stack, and stack size.
gStack = [];
gStackSize = 0;
}

Push(string operator) {
// Just put this on the end of the list.
gStack += [operator];
gStackSize++;
}

string Pop() {
if (gStackSize) {
// Remove the last item from the list.
string Operator = llList2String(gStack, -1);
gStack = llDeleteSubList(gStack, -1, -1);
gStackSize--;
return Operator;
}
else
return "";
}

string StackTop() {
// If the stack is empty, return nothing.
if (gStackSize == 0)
return "";
else
// Return the top of the stack.
return llList2String(gStack, -1);
}

integer IsStackEmpty() {
// Check the stack size.
if (gStackSize)
return FALSE;
else
return TRUE;
}
////////// END STACK FUNCTIONS ///////////////

integer Precedence(string oper1, string oper2) {
// This returns TRUE if oper1 has precedence
// over oper2 when oper1 appears to the left
// of oper2 in an infix expression without
// parentheses.

// First check for parentheses.
if (oper1 == "(" || oper2 == "(")
return FALSE;

// oper1 should never == ")"

if (oper2 == ")")
return TRUE;

integer Oper1Index = llListFindList(OPERATORS, [oper1]);
integer Oper2Index = llListFindList(OPERATORS, [oper2]);

integer Oper1Precedence;
if (Oper1Index > OP_KNOWN)
Oper1Precedence = OP_UNKNOWN_VAL;
else
Oper1Precedence = llList2Integer(OPERATOR_PRECEDENCE, Oper1Index);

integer Oper2Precedence;
if (Oper2Index > OP_KNOWN)
Oper2Precedence = OP_UNKNOWN_VAL;
else
Oper2Precedence = llList2Integer(OPERATOR_PRECEDENCE, Oper2Index);


// See if oper1 higher or equal precedence than oper2.
if (Oper1Precedence >= Oper2Precedence)
return TRUE;
else
return FALSE;
}

integer CheckForCommand(string mesg) {
// Check for command.
integer i;
string CommandName;
integer CommandNameLen;
for (i = 0; i < llGetListLength(COMMAND_NAMES); i++) {
CommandName = llList2String(COMMAND_NAMES, i);
CommandNameLen = llStringLength(CommandName);

if (llGetSubString(mesg, 0, CommandNameLen - 1) == CommandName) {
// Convert the command into a message.
integer CommandChannel = llList2Integer(COMMAND_CHANS, i);
// Send the command.
llMessageLinked(LINK_SET, CommandChannel,
llGetSubString(mesg, CommandNameLen, -1), "");
// Command found.
return TRUE;
}
}
// No command.
return FALSE;
}

ExecuteOperator(string operator) {
if (operator == "(")
Say("Warning: Mismatched parenthesis.");
else
llMessageLinked(LINK_SET, EXECUTE_OPERATOR, llList2CSV(
[operator, (string) llListFindList(OPERATORS, [operator])]), "");
}

default {
state_entry() {
gOwner = llGetOwner();
llListen(0, "", gOwner, "");

// Pre-calculate these to save processing time.
gInfixPrefixLen = llStringLength(INFIX_PREFIX);
gRpnPrefixLen = llStringLength(RPN_PREFIX);

Say("Free Memory: " + (string) llGetFreeMemory());
}

on_rez(integer param) {
// See if owner changed.
if (gOwner != llGetOwner())
llResetScript();
}

listen(integer channel, string name, key id, string mesg) {
// Go lowercase.
mesg = llToLower(mesg);

// Check for a command prefix.
if (llGetSubString(mesg, 0, gInfixPrefixLen - 1) == INFIX_PREFIX) {
// Strip off the prefix.
string Command = llGetSubString(mesg, gInfixPrefixLen, -1);

// Check for a special command.
if (CheckForCommand(Command))
return;

// Parse the command into tokens, discarding spaces.
list Parsed = ParseTokens(Command);

// Convert from in-fix to post-fix form.
ClearStack();
// Tell the processor a new command is starting.
llMessageLinked(LINK_SET, NEW_COMMAND, "", "");
// Set the last was operator flag for '-' processing.
gLastWasOperator = TRUE;

integer i;
string Token;
for (i = 0; i < llGetListLength(Parsed); i++) {
Token = llList2String(Parsed, i);

// Check for negative numbers.
if (Token == "-" && gLastWasOperator) {
// Concatenate this with the next token.
i++;
Token = "-" + llList2String(Parsed, i);
}

if (IsOperand(Token)) {
// Check for the special case that the next token is
// the '=' operator, in which case we do not do a lookup.
if (llList2String(Parsed, i + 1) == "=")
// Bypass the variable handler.
llMessageLinked(LINK_SET, PIPE_OPERAND,
llList2CSV([Token, OP_STRING]), "");
else
// Send this to the variable handler for possible varable lookup.
llMessageLinked(LINK_SET, VARIABLE_LOOKUP, Token, "");
// Set the last was operator flag.
gLastWasOperator = FALSE;
}
else {
// Execute operators until there are none left, or
// precedence takes over.
while (!IsStackEmpty() && Precedence(StackTop(), Token))
// Execute the operator.
ExecuteOperator(Pop());

if (IsStackEmpty() || Token != ")")
Push(Token);
else // Pop the open paranthesis and discard it.
Pop();

// Set the last was operator flag.
gLastWasOperator = TRUE;
}
}

// Add any remaining operators.
while (!IsStackEmpty())
// Execute the operator.
ExecuteOperator(Pop());

// Tell the processor to display results.
// Pipe this through the variable lookup script,
// so that message timing is correct.
llMessageLinked(LINK_SET, PIPE_RESULTS, "", "");
return;
}
if (llGetSubString(mesg, 0, gRpnPrefixLen - 1) == RPN_PREFIX) {
// Strip off the prefix.
string Command = llGetSubString(mesg, gRpnPrefixLen, -1);

// Check for a special command.
if (CheckForCommand(Command))
return;

// Parse the command into tokens.
list Parsed = llParseString2List(Command, [" "], OPERATORS);

// Tell the processor a new command is starting.
llMessageLinked(LINK_SET, NEW_COMMAND, "", "");

integer i;
string Token;
for (i = 0; i < llGetListLength(Parsed); i++) {
Token = llList2String(Parsed, i);

if (IsOperand(Token)) {
// Send this to the variable handler for possible varable lookup.
llMessageLinked(LINK_SET, VARIABLE_LOOKUP, Token, "");
}
else {
// Execute this operator.
llMessageLinked(LINK_SET, EXECUTE_OPERATOR, llList2CSV(
[Token, (string) llListFindList(OPERATORS, [Token])]), "");
}
}

// Tell the processor to display results.
// Pipe this through the variable lookup script,
// so that message timing is correct.
llMessageLinked(LINK_SET, PIPE_RESULTS, "", "");
return;
}
}
}
Xylor Baysklef
Scripting Addict
Join date: 4 May 2003
Posts: 109
02-14-2004 17:03
[edit: added patch by Francis Chung]

XyCalc Variables
CODE

////////////////////////////////////////////
// XyCalc Variables Script
//
// Written by Xylor Baysklef
////////////////////////////////////////////

/////////////// CONSTANTS ///////////////////
// Channels to communicate with the processor.
integer NEW_COMMAND = 14000;
integer PUSH_OPERAND = 14001;
integer EXECUTE_OPERATOR = 14002;
integer DISPLAY_RESULTS = 14003;
integer XYCALC_SAY = 14004;
integer XYCALC_RESULTS = 14005;

// Variable Handler.
integer VARIABLE_LOOKUP = 14300;
integer PIPE_RESULTS = 14301;
integer PIPE_OPERAND = 14302;
integer CMD_LIST_VARS = 14303;
integer CMD_CLEAR_VARS = 14304;
integer CMD_LIST_CONST = 14305;

// Bit-wise operand types.
integer OP_OPERATOR = 0;
integer OP_INTEGER = 1;
integer OP_FLOAT = 2;
integer OP_NUMBER = 3;
integer OP_VECTOR = 4;
integer OP_ROTATION = 8;
integer OP_STRING = 16;
integer OP_PARAMS = 32;
//integer OP_RESERVED = 64;
integer OP_ANY = 127;
integer OP_MULTIPLIER = 128;

// OpHandler channels.
integer OP_HANDLER_REQUEST = 14100;
integer OP_HANDLER_RETURN = 14101;

// These are invalid characters, that a
// variable name cannot start with.
string INVALID_CHARS = "0123456789.";

// Math constant.
float e = 2.71828182845904523536;

// These are constants, always present.
list CONSTANT_NAMES = ["pi", "e", "ans"];
list CONSTANT_VALUES = [ PI, OP_FLOAT,
e, OP_FLOAT,
0, 0 // Special
];
///////////// END CONSTANTS ////////////////

///////////// GLOBAL VARIABLES ///////////////
// These hold the variable names.
list gVarNames = CONSTANT_NAMES;
// These hold the variable values.
list gVarValues = CONSTANT_VALUES;
// These are the number of constants in
// the variables list, which we should not change.
integer gNumConstants;
// This is the last result info.
string gLastResult = "0";
integer gLastResultType = OP_INTEGER;
// These are the operands we are working on.
list gOperandList;
/////////// END GLOBAL VARIABLES ////////////

Say(string mesg) {
// Send this to the xy calc script for output.
llMessageLinked(LINK_SET, XYCALC_SAY, mesg, "");
}

SendResults(string result, integer type) {
// Send back the results.
llMessageLinked(LINK_SET, OP_HANDLER_RETURN, llList2CSV(
[result, type]), "");
}

Error(string operator, string mesg) {
// Send back the results as an error.
llMessageLinked(LINK_SET, OP_HANDLER_RETURN, llList2CSV(
["ERROR", operator, mesg]), "");
}

string FormatFloat(float num) {
string Float = (string) num;
// Remove trailing 0's.
integer i = llStringLength(Float) - 1;
integer NonZero = FALSE;
string Char;
integer NumZeroes = 0;
while( !NonZero && i >= 0) {
Char = llGetSubString(Float, i, i);
if (Char == "0")
NumZeroes++;
else if (Char == ".") {
// Back up one zero.
NumZeroes--;
NonZero = TRUE;
}
else
NonZero = TRUE;
i--;
}

return llGetSubString(Float, 0, -NumZeroes - 1);
}

string FormatData(string value, integer type) {
// Format this value based on its type.
if (type == OP_INTEGER)
return (string) ((integer) value);

else if (type == OP_FLOAT)
return FormatFloat((float) value);

else if (type == OP_VECTOR)
return (string) ((vector) value);

else if (type == OP_ROTATION)
return (string) ((rotation) value);

else if (type == OP_STRING)
return value;
else
return value + ", " + (string) type;
}

DisplayVariable(string name, string value, integer type) {
// Format the value, and display it.
Say(name + " = " + FormatData(value, type));
}

////////// OPERATOR LIST FUNCTIONS ///////////
integer GetOpType(integer op_number) {
return llList2Integer(gOperandList, op_number * -2 + 1);
}

integer OpToInteger(integer op_number) {
return llList2Integer(gOperandList, op_number * -2);
}
float OpToFloat(integer op_number) {
return llList2Float(gOperandList, op_number * -2);
}
vector OpToVector(integer op_number) {
return llList2Vector(gOperandList, op_number * -2);
}
rotation OpToRotation(integer op_number) {
return llList2Rot(gOperandList, op_number * -2);
}

string OpToString(integer op_number) {
return llList2String(gOperandList, op_number * -2);
}

//////// END OPERATOR LIST FUNCTIONS ////////



default {
state_entry() {
// Set the number of constants.
gNumConstants = llGetListLength(CONSTANT_NAMES);
}

link_message(integer sender, integer channel, string data, key id) {
if (channel == VARIABLE_LOOKUP) {
// Check if this operand is known.
integer VarIndex = llListFindList(gVarNames, [data]);

// Just pass this on as unknown if we couldn't find it.
if (VarIndex == -1) {
llMessageLinked(LINK_SET, PUSH_OPERAND,
llList2CSV([data, OP_STRING]), "");
return;
}

// "ans" is special, check for that first.
if (data == "ans") {
llMessageLinked(LINK_SET, PUSH_OPERAND,
llList2CSV([gLastResult, gLastResultType]), "");
return;
}

// Otherwise replace this operand with our recorded
// value/type, and send that instead.
string VarValue = llList2String (gVarValues, VarIndex * 2);
integer VarType = llList2Integer(gVarValues, VarIndex * 2 + 1);

llMessageLinked(LINK_SET, PUSH_OPERAND,
llList2CSV([VarValue, VarType]), "");

return;
}
if (channel == XYCALC_RESULTS) {
// Record these results, and display them.
list Results = llCSV2List(data);

gLastResult = llList2String(Results, 0);
gLastResultType = llList2Integer(Results, 1);

// Show results.
Say(FormatData(gLastResult, gLastResultType));

return;
}
if (channel == OP_HANDLER_REQUEST) {
// Split up the data.
list Parsed = llCSV2List(data);

// See if this an assignment operator.
string Operator = llList2String(Parsed, 0);
if (Operator != "=")
// No, Ignore it.
return;

// Extract the operands.
gOperandList = llList2List(Parsed, 1, -1);

// Otherwise lets check the first operand for a valid name.
string VarName = OpToString(1);

if (llSubStringIndex(INVALID_CHARS,
llGetSubString(VarName, 0, 0)) != -1) {
Error(Operator, "Invalid variable name '" + VarName + "'");
return;
}

// Valid variable name. See if this variable is already known.
integer VarIndex = llListFindList(gVarNames, [VarName]);

string VarValue = OpToString(2);
integer VarType = GetOpType(2);

if (VarIndex != -1) {
// See if this is a constant.
if (VarIndex < gNumConstants) {
// Trigger error.
Error("=", "Cannot assign to the constant '" + VarName + "'");
SendResults(VarValue, VarType);
return;
}
// Remove the old one.
gVarNames = llDeleteSubList(gVarNames, VarIndex, VarIndex);
gVarValues = llDeleteSubList(gVarValues, VarIndex * 2, VarIndex * 2 + 1);
}

// Add this variable.
gVarNames += [VarName];
gVarValues += [VarValue, VarType];

SendResults(VarValue, VarType);
return;
}
if (channel == PIPE_RESULTS) {
// Just pass this on to the main processor. It is only
// sent here to make sure message timings are syncronized.
llMessageLinked(LINK_SET, DISPLAY_RESULTS, "", "");
return;
}
if (channel == PIPE_OPERAND) {
// Pass this through unfiltered. This keeps messages
// syncronized.
llMessageLinked(LINK_SET, PUSH_OPERAND, data, "");
return;
}
if (channel == CMD_LIST_VARS) {
// List all variables.
// First see if there are any variables to show.
integer NumVars = llGetListLength(gVarNames);
if (NumVars <= gNumConstants) {
Say("No variables.");
}
else {
// Go through the variables list, and display them.
integer i;
for (i = gNumConstants; i < NumVars; i++) {
string VarName = llList2String(gVarNames, i);
string VarValue = llList2String (gVarValues, i * 2);
integer VarType = llList2Integer(gVarValues, i * 2 + 1);

DisplayVariable(VarName, VarValue, VarType);
}
}
return;
}
if (channel == CMD_CLEAR_VARS) {
// Clear all variables.
gVarNames = CONSTANT_NAMES;
gVarValues = CONSTANT_VALUES;
Say("Variables cleared.");
return;
}
if (channel == CMD_LIST_CONST) {
// Go through the constants in the variable list, and display them.
integer i;
// Don't show the last one (ans), we will handle this seperately.
for (i = 0; i < gNumConstants - 1; i++) {
string VarName = llList2String(gVarNames, i);
string VarValue = llList2String (gVarValues, i * 2);
integer VarType = llList2Integer(gVarValues, i * 2 + 1);

DisplayVariable(VarName, VarValue, VarType);
}
// Show 'ans'.
DisplayVariable("ans", gLastResult, gLastResultType);
return;
}
}
}



XyCalc OpHandler +-*/^%&|~
CODE

////////////////////////////////////////////
// XyCalc OpHandler +-*/^%&|~ Script
//
// Written by Xylor Baysklef
////////////////////////////////////////////

/////////////// CONSTANTS ///////////////////
// These are the operators this script handles.
list HANDLED_OPERATORS = [ "+", "-", "*", "/",
"^", "%", "&", "|", "~"];

// OpHandler channels.
integer OP_HANDLER_REQUEST = 14100;
integer OP_HANDLER_RETURN = 14101;

// Bit-wise operand types.
integer OP_OPERATOR = 0;
integer OP_INTEGER = 1;
integer OP_FLOAT = 2;
integer OP_NUMBER = 3;
integer OP_VECTOR = 4;
integer OP_ROTATION = 8;
integer OP_STRING = 16;
integer OP_PARAMS = 32;
//integer OP_RESERVED = 64;
integer OP_ANY = 127;
integer OP_MULTIPLIER = 128;


///////////// END CONSTANTS ////////////////

///////////// GLOBAL VARIABLES ///////////////
// These are the operands we are working on.
list gOperandList;
/////////// END GLOBAL VARIABLES ////////////


////////// OPERATOR LIST FUNCTIONS ///////////
integer GetOpType(integer op_number) {
return llList2Integer(gOperandList, op_number * -2 + 1);
}

integer OpToInteger(integer op_number) {
return llList2Integer(gOperandList, op_number * -2);
}
float OpToFloat(integer op_number) {
return llList2Float(gOperandList, op_number * -2);
}
vector OpToVector(integer op_number) {
return llList2Vector(gOperandList, op_number * -2);
}
rotation OpToRotation(integer op_number) {
return llList2Rot(gOperandList, op_number * -2);
}

//////// END OPERATOR LIST FUNCTIONS ////////

SendResults(string result, integer type) {
// Send back the results.
llMessageLinked(LINK_SET, OP_HANDLER_RETURN, llList2CSV(
[result, type]), "");
}

Error(string operator, string mesg) {
// Send back the results as an error.
llMessageLinked(LINK_SET, OP_HANDLER_RETURN, llList2CSV(
["ERROR", operator, mesg]), "");
}


DivideByZero(string operator) {
Error(operator, "Divide by zero");
}

///// Start Patch by Francis Chung

// Fran wuz here
//
// The `fmod' function returns the value X-I*Y, for the
// largest integer I such that, if Y is nonzero, the result
// has the same sign as X and magnitude less than the magni-
// tude of Y.

// Raises divide by zero when y = 0, but as far as I know, LSL
// doesn't allow me to specify NaN, soo...
float fmod( float x, float y ) {
float sign = 1;
float maxI = 1;
float k;

if ( x ) {
sign = x/llFabs(x);
x = llFabs(x);
}

if ( y )
y = llFabs(y);
else {
DivideByZero("%");
return 0;
}

while ( x > maxI * y )
maxI *= 2;

for( k = maxI/2; k >= 1; k/=2 ) {
if ( k*y < x )
x -= k*y;
}

return x * sign;
}
////// End Patch by Francis Chung

default {
state_entry() {
}

link_message(integer sender, integer channel, string data, key id) {
if (channel == OP_HANDLER_REQUEST) {
// Split up the data.
list Parsed = llCSV2List(data);

// See if we need to handle this request.
string Operator = llList2String(Parsed, 0);

if (llListFindList(HANDLED_OPERATORS, [Operator]) == -1)
// No, ignore it.
return;

// Extract the operands.
gOperandList = llList2List(Parsed, 1, -1);

// Check the operator, and execute it.
if (Operator == "+") {
// If both operands are integers, keep the result
// an integer.
if (GetOpType(1) == OP_INTEGER &&
GetOpType(2) == OP_INTEGER)
// Stay in integer form.
SendResults((string) ( OpToInteger(1) + OpToInteger(2) ), OP_INTEGER);
else
// Go to float form.
SendResults((string) ( OpToFloat(1) + OpToFloat(2) ), OP_FLOAT);
}
else if (Operator == "-") {
// If both operands are integers, keep the result
// an integer.
if (GetOpType(1) == OP_INTEGER &&
GetOpType(2) == OP_INTEGER)
// Stay in integer form.
SendResults((string) ( OpToInteger(1) - OpToInteger(2) ), OP_INTEGER);
else
// Go to float form.
SendResults((string) ( OpToFloat(1) - OpToFloat(2) ), OP_FLOAT);
}
else if (Operator == "*") {
// If both operands are integers, keep the result
// an integer.
if (GetOpType(1) == OP_INTEGER &&
GetOpType(2) == OP_INTEGER)
// Stay in integer form.
SendResults((string) ( OpToInteger(1) * OpToInteger(2) ), OP_INTEGER);
else
// Go to float form.
SendResults((string) ( OpToFloat(1) * OpToFloat(2) ), OP_FLOAT);
}
else if (Operator == "/") {
// Check for divide by 0.
if ( llFabs(OpToFloat(2)) < 0.00001 ) {
DivideByZero(Operator);
return;
}

// If both operands are integers, keep the result
// an integer (if possible).
if (GetOpType(1) == OP_INTEGER &&
GetOpType(2) == OP_INTEGER) {
// Try to stay in integer form.
integer OpInt1 = OpToInteger(1);
integer OpInt2 = OpToInteger(2);

if ( OpInt1 % OpInt2 == 0)
// We can stay integer.
SendResults((string) ( OpInt1 / OpInt2 ), OP_INTEGER);
else
// There is a remainder, go float.
SendResults((string) ( OpToFloat(1) / OpToFloat(2) ), OP_FLOAT);
}
else
// Go to float form.
SendResults((string) ( OpToFloat(1) / OpToFloat(2) ), OP_FLOAT);
}
else if (Operator == "^") {
// Check for divide by 0.
float Op1 = OpToFloat(1);
float Op2 = OpToFloat(2);

if (Op1 == 0 && Op2 <= 0) {
DivideByZero(Operator);
return;
}

// If both operands are integers, keep the result
// an integer.
float Result = llPow(Op1, Op2);

if (GetOpType(1) == OP_INTEGER &&
GetOpType(2) == OP_INTEGER &&
Op2 > 0)
// Stay in integer form.
SendResults((string) Result, OP_INTEGER);
else
// Go to float form.
SendResults((string) Result, OP_FLOAT);
}
else if (Operator == "%") {
///// Start Patch by Francis Chung
if ( GetOpType(1) == OP_INTEGER && GetOpType(2) == OP_INTEGER ) {
SendResults((string) (OpToInteger(1) % OpToInteger(2)), OP_INTEGER);
}
else {
float x = 0;
float y = 1;
if ( GetOpType(1) == OP_INTEGER )
x = OpToInteger(1);
if ( GetOpType(1) == OP_FLOAT )
x = OpToFloat(1);

if ( GetOpType(2) == OP_INTEGER )
y = OpToInteger(2);
if ( GetOpType(2) == OP_FLOAT )
y = OpToFloat(2);

SendResults((string) fmod(x,y), OP_FLOAT);
}
////// End Patch by Francis Chung
}
else if (Operator == "&") {
SendResults((string) (OpToInteger(1) & OpToInteger(2)), OP_INTEGER);
}
else if (Operator == "|") {
SendResults((string) (OpToInteger(1) | OpToInteger(2)), OP_INTEGER);
}
else if (Operator == "~") {
SendResults((string) (~OpToInteger(1)), OP_INTEGER);
}

return;
}
}
}
Xylor Baysklef
Scripting Addict
Join date: 4 May 2003
Posts: 109
02-14-2004 17:04
XyCalc OpHandler Transcendental
CODE

////////////////////////////////////////////
// XyCalc OpHandler Transcendental Script
//
// Written by Xylor Baysklef
////////////////////////////////////////////

/////////////// CONSTANTS ///////////////////

// These are the operators this script handles.
list HANDLED_OPERATORS = [ "sin", "cos", "tan",
"asin", "acos", "atan",
"sinh", "cosh", "tanh",
"asinh", "acosh", "atanh",
"ln", "log", "sqrt"];

// Used to re-route output.
integer XYCALC_SAY = 14004;

// OpHandler channels.
integer OP_HANDLER_REQUEST = 14100;
integer OP_HANDLER_RETURN = 14101;

// Channels to listen for mode info.
integer MODE_DEGREE = 14200;
integer MODE_RADIAN = 14201;

// Bit-wise operand types.
integer OP_OPERATOR = 0;
integer OP_INTEGER = 1;
integer OP_FLOAT = 2;
integer OP_NUMBER = 3;
integer OP_VECTOR = 4;
integer OP_ROTATION = 8;
integer OP_STRING = 16;
integer OP_PARAMS = 32;
//integer OP_RESERVED = 64;
integer OP_ANY = 127;
integer OP_MULTIPLIER = 128;


// Math Constants.
float e = 2.71828182845904523536;
float LOG_10 = 2.302585092994;

float LN_ERROR_MARGIN = 0.01;
// How long before timing out.
float LN_CALC_TIMEOUT = 15.0;
///////////// END CONSTANTS ////////////////

///////////// GLOBAL VARIABLES ///////////////
// These are the operands we are working on.
list gOperandList;
// This is used to convert from deg to rad,
// if needed.
// Note: We start in DEGREE mode by default.
float gModeMultiplier = DEG_TO_RAD;
/////////// END GLOBAL VARIABLES ////////////

Say(string mesg) {
// Send this to the xy calc script for output.
llMessageLinked(LINK_SET, XYCALC_SAY, mesg, "");
}


/////////////// MATH FUNCTIONS /////////////////////
float Ln(float x) {
// Use the power series:
// ln(x) = 2 * Sum{n=0, n=Inf} [ ([(x-1)/(x+1)]^(2n+1)) / 2n+1 ]
// Keep interating until the current term is less
// than LN_ERROR_MARGIN.

// Scale this to match the individual term magnitude.
float ErrorMargin = LN_ERROR_MARGIN * LN_ERROR_MARGIN / 2.0;

// Precalculate (x-1)/(x+1), and [(x-1)/(x+1)]^2 (so we
// avoid repeatedly calling llPow), and initialize variables
// used for lookup of previous term results
float X_Term = (x - 1) / (x + 1);
float X_Term_Squared = X_Term * X_Term;

float Result = X_Term;

float This_X_Term = X_Term;
float ThisFullTerm;

// (Start on n=2*1+1=3)
integer n = 3;

// Keep track of how long we are trying this, and just
// use whatever we came up with at the timeout point.
llResetTime();
do {
This_X_Term *= X_Term_Squared;
ThisFullTerm = This_X_Term / n;
Result += ThisFullTerm;
n += 2;
} while (llFabs(ThisFullTerm) > ErrorMargin &&
llGetTime() < LN_CALC_TIMEOUT);

// Give a warning if we timed out.
if (llFabs(ThisFullTerm) > ErrorMargin) {
Say("Warning: Ln(x) calculation timed out.");
}

return Result * 2;
}

float Log(float x) {
// This returns the log, base 10, of x.
return Ln(x) / LOG_10;
}

float Cosh(float x) {
return (llPow(e, x) + llPow(e, -x)) / 2.0;
}

float Sinh(float x) {
return (llPow(e, x) - llPow(e, -x)) / 2.0;
}

float Tanh(float x) {
return Sinh(x) / Cosh(x);
}

float Asinh(float x) {
return Ln(x + llSqrt(x * x + 1));
}

float Acosh(float x) {
return Ln(x + llSqrt(x * x - 1));
}

float Atanh(float x) {
return Ln( (1 + x) / (1 - x) ) / 2;
}
///////////// END MATH FUNCTIONS //////////////////


////////// OPERATOR LIST FUNCTIONS ///////////
integer GetOpType(integer op_number) {
return llList2Integer(gOperandList, op_number * -2 + 1);
}

integer OpToInteger(integer op_number) {
return llList2Integer(gOperandList, op_number * -2);
}
float OpToFloat(integer op_number) {
return llList2Float(gOperandList, op_number * -2);
}
vector OpToVector(integer op_number) {
return llList2Vector(gOperandList, op_number * -2);
}
rotation OpToRotation(integer op_number) {
return llList2Rot(gOperandList, op_number * -2);
}

//////// END OPERATOR LIST FUNCTIONS ////////

SendResults(string result, integer type) {
// Send back the results.
llMessageLinked(LINK_SET, OP_HANDLER_RETURN, llList2CSV(
[result, type]), "");
}

Error(string operator, string mesg) {
// Send back the results as an error.
llMessageLinked(LINK_SET, OP_HANDLER_RETURN, llList2CSV(
["ERROR", operator, mesg]), "");
}

DomainError(string operator) {
Error(operator, "Invalid domain");
}


DivideByZero(string operator) {
Error(operator, "Divide by zero");
}

default {
state_entry() {
}

link_message(integer sender, integer channel, string data, key id) {
if (channel == OP_HANDLER_REQUEST) {
// Split up the data.
list Parsed = llCSV2List(data);

// See if we need to handle this request.
string Operator = llList2String(Parsed, 0);

if (llListFindList(HANDLED_OPERATORS, [Operator]) == -1)
// No, ignore it.
return;

// Extract the operands.
gOperandList = llList2List(Parsed, 1, -1);

// Check the operator, and execute it.
if (Operator == "sqrt") {
// Bounds check.
float Operand = OpToFloat(1);
if (Operand < 0) {
DomainError(Operator);
return;
}

SendResults((string) (llSqrt(Operand)), OP_FLOAT);
}
else if (Operator == "sin") {
SendResults((string) llSin(OpToFloat(1) * gModeMultiplier), OP_FLOAT);
}
else if (Operator == "cos") {
SendResults((string) llCos(OpToFloat(1) * gModeMultiplier), OP_FLOAT);
}
else if (Operator == "tan") {
SendResults((string) llTan(OpToFloat(1) * gModeMultiplier), OP_FLOAT);
}
else if (Operator == "asin") {
// Bounds check.
float Operand = OpToFloat(1);
if (Operand < -1.0)
Operand = -1.0;

if (Operand > 1.0)
Operand = 1.0;

SendResults((string) (llAsin(Operand) / gModeMultiplier), OP_FLOAT);
}
else if (Operator == "acos") {
// Bounds check.
float Operand = OpToFloat(1);
if (Operand < -1.0)
Operand = -1.0;

if (Operand > 1.0)
Operand = 1.0;

SendResults((string) (llAcos(Operand) / gModeMultiplier), OP_FLOAT);
}
else if (Operator == "atan") {
SendResults((string) (llAtan2(OpToFloat(1), 1) / gModeMultiplier), OP_FLOAT);
}

else if (Operator == "sinh") {
SendResults((string) Sinh(OpToFloat(1)), OP_FLOAT);
}
else if (Operator == "cosh") {
SendResults((string) Cosh(OpToFloat(1)), OP_FLOAT);
}
else if (Operator == "tanh") {
SendResults((string) Tanh(OpToFloat(1)), OP_FLOAT);
}

else if (Operator == "asinh") {
SendResults((string) Asinh(OpToFloat(1)), OP_FLOAT);
}
else if (Operator == "acosh") {
// Check domain.
float Operand = OpToFloat(1);
if (Operand < 1.0) {
DomainError(Operator);
return;
}
SendResults((string) Acosh(Operand), OP_FLOAT);
}
else if (Operator == "atanh") {
// Check domain.
float Operand = OpToFloat(1);
if (Operand <= -1.0 ||
Operand >= 1.0) {
DomainError(Operator);
return;
}
SendResults((string) Atanh(Operand), OP_FLOAT);
}
else if (Operator == "ln") {
// Check domain.
float Operand = OpToFloat(1);
if (Operand <= 0.0) {
DomainError(Operator);
return;
}
SendResults((string) Ln(Operand), OP_FLOAT);
}
else if (Operator == "log") {
// Check domain.
float Operand = OpToFloat(1);
if (Operand <= 0.0) {
DomainError(Operator);
return;
}
SendResults((string) (Ln(Operand) / LOG_10), OP_FLOAT);
}

return;
}
if (channel == MODE_DEGREE) {
// Set the mode multiplier.
gModeMultiplier = DEG_TO_RAD;
Say("Trig is now in degree mode.");
return;
}
if (channel == MODE_RADIAN) {
// Set the mode multiplier.
gModeMultiplier = 1.0;
Say("Trig is now in radian mode.");
return;
}
}
}


XyCalc OpHandler Misc
CODE

////////////////////////////////////////////
// XyCalc OpHandler Misc Script
//
// Written by Xylor Baysklef
////////////////////////////////////////////

/////////////// CONSTANTS ///////////////////
// These are the operators this script handles.
list HANDLED_OPERATORS = [ "abs", "rand", "round",
"floor", "ceil"];

// OpHandler channels.
integer OP_HANDLER_REQUEST = 14100;
integer OP_HANDLER_RETURN = 14101;

// Bit-wise operand types.
integer OP_OPERATOR = 0;
integer OP_INTEGER = 1;
integer OP_FLOAT = 2;
integer OP_NUMBER = 3;
integer OP_VECTOR = 4;
integer OP_ROTATION = 8;
integer OP_STRING = 16;
integer OP_PARAMS = 32;
//integer OP_RESERVED = 64;
integer OP_ANY = 127;
integer OP_MULTIPLIER = 128;


///////////// END CONSTANTS ////////////////

///////////// GLOBAL VARIABLES ///////////////
// These are the operands we are working on.
list gOperandList;
/////////// END GLOBAL VARIABLES ////////////


////////// OPERATOR LIST FUNCTIONS ///////////
integer GetOpType(integer op_number) {
return llList2Integer(gOperandList, op_number * -2 + 1);
}

integer OpToInteger(integer op_number) {
return llList2Integer(gOperandList, op_number * -2);
}
float OpToFloat(integer op_number) {
return llList2Float(gOperandList, op_number * -2);
}
vector OpToVector(integer op_number) {
return llList2Vector(gOperandList, op_number * -2);
}
rotation OpToRotation(integer op_number) {
return llList2Rot(gOperandList, op_number * -2);
}

//////// END OPERATOR LIST FUNCTIONS ////////

SendResults(string result, integer type) {
// Send back the results.
llMessageLinked(LINK_SET, OP_HANDLER_RETURN, llList2CSV(
[result, type]), "");
}

Error(string operator, string mesg) {
// Send back the results as an error.
llMessageLinked(LINK_SET, OP_HANDLER_RETURN, llList2CSV(
["ERROR", operator, mesg]), "");
}


DivideByZero(string operator) {
Error(operator, "Divide by zero");
}

default {
state_entry() {
}

link_message(integer sender, integer channel, string data, key id) {
if (channel == OP_HANDLER_REQUEST) {
// Split up the data.
list Parsed = llCSV2List(data);

// See if we need to handle this request.
string Operator = llList2String(Parsed, 0);

if (llListFindList(HANDLED_OPERATORS, [Operator]) == -1)
// No, ignore it.
return;

// Extract the operands.
gOperandList = llList2List(Parsed, 1, -1);

// Check the operator, and execute it.
if (Operator == "abs") {
// If operands is an integer, keep the result
// an integer.
if (GetOpType(1) == OP_INTEGER)
// Stay in integer form.
SendResults((string) llAbs(OpToInteger(1)), OP_INTEGER);
else
// Go to float form.
SendResults((string) llFabs(OpToFloat(1)), OP_FLOAT);
}
else if (Operator == "rand") {
// If operands is an integer, keep the result
// an integer.
if (GetOpType(1) == OP_INTEGER) {
// Stay in integer form. Return 1 to Operand
integer Operand = OpToInteger(1);

integer Result;
// Check for 0.
if (Operand == 0)
Result = 0;
// Otherwise check sign, and send back results.
else if (Operand < 0) {
Result = llFloor(llFrand(Operand));
// This is a very small possibility, but...
if (Result == 0)
Result = -1;
}
else { // Operand > 0
Result = llCeil(llFrand(Operand));
// This is a very small possibility, but...
if (Result == 0)
Result = 1;
}
// Send back the results.
SendResults((string) Result, OP_INTEGER);
}
else
// Go to float form.
SendResults((string) llFrand(OpToFloat(1)), OP_FLOAT);
}
else if (Operator == "round") {
SendResults((string) llRound(OpToFloat(1)), OP_INTEGER);
}
else if (Operator == "floor") {
SendResults((string) llFloor(OpToFloat(1)), OP_INTEGER);
}
else if (Operator == "ceil") {
SendResults((string) llCeil(OpToFloat(1)), OP_INTEGER);
}
return;
}
}
}
Xylor Baysklef
Scripting Addict
Join date: 4 May 2003
Posts: 109
04-02-2004 06:36
Francis Chung has added a patch to the XyCalc OpHandler +-*/^%&~ script for float modulus =D. I have applied it to the listed code, but here is a quick loook at the changes if you don't want to track down the changes:

This is in the script: "XyCalc OpHandler +-*/^%&~"
CODE


.
.
.

DivideByZero(string operator) {
Error(operator, "Divide by zero");
}

///// Start Patch by Francis Chung /////////////////////////////////////

// Fran wuz here
//
// The `fmod' function returns the value X-I*Y, for the
// largest integer I such that, if Y is nonzero, the result
// has the same sign as X and magnitude less than the magni-
// tude of Y.

// Raises divide by zero when y = 0, but as far as I know, LSL
// doesn't allow me to specify NaN, soo...
float fmod( float x, float y ) {
float sign = 1;
float maxI = 1;
float k;

if ( x ) {
sign = x/llFabs(x);
x = llFabs(x);
}

if ( y )
y = llFabs(y);
else {
DivideByZero("%");
return 0;
}

while ( x > maxI * y )
maxI *= 2;

for( k = maxI/2; k >= 1; k/=2 ) {
if ( k*y < x )
x -= k*y;
}

return x * sign;
}
////// End Patch by Francis Chung //////////////////////////////////////

default {
state_entry() {
}

.
.
.
.
.

// Go to float form.
SendResults((string) Result, OP_FLOAT);
}
else if (Operator == "%") {
///// Start Patch by Francis Chung /////////////////////////////////////
if ( GetOpType(1) == OP_INTEGER && GetOpType(2) == OP_INTEGER ) {
SendResults((string) (OpToInteger(1) % OpToInteger(2)), OP_INTEGER);
}
else {
float x = 0;
float y = 1;
if ( GetOpType(1) == OP_INTEGER )
x = OpToInteger(1);
if ( GetOpType(1) == OP_FLOAT )
x = OpToFloat(1);

if ( GetOpType(2) == OP_INTEGER )
y = OpToInteger(2);
if ( GetOpType(2) == OP_FLOAT )
y = OpToFloat(2);

SendResults((string) fmod(x,y), OP_FLOAT);
}
////// End Patch by Francis Chung //////////////////////////////////////
}
else if (Operator == "&") {
SendResults((string) (OpToInteger(1) & OpToInteger(2)), OP_INTEGER);
}
.
.
.




Thanks Francis!

Xylor