Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Parallel

Using the Linden Scripting Language


Advanced Language Features

Like other languages that share C-like syntax, blocks and scope are fundamental to understanding how to write good code in LSL. Generally speaking, in LSL a code block is any code set off by braces within a global function or an event handler, as shown here:

string GlobalString = "Hi!"; // this global variable is
                             // visible anywhere
                             // in the LSL program
integer factorial(integer n) // this is a user-defined
                             // function more on them
                             // below
{                            // this brace begins the code
                             // associated with
                             // the function factorial
string local = "Bye!";       // a local variable is
                             // visible in the block it is
                             // created in and any new
                             // blocks created within that
                             // block
llSay(0, GlobalString);      // global variables can be
                             // used anywhere in the LSL
                             // program
llSay(0, local);             // local is available anywhere
                             // inside the factorial
if (n < 3)
{                   // the if statement creates another code block
   float pi = 3.14; // pi is only available
                    // inside the if block
   llSay(0, local); // local is available
                    // anywhere inside factorial
                    // including inside new
                    // blocks created within it
   llSay(0, (string)pi); // this works since we're
                    // still inside the block
   return n;        // the return statement
                    // jumps back to the calling
                    // function or event handler
}                   // end of the if block
else
{                     // the else clause creates another block
   float e = 2.71828; // e is only available inside
                      // the else block
   llSay(0, (string)pi); // ERROR!! We aren't in the
                         // block that made pi
                         // so this is an error!
   return n*factorial(n - 1); // this is recursion,
                              // more later
   }                // end of the else block
   llSay(0, local); // local is available
                    // anywhere inside factorial
    }               // end of the factorial block
    default
    {              // states create scope in the sense that they
                   // determine which event handlers will
                   // be called
   state_entry()
   {                // this starts a block in state_entry()
   integer num = 1; // the variable do_something
                    // is a local
   if (num)
      num = 4;      // even though there are no braces,
                    // this line is actually a code block
   llSay(0, GlobalString); // global variables may
                           //be used anywhere in the
                           // LSL program
   llSay(0, (string)factorial(num));
                           // user-defined functions may be
                           // called form any event handler.
                           // This will say "24".
   }                       // end of the state_entry block
}                          // end of the default state

There is a lot going on in the simple example program. Although it is a rather silly bit of code, it shows where blocks begin and end, and introduces the idea of scope. For variables and functions, scope defines when they may be called or used. Within LSL, there are two levels of scope: global and local.

Global scope applies to user-defined functions and global variables. Both global variables and user-defined functions may be used anywhere in an LSL program; thus they are considered to be "globally accessible" and therefore of global scope. In the preceding example, the function factorial and the string GlobalString are globals.

Local scope applies to variables created within code blocks (there are no local functions in LSL). As the example shows, functions, event handlers, and flow control create blocks, and these blocks may have further blocks nested within them. The scope for a local variable is the code block it was created within, as well as any new blocks created within that block. Consider this example:

{             // a code block
   integer true = TRUE;
   list foo = ["I", "am", "a", "list"]; // a local list
   if (true) 
   {        // a new code block
      foo = []; // we can still see foo,
                //so assigning an empty
                // list to it
   }
}

Note that these scope rules can allow some really bad coding habits, like in the following example:

{
   float pi = 3.14;
   if (pi) // if a floating point value
                                               // is used in an
                                               // expression, it is FALSE
                                               // if it is exactly
                                               // equal to 0.f,
                                               // and TRUE for any other value                                      // This is a bad habit,
                                              // since floating point
                                              // values are often near to 0.f
                                              // but not exactly
                                              // equal to 0.f.
{                                            // start of if code block
       integer pi = 3; // this local pi has the same name
                                              // as pi from
                                              // the earlier scope and
                                              // is said to "shadow"
                                              // the other variable.
       llSay(0, (string)pi);          // Which pi will be used?
   }                                         // end of the if block
   else
   {                                        // start of the else block
       string pi = "3.1415";      // Ack! Another pi
        llSay(0, (string)pi);        // Now which pi will be
                                             // used?
   }
}

Please do not ever write code like this! Although it compiles and (if you are very fortunate) may even work, it will cause you_or anyone you share your code with -- headaches and confusion.

Now that you understand scope, let us turn to functions. In the last section we introduced global functions, the first of the two types of functions in Second Life. The second type, library functions, will be discussed shortly. User-defined functions allow you to create blocks of code that perform a specific task or calculation. They are placed above the default state, either before or after global variables, and are written as follows:

type function_name(type parameter1, type parameter2, . . .)
{
   // do something in here
}

The type is the function's return type, which means that if you want the function to return a value after it is called, you need to specify the type. For example, if you wanted to take the absolute value of a float, you could write the following function:

float fabs(float num)
{
   if (num > 0.f) // already a positive number, just return it
      return num; // the return command returns the value of
                  // the expression that follows it
   else
     return -num; // the negation operator returns the -1*num
}

This would be used as follows:

float negone = -1.f;
float posone = fabs(negone); // passes -1.f to fabs, returns 1.f
llSay(0, (string)posone); // will say "1.f"

Notice that fabs takes one parameter of type float. Functions can have any number of parameters (including zero). So, the following are all legal formats for naming a function:

do_something()
string do_something_else()
vector do_something_too(string target)
list do_something_now(integer number, rotation rot1, rotation rot2)

Two important features of LSL functions are that their parameters are passed by value and they support recursion. To understand the concept of pass by value, look at the following function:

integer add_one(integer n)
{
n = n + 1;
return n;
}
integer number = 10;
number = add_one(number);

So, what is the value in number? As you would hope, the value is 11. This is because when number is passed into add_one, it is passed in as its value, 10. Thus, any operations on n within the function are acting on a local copy, not on the variable number. Generally, this is what you want to have happen, but it means that in order for your function to return a value, you have to use the return command. If you had written add_one as follows, the number would still be 10:

add_one(integer n)
{
n = n + 1;
}
integer number = 10;
add_one(number);

User-defined functions are covered in considerable detail at http://lslwiki.com/lslwiki/wakka.php?wakka=UserDefinedFunction.

The second type of function in LSL is library functions (Figure 8.4). They are built-in functions that are there to perform common tasks or to provide functionality that would be difficult to write in LSL directly. More than 300 functions are built into LSL, and more are being added regularly.) For a comprehensive description of all of the functions, check out http://lslwiki.com/lslwiki/wakka.php?wakka=functions.) It is important to recognize that LSL functions operate just like the user-defined functions. They are available within any event handler or user-generated function, their arguments are passed by value, and they may or may not return a value. One additional aspect is that some of them have a delay value associated with them. This delay exists to protect Second Life from certain types of abuse.

Figure 8.4: Libraryfunction list


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.