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+1XyCalc v1.0: 2You can also use parenthesis to input more complex commands: xc sqrt(3^2 + 4^2)XyCalc v1.0: 5.0XyCalc handles variables, and has a few constants of its own. You can assign a variable using the '=' operator: xc W = 5.0XyCalc v1.0: 5.0xc L = 4.0XyCalc v1.0: 4.0xc A = W * LXyCalc v1.0: 20.0You can list which variables are set, by using the "xc vars" command: xc varsXyCalc v1.0: a = 20.0 XyCalc v1.0: w = 5.0 XyCalc v1.0: l = 4.0Note: 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 constantsXyCalc v1.0: pi = 3.141593 XyCalc v1.0: e = 2.718282 XyCalc v1.0: ans = 20.0Notice 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.707107xc asin(ans)XyCalc v1.0: 45.000019You can also assign a variable embedded in your command: xc sin( theta=asin( sqrt(2) / 2) )XyCalc v1.0: 0.707107xc thetaXyCalc v1.0: 45.000019You can switch between Radian and Degree mode for trig commands by saying: xc radiansXyCalc v1.0: Trig is now in radian mode.or xc degreesXyCalc 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, logFuture 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 //////////////////////////////////////////// // 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 //////////////////////////////////////////// // 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 //////////////////////////////////////////// // 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 +-*/^%&|~ //////////////////////////////////////////// // 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 //////////////////////////////////////////// // 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 //////////////////////////////////////////// // 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 +-*/^%&~"
. . .
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
|