Table of Contents

Shortnote

The following is a postfix calculator that transforms any text spoken by the owner of the script to postfix notation and then evaluates the expression.

Implemented Features

Exports

Operator Precedence

From highest to lowest, the precedence is given by the last element in the array op in the wasInfixToPostfix function. In order from highest to lowest:

Operator
sin,cos,tan,asin,acos,ln,sqrt
^
*, /
%
+, -

Examples

Șpecıaŀ K(gonor.chunker): ln(2+2)*3+50/2
Object whispers: Postfix is: 2 2 + ln 3 * 50 2 / +
Object whispers: Value is: 25.000000
Șpecıaŀ K(gonor.chunker): 2*10/20
[WaS-K] Calculator whispers: Postfix is: 2 10 20 / *
[WaS-K] Calculator whispers: Value is: 1
Șpecıaŀ K(gonor.chunker): 2^10/10
[WaS-K] Calculator whispers: Postfix is: 2 10 ^ 10 /
[WaS-K] Calculator whispers: Value is: 102.400000

Code

This script was tested and works on OpenSim version 0.7.4!

expression_calculator.lsl
///////////////////////////////////////////////////////////////////////////
//  Copyright (C) Wizardry and Steamworks 2012 - License: GNU GPLv3      //
//  Please see: http://www.gnu.org/licenses/gpl.html for legal details,  //
//  rights of fair usage, the disclaimer and warranty conditions.        //
///////////////////////////////////////////////////////////////////////////
 
//////////////////////////////////////////////////////////
// Most of the helper functions can be found at:
// http://grimore.org/fuss:lsl
//////////////////////////////////////////////////////////
 
//////////////////////////////////////////////////////////
// Returns a reversed list.
//////////////////////////////////////////////////////////
list wasListReverse(list lst) {
    if(llGetListLength(lst)<=1) return lst;
    return wasListReverse(llList2List(lst, 1, llGetListLength(lst))) + llList2List(lst,0,0);
}
 
//////////////////////////////////////////////////////////
// Deletes elements delete from list input.
//////////////////////////////////////////////////////////
list wasSubtractSubList(list input, list delete) {
    do {
        string tok = llList2String(delete, 0);
        list clean = input;
        do {
            if(llList2String(clean, 0) == tok) {
                integer idx = llListFindList(input, (list)tok);
                input = llDeleteSubList(input, idx, idx);
            }
        } while(clean = llDeleteSubList(clean, 0, 0));
    } while(delete = llDeleteSubList(delete, 0, 0));
    return input;
}
 
//////////////////////////////////////////////////////////
// Returns a list of operators and operands.
//////////////////////////////////////////////////////////
list wasInfixTokenize(string input) {
    list op = [ "+", "-", "(", ")", "%", "*", "/", "^", "sin", "asin", "cos", "acos", "tan", "sqrt", "ln" ];
    list result = llParseString2List(input, [], op);
    return wasSubtractSubList(result, [" "]);
}
 
//////////////////////////////////////////////////////////
// Transforms an infix expression to a postfix expression.
//////////////////////////////////////////////////////////
list wasInfixToPostfix(list infix) {
    list op = [  "+", "-", "%", "*", "/", "^", "sin", "asin", "cos", "acos", "tan", "sqrt", "ln" ];
    list opStack = [];
    list result = [];
    do {
        string t = llList2String(infix, 0);
        infix = llDeleteSubList(infix, 0, 0);
        if(t == "(") {
            opStack += "(";
            jump continue;
        }
        if(t == ")") {
            while(llGetListLength(opStack) != 0) {
                string topa = llList2String(opStack, llGetListLength(opStack)-1);
                opStack = llDeleteSubList(opStack, llGetListLength(opStack)-1, llGetListLength(opStack)-1);
                if(topa != "(" && topa != ")") result += topa;
            }
            opStack = llDeleteSubList(opStack, llGetListLength(opStack)-1, llGetListLength(opStack)-1);
            jump continue;
        }
        integer idx = llListFindList(op, (list)t);
        if(idx == -1) {
            result += t;
            jump continue;
        }
@repeat;
        string topb = llList2String(opStack, llGetListLength(opStack)-1);
        integer odx = llListFindList(op, (list)topb);
        if(odx >= idx) {
            opStack = llDeleteSubList(opStack, llGetListLength(opStack)-1, llGetListLength(opStack)-1);
            result += topb;
            if(llGetListLength(opStack) != 0) jump repeat;
        }
        opStack += t;
@continue;
    } while(llGetListLength(infix) != 0);
    result += wasListReverse(opStack);
    return result;   
}
 
//////////////////////////////////////////////////////////
// Evaluate a postfix expression.
//////////////////////////////////////////////////////////
float wasPostfixEval(list postfix) {
    list op = [ "+", "-", "%", "*", "/", "^", "sin", "asin", "cos", "acos", "tan", "sqrt", "ln" ];
    list orStack = [];
    do {
        string t = llList2String(postfix, 0);
        postfix = llDeleteSubList(postfix, 0, 0);
        integer idx = llListFindList(op, (list)t);
        if(idx == -1) {
            orStack += t;
            jump continue;
        }
        float a = llList2Float(orStack, llGetListLength(orStack)-1);
        orStack = llDeleteSubList(orStack, llGetListLength(orStack)-1, llGetListLength(orStack)-1);
        float b = llList2Float(orStack, llGetListLength(orStack)-1);
        float r = 0;
        if(t == "+") {
            orStack = llDeleteSubList(orStack, llGetListLength(orStack)-1, llGetListLength(orStack)-1);
            r = b + a;
            jump push;
        }
        if(t == "-") {
            orStack = llDeleteSubList(orStack, llGetListLength(orStack)-1, llGetListLength(orStack)-1);
            r = b - a;
            jump push;
        }
        if(t == "*") {
            orStack = llDeleteSubList(orStack, llGetListLength(orStack)-1, llGetListLength(orStack)-1);
            r = b * a;
            jump push;
        }
        if(t == "/") {
            orStack = llDeleteSubList(orStack, llGetListLength(orStack)-1, llGetListLength(orStack)-1);
            if(a == 0) {
                r = (float)"NaN";
                jump push;
            }
            r = b / a;
            jump push;
        }
        if(t == "^") {
            orStack = llDeleteSubList(orStack, llGetListLength(orStack)-1, llGetListLength(orStack)-1);
            r = llPow(b,a);
            jump push;
        }
        if(t == "%") {
            orStack = llDeleteSubList(orStack, llGetListLength(orStack)-1, llGetListLength(orStack)-1);
            r = (integer)b % (integer)a;
            jump push;
        }
        if(t == "sin") {
            r = llSin(a * DEG_TO_RAD);
            jump push;
        }
        if(t == "asin") {
            r = llAsin(a * DEG_TO_RAD);
            jump push;
        }
        if(t == "cos") {
            r = llCos(a * DEG_TO_RAD);
            jump push;
        }
        if(t == "acos") {
            r = llAcos(a * DEG_TO_RAD);
            jump push;
        }
        if(t == "tan") {
            r = llTan(a * DEG_TO_RAD);
            jump push;
        }
        if(t == "ln") {
            r = llLog(a);
            jump push;
        }
        if(t == "sqrt") {
            r = llSqrt(a);
        }
@push;
        orStack += r;
@continue;
    } while(llGetListLength(postfix) != 0);
    return llList2Float(orStack, 0);
}
 
default
{
    state_entry() {
        llListen(0, "", llGetOwner(), "");
    }
    listen(integer chan, string name, key id, string message) {
        // Tokenize the message.
        list tokenized = wasInfixTokenize(message);
        // Convert infix to postfix.
        list postfixExpression = wasInfixToPostfix(tokenized);
        // Evaluate postifx expression.
        float result = wasPostfixEval(postfixExpression);
 
        llWhisper(0, "Postfix is: " + llDumpList2String(postfixExpression, " "));
        llWhisper(0, "Value is: " + (string)result);
    }
}