Jump to content

Type Changes with LSL Expressions - Looking for cases where "Resulting Type" doesn't match "Expression" Type


Love Zhaoying
 Share

You are about to reply to a thread that has been inactive for 186 days.

Please take a moment to consider if this thread is worth bumping.

Recommended Posts

I've been working on my Parser and am coming back around to an "exception": Some LSL Expressions result in a Type that is different from the "Left Operand" in the Expression.

Reviewing my notes and the LSL Wiki, I only found this one:

Float = Vector * Rotation

https://wiki.secondlife.com/wiki/Category:LSL_Vector

The example given:

float f = <9, 2, 7> * <4, 8, 10>; // 122.0

I thought that I saw a "type change" for one of the Rotation operation, but did not find it:

Vector = Vector * Rotation

Vector = Vector / Rotation

Here's my question: Are there any other LSL Expressions where the final Expression is a "different" type than the "Left Operand" of the Expression?

Thanks,

Love

Link to comment
Share on other sites

float = vector * vector. . .

float = integer ? float, for basically all opperators in place of '?'

string ?= key + string ; can you add a key and a string to get another string?

list = * + list ; preppend anything to a list

==,  !=, >, <, >=, <= all return integers regardless of the arguments.

unary opperators ~ ! ++ -- might need special consideration?

Edited by Quistess Alpha
  • Thanks 1
Link to comment
Share on other sites

10 minutes ago, Quistess Alpha said:

float = vector * vector. . .

float = integer ? float, for basically all opperators in place of '?'

string ?= key + string ; can you add a key and a string to get another string?

list = * + list ; preppend anything to a list

==,  !=, >, <, >=, <= all return integers reglardless of the arguments.

Awesome, thanks! Some of these I had right!  Others, I missed somehow (or intepreted slightly differently).

11 minutes ago, Quistess Alpha said:

float = vector * vector. . .

This one I somehow missed. (Weird!)

12 minutes ago, Quistess Alpha said:

float = integer ? float, for basically all opperators in place of '?'

For this one, it's a shame that LSL doesn't automatically cast floats to integers (if the left-side of your example was an Integer variable, you'd get a Syntax Error).

13 minutes ago, Quistess Alpha said:

string ?= key + string ; can you add a key and a string to get another string?

I'll have to check how I did this one.  I know that I allow treating Keys "as strings"..

13 minutes ago, Quistess Alpha said:

list = * + list ; preppend anything to a list

I did not know you could do this!  However, I don't prepend things to lists like, ever. lol! (So I forgive me.)

14 minutes ago, Quistess Alpha said:

=,  !=, >, <, >=, <= all return integers reglardless of the arguments.

Hmm...this may be true, but "&&" and "||", etc. require both sides to be "integers" AFAIK. Right?

 

Link to comment
Share on other sites

1 minute ago, Quistess Alpha said:
3 minutes ago, Love Zhaoying said:

&&" and "||", etc. require both sides to be "integers" AFAIK. Right?

I think so, but it may typecast floats to integers implicitly; would have ton test.

Related - I was researching BASIC languages since my initial use-case for a Parser is BASIC=>LSL.

And, most BASIC languages don't have separate "logical" vs. "binary/integer" AND/OR, etc.  like LSL and C/C++ do..

So, that raises a special case for me, whether I will add a "BAND" / "BOR" for those cases or do some implicit conversion.  I don't WANT to do a lot, since my target is LSL.

But when I make a Parser use-case for LSL=>LSL it won't be an issue.

Link to comment
Share on other sites

46 minutes ago, Love Zhaoying said:
1 hour ago, Quistess Alpha said:

float = vector * vector. . .

This one I somehow missed. (Weird!)

Wierd?  Nah.  That's just basic matrix algebra.   

vector A = < A1, A2, A3>

vector B = < B1, B2, B3 >

in which each of the elements in A and B are floats, then

vector A * vector B =  A1*B1 + A2*B2 + A3*B3    <---- a float

If any of the elements are integers, I think LSL does implicit typecasting to convert them to floats before multiplying.

  • Like 1
  • Thanks 1
Link to comment
Share on other sites

4 minutes ago, Rolig Loon said:
1 hour ago, Love Zhaoying said:

This one I somehow missed. (Weird!)

Wierd?  Nah.  That's just basic matrix algebra.   

I meant "weird" as in, I did not see its example on the Vectors LSL Wiki page earlier.

But now I see it!

Dot Product, Operator "*"

float f = <9, 2, 7> * <4, 8, 10>; // 122.0
Link to comment
Share on other sites

scalar multiplication (and also division?) of vectors/rotations also I think may allow the scalar on the left,

For debugging, I think you can get the type of an expression by embedding it in a list and using llListStatistics. if you try to assign an expression to a variable to assume its type, you might accidently do an implicit typecast.

Edited by Quistess Alpha
  • Thanks 1
Link to comment
Share on other sites

17 minutes ago, Quistess Alpha said:

scalar multiplication (and also division?) of vectors/rotations also I think may allow the scalar on the left,

Thanks, I'll have to add that to my lookup. 

The "Commutative property" can't be "implied" due to the exceptions, plus I'd have to do 2 lookups for any "implied" commutative scenarios.

 

 

Link to comment
Share on other sites

14 hours ago, Quistess Alpha said:

scalar multiplication (and also division?) of vectors/rotations also I think may allow the scalar on the left,

For debugging, I think you can get the type of an expression by embedding it in a list and using llListStatistics. if you try to assign an expression to a variable to assume its type, you might accidently do an implicit typecast.

Wouldn't llGetListEntryType() be what you'd use? If not, please let me know how you'd use llListStatistics() for this.

Example:

list MyList;

MyList = [expression]; // Where "expression" = Vector * Float for example..

llSay(0, "List Entry Type=" + (string)llGetListEntryType(MyList, 0);

 

Link to comment
Share on other sites

3 hours ago, Love Zhaoying said:

Wouldn't llGetListEntryType() be what you'd use? If not, please let me know how you'd use llListStatistics() for this.

Yeah, that's what I get for typing things and not looking it up on the wiki. I forgot llGetListEntryType() was its own function.

  • Thanks 1
Link to comment
Share on other sites

On 10/25/2023 at 3:51 PM, Love Zhaoying said:
On 10/25/2023 at 3:35 PM, Quistess Alpha said:

string ?= key + string ; can you add a key and a string to get another string?

I'll have to check how I did this one.  I know that I allow treating Keys "as strings"..

This turned out to be incorrect. You cannot add a "key" to anything! Tests in post below.

  • Like 1
Link to comment
Share on other sites

I have gone through all the test cases and my test code is below.  I will use this to correct / add missing validations to my current setup.

@Quistess Alpha and @Rolig Loon - since you replied earlier, can you please review and let me know of anything I may have missed?

As a result of this testing, I changed my setup specifically for:

- Adding different types to a List (append or prepend)

- Vector and Rotation operations

Thank you!

// Test Case 1.4 - Expressions with Type Restrictions and Type Changes

// The purpose of this function is to test Expressions with Operands of differing Types,
// for assignment to Identifiers of the "same Type", and of differing Types.

// For each test, the "left Operand" of the Expression is being tested.
// Note: Typecast operations are not part of this test.

// "Identity" operations are not being tested unless they are "exceptions", example: 
// integer + integer is not an "exception".

list TypeNames = ["Invalid", "Integer", "Float", "String", "Key", "Vector", "Rotation"];

GetExpressionType(list l, string sExpression) {
    llSay(DEBUG_CHANNEL, "Expression: " + sExpression + ", Type = " + llList2String(TypeNames,llGetListEntryType(l, 0)));
}

PerformTests() {

    string s;
    integer i;
    float f;
    key k;
    list l;
    vector v;
    rotation r;

/////////////////////
// 1-String

    llSay(DEBUG_CHANNEL, "1-String");
    
    s = "Test String";  // Initial value
    k = s;      // Assignment to Key
//    GetExpressionType([s + k], "s + k"); // Append Key - Type mismatch
    GetExpressionType([s == k], "s == k"); // Compare to Key - result always Integer
// TODO - Check notes from Quistess and Rollig  

/////////////////////
// 2-Integer

    llSay(DEBUG_CHANNEL, "2-Integer");
    
    i = 123;    // Initial value
    f = i;      // Assignment to Float - OK
    GetExpressionType([i + f], "i + f"); // Add Float - result is Float
    GetExpressionType([i == f], "i == f"); // Compare to Float - result always Integer

/////////////////////
// 3-Float

    llSay(DEBUG_CHANNEL, "3-Float");
    
    f = 4.56;   // Initial value
//    i = f;      // Assignment to Integer - Type mismatch
    GetExpressionType([f + i], "f + i");    // Add Integer - result is Float
    GetExpressionType([f == i], "f == i"); // Compare to Integer - result always Integer


/////////////////////
// 4-Key

    llSay(DEBUG_CHANNEL, "4-Key");
    
    k = llGetOwner(); // Initial value
    s = k;        // Assignment to String
//    GetExpressionType([k + s], "k + s"); // Append String - Type mismatch
    GetExpressionType([k == s], "k == s"); // Compare to String - result always Integer

/////////////////////
// 5-List

    llSay(DEBUG_CHANNEL, "5-List");
    
    l = ["Initial List"];   // Initial value
    s = "123";  // Initial value
    l = l + s;  // Append String or any type
    l = s + l;  // Prepend String or any type
    l = l + l;  // Append List
    if (l==[123]); // Compare lists = compares list length

/////////////////////
// 6-Vector

    llSay(DEBUG_CHANNEL, "6-Vector");
    
    v = llGetPos(); // Initial value
    v = v + v;  
    v = v - v;
    GetExpressionType([v * v], "v * v"); // Dot Product * Vector - result is Float
    f = 4.56;   // Initial value
    i = 1;      // Initial value
// NOTE: All tests below result in expression type of Vector, except "v * f", result is Float
    GetExpressionType([v * f], "v * f"); // Multiply * Float
// Remaining tests result in expression type of Vector 
    GetExpressionType([f * v], "f * v"); // Multiply * Float
    GetExpressionType([v * i], "v * i"); // Multiply * Integer
    GetExpressionType([i * v], "i * v"); // Multiply * Integer
    GetExpressionType([v / f], "v / f"); // Divide / Float
// Note: Next result is expected:
//    GetExpressionType([f / v], "f / v"); // Divide Float / Vector - Type Mismatch
    GetExpressionType([v / i], "v / i"); // Divide / Integer
// Note: Next result is expected:
//    GetExpressionType([i / v], "i / v"); // Divide Integer / Vector - Type Mismatch
    GetExpressionType([v % v], "v / v"); // Cross Product % Vector
    r = llGetRot(); // Initial value
    GetExpressionType([v * r], "v * r"); // Multiply * Rotation
    GetExpressionType([v / r], "v / r"); // Divide / Rotation

/////////////////////
// 7-Rotation
    llSay(DEBUG_CHANNEL, "7-Rotation");
    r = llGetRot(); // Initial value
    r = r + r;  // !! TODO - Note that this is not a good idea per the Wiki
    r = r - r;  // !! TODO -  Note that this is not a good idea per the Wiki
    GetExpressionType([r * r], "r * r"); // Multiply * Rotation
    GetExpressionType([r / r], "r / r"); // Divide / Rotation

}

default {
 
state_entry() {
    

    llSay(DEBUG_CHANNEL, "=================================================");
    llSay(DEBUG_CHANNEL, "Test Case 1.4 - Expressions with Type Restrictions and Type Changes");

    PerformTests();
    
    llSay(DEBUG_CHANNEL, "Script ran to completion.");

}   // state_entry
    
}

 

Edited by Love Zhaoying
Link to comment
Share on other sites

19 minutes ago, Frionil Fang said:

Only thing I see as missing is that rotation*vector and rotation/vector are invalid as well (are they truly missing in that case?), you can only rotate vectors from the right.

Thanks, good catch.

For Rotation and Vector, I only included test cases that I thought would work based on the Wiki and earlier replies.  On the Wiki page for Rotation, only the examples I included for "left operand of Rotation" are given.  So, for anything else I assumed "no examples, so I guess it's not valid".  (This assumption especially applied to Rotation and Vector types, where I did not assume anything was valid.)

I see my test line is wrong here, says "multiply" but should say "divide" (so I edited just now): 

 GetExpressionType([r / r], "r / r"); // Multiply * Rotation

Interestingly, the Wiki says:

In other words, to combine rotations, you use the multiply and divide operators. Don't try to use addition or subtraction operators on rotations, as they will not do what you expect. The multiply operation applies the rotation in the positive direction, the divide operation does a negative rotation. You can also negate a rotation directly, just negate the s component, e.g. X.s = -X.s.

So, I probably should "remove" the "addition and subtraction" option for Rotations!  ETA: Or leave in place since it compiles.  Guess I will leave it in place since I want my code to "work" for LSL, even if it is "bad LSL".

Note: I did add a few tests based on Quistess's feedback, which weren't in the Wiki.  

 

Edited by Love Zhaoying
Link to comment
Share on other sites

1 minute ago, Love Zhaoying said:

So, I probably should "remove" the "addition and subtraction" option for Rotations!

They are valid operations though, just don't make sense in a rotation way but as bunches of 4 float add/sub. Not terribly useful, unless you were using rotations just as float storage (or complex numbers! A rotation in the form <b, 0, 0, a> behaves like the complex number a+bi for addition, subtraction and multiplication, though not division.)

Link to comment
Share on other sites

1 minute ago, Frionil Fang said:

They are valid operations though, just don't make sense in a rotation way but as bunches of 4 float add/sub. Not terribly useful, unless you were using rotations just as float storage (or complex numbers! A rotation in the form <b, 0, 0, a> behaves like the complex number a+bi for addition, subtraction and multiplication, though not division.)

Good idea! Yep, just added this ETA to my previous reply:

ETA: Or leave in place since it compiles.  Guess I will leave it in place since I want my code to "work" for LSL, even if it is "bad LSL".

Edited by Love Zhaoying
Link to comment
Share on other sites

FYI, the "next level" of this for me will be: 

- Add missing operators to my setup 

- Add the "prepend" case for lists to my setup suggested by Quistess (list = anytype + list)

- Test expressions with additional operator level mixes with &&, ||, <=, >=, etc.

Then I can finally move on to something else..

Link to comment
Share on other sites

On 10/26/2023 at 6:51 AM, Love Zhaoying said:

For this one, it's a shame that LSL doesn't automatically cast floats to integers (if the left-side of your example was an Integer variable, you'd get a Syntax Error).

And it should not.  The idea is not to lose information, unless you explicitly tell it to — the set of decimal numbers (what floats are intended to represent) is a superset of the set of integer numbers, therefore it's only safe to implicitly cast in one direction.

Of course it's not actually that simple in practice — the assumption in the language design is that we're using 40-bit floats, where you have a full 32 bits of mantissa to match the corresponding integers.  That's not the case in LSL where we only get 24 bits of mantissa, but it's where that difference in casting comes from.

And even if you accept implicit rounding, then comes the question of how to do that rounding; round towards zero, round away from zero, round towards larger, round towards smaller, round towards even, or odd, etc. (I'm probably forgetting a few, too), plus the truncation versions of all of those ("rounding" is specifically what you do as a tie breaker on a perfect 0.5, truncation is simply moving the threshold to the integers instead of the centre-point between them), are all perfectly legitimate options, and I have seen a few math libraries that offer the full set.  So, implicit cast from float to integer is prevented to leave it to you to specify how you want those decimals to round.  (I once implemented a math library where you could parameterise the floating type with a rounding method to allow exactly such implicit casts, and never actually used it because it was mostly pointless.)

To be "correct", implicit casting of integers to floats should be prevented also unless the compiler can statically determine that the number will fit, but in practice most 32-bit integers that are subject to implicit float casting generally do actually fit in 24 bits, and it's assumed that you're aware floats are only approximations to begin with, so someone at some point long before SL was a thing decided to let that one slide.

Link to comment
Share on other sites

You are about to reply to a thread that has been inactive for 186 days.

Please take a moment to consider if this thread is worth bumping.

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now
 Share

×
×
  • Create New...