Don't click here unless you want to be banned.

LSL Wiki : debugging

HomePage :: PageIndex :: RecentChanges :: RecentlyCommented :: UserSettings :: You are crawl338.us.archive.org

Debugging


So you've written a script, thought you had everything perfect, tried compiling it, and BOOM!

Down went the simulator :-)

(Un)fortunately, that's not how many debugging escapades begin. Even so, it can be a rather daunting task if you don't know where or how to start fixing your script when something goes wrong. "Debugging" as we scripterati call it, is the process by which bugs, mistakes, are removed from the script. There are two main kinds of problems we focus on when we debug, syntax errors and logic errors.

Syntax Errors

These are often called the easiest to fix errors, since the LSL compiler tells you exactly how to fix them when you press the "Save" button. NOT!
Often times, the LSL compiler complains about a line of text completely unrelated to the problem its actually having. Take this for example:

default 
{
    state_entry()
    {
        integer hungry = TRUE;
        if (hungry)
            llSay(0, "I want BAGELS!");
        }
    }
}

When you hit the save button the LSL compiler spits out:
(9, 0) : ERROR: Syntax error
...and leaves the blinking cursor at the last line of the script. The LSL compiler (unlike the Python interpreter) has absolutely no idea where you put whitespace (spaces, newlines) in your script. To it, the above looks like:

default{state_entry(){integer hungry=TRUE;if(hungry)llSay(0,"I want BAGELS!");}}}

The compiler is pendantic about braces because they signify where you should create and destroy scopes and all the variables you declare within them. In order to keep all of this straight, the compiler increments a counter when it encounters an open brace, and decrements it when it encounters a closed brace. If this counter is nonzero once it reaches the end of the script, you either have too many or not enough opening or closing braces. Unfortunately, it tells you this by giving you the extremely general Syntax error. This is where good brace indention style comes in handy. The LSL compiler can't see whitespace, but *you* can! check to see if all of your open braces have a corresponding closed brace, and vice-versa.


Here's another snag people usually hit upon:
default 
{
    state_entry() 
    {
        integer hungry = TRUE;
        if (hungry)
        {
            llSay(0, "I want BAGELS!")
        }
    }
}

In this example, the LSL compiler pulls another confusing whitespace-dependant trick on us. Here, it spits out:
(8, 8) : ERROR : Syntax error
...leaving the blinking cursor before the closing brace after the llSay line. The LSL compiler does this because it's expecting a emicolon to end the llSay() statement, but runs into a closing brace (and because the LSL compiler is stupid, it simply stops at the closing brace). Easiest way to fix these problems - check that all your statements end with a semicolon.


Now you may be asking, "Is there any other way to check my code's syntax without using SL's LSL compiler?" I'm very happy to state YES! The dazzling Masakazu Kojima has written an external program, called lslint, that checks the syntax of LSL scripts. You can access it online here or join in the forum discussion about it here. It shows you in greater detail where your script's syntax is off, and even makes suggestions! It slices, dices and even makes julian fries!

Logic Errors

These beasts range from the simplest "why is this code not running" to the extreme "OMG MY OBJECT EXPLODED!" These errors are not caught by the LSL compiler, because they're errors in what you told SL to do. Computers are intrinsically stupid - they lack intuition - so they will do exactly what you tell them to do, no matter how much that differs from what you intend for them to do or from what any sane person would want them to do. Here are some of the most common ones:

Semicolon After Condition
default 
{
    state_entry()
    {
        integer hungry = FALSE;
        if (hungry);
        {
            llSay(0, "I want BAGELS!");
        }
    }
}

This script compiles, but the llSay statement will always execute, even when hungry is FALSE (as is seen here). - This is because the semicolon after the conditional statement completely negates the conditional's effects, turning it from a scope-starter into a statement that has no effect. To fix this, remove the semicolon from the end of the conditional statement.

Use of == Instead of =, and Vice-Versa
integer FOOD_ITEMS = INVENTORY_OBJECT;
integer hungry;
default
{
    state_entry()
    {
        // Use of == instead of =
        hungry == TRUE;
        if (hungry)
        {
            llSay(0, "I want BAGELS!");
        }
        
        // Use of = instead of ==
        integer foodCount = llGetInventoryNumber(FOOD_ITEMS);
        if (foodCount = 1)
        {
            llSay(0, "Time to stock up on groceries, this script is all that's left!");
        }
    }
}

Unfortunately, this script compiles without any problems, but will never print the "BAGELS!" line and will always print the "groceries" line. Check that variable assignments use the = operator, and equality comparisons use the == operator.

Both of these logic errors occur due to the way LSL's syntax has mimiced C's.

Similarly, watch out for Boolean vs. Bitwise operators
Just as "=" can end up where you meant to have "==", so can "&" vs. "&&" and "|" vs. "||". These bugs can be even more difficult to find because the potential exists that a bitwise operation will yield a result that can then be interpreted as a boolean value. Half of the time your code may seem to be working fine, depending on the circumstances!


General Solution: Isolate-and-Conquer
Logic errors, especially the ones that cause the script to sit there doing nothing when you expect it to do something, can be very tough to find when you're trying to fix them by tinkering with the entire script as a whole. Test small chunks of your script, one part at a time, to make sure they are doing exactly what you want them to. This is easiest if you have divided your script into separate functions.

Finding the source of logic errors can often be like searching for a needle in a haystack. Using this principle, you can throw chunks of haystack off to the side once you're done searching through them.

General Solution: Variable Dumps
If your script isn't easily separable into self-contained chunks, the isolate-and-conquer approach might not be the best solution for you. Instead, at key points in your script (where important data is manipulated or a loop is entered/exited) insert llOwnerSay statements that print out the contents of any relevant variables. This way you can tell what goes wrong with the manipulation or if your script goes into an infinite loop.


Errors
Comments [Hide comments/form]
Is there any way to something like this?

expose(string name)
{
  string value = xxValueOf(name);
  llSay(0, name + " is '" + value + "'");
}

I've found this kind of thing to be handy in lots of other languages.
-- PetreLamar (2006-07-19 11:14:07)
BTW, calling it like this:
some_func()
{
  string test_string = "foo";
  expose("test_string");
}
should produce "test_string is 'foo'".
-- PetreLamar (2006-07-19 11:18:15)
No, there's no built in way to do that. LSL is compiled, not interpreted.
-- KeknehvPsaltery (2006-07-20 18:40:59)
LSL doesn't need to be interpreted to have this feature. For example, Java is compiled, but it has introspection.

BTW, I wrote up a description of such a feature, but KellyLinden's suggestion box is missing from SL!
-- PetreLamar (2006-07-21 23:15:42)
*LAUGHS* everyone please read:

(This is from top of page:)
Often times, the LSL compiler complains about a line of text completely unrelated to the problem its actually having. Take this for example:
default
{
state_entry()
{
integer hungry = TRUE;
if (hungry)
llSay(0, "I want BAGELS!")
}
}
}

The author TRIES to say that the compiler is giving an error at the END of the file, where it's "supposed" to be erroring near the if (hungry).

That is NOT true. The compiler IS indeed working properly. It sees the end bracket after llSay() as the closure to state_entry(). So in fact, the erroring at the end of the file thing... That is proper... If state_exit() closes, and default closes, then there is an extra end bracket there == ERROR. ;)
-- BirdRaven (2006-07-22 10:57:38)
Bird, while you are technicaly correct, the example chosen isn't very good. If there were more events before that last bracket you would get an error at the next event. What we are talking about here, is finding bugs in the code and how the compiler does not help. The the point is an extra bracket can really screw things up, and that it can be hard to find the extra bracket.

For future refrence could you wrap your code in %%(lsl) *code* %%.
-- BlindWanderer (2006-07-22 12:04:15)
The author is right in his example. The compiler is unable to detect invisible factors affecting logic unlike humans. The compiler reacts only to existing observations. Humans know to look for not yet seen observations too when an overall pattern is triggered in their mind. (At least I'm pretty good at that.) The compiler doesn't know much about 'the big picture'.

Implementing a smart compiler for a programming language would be a waste of time. But a must for natural language processing.
-- e212-246-67-151.elisa-laajakaista.fi (2007-04-26 11:24:52)
Feel free to choose a more applicable example.
This is a wiki after all :-P
-- ChristopherOmega (2007-06-07 18:15:59)
Attach a comment to this page: