Phantasmal MUD Lib for DGD

Phantasmal Site > DGD > DGD Reference Manual > LPC > Statements

Statements

A statement is sort of a full sentence of instructions, made up from one or more expressions. Statements usually cover no more than a single line of code. Sometimes it's necessary to break it up though if it becomes too long simply to improve on legibility -- a little like avoiding long run-on sentences. For most statements you simply break the line between two words, but if you are in the middle of a string you need to add a backslash (\) at the end of the line in order for the gamedriver to understand what's going on.

DRIVER->message("This is an example of \
a broken string.\n");
    

However, breaking a statement with backslash is extremely ugly and makes the code hard to read. It's usually possible to break the line naturally at the end of a string, or between two operators of some kind, or even just split the string in half and add the two parts together with the + operator. The only time the backslash really is necessary is in #define-statements, which we'll mention later.

DRIVER->message("This is a better way of " +
                "breaking a string.\n");

Statements in LPC almost always end with ;. It's considered good practice to put a newline there as well. That is to say, you shouldn't put multiple statements on the same line if you can help it. Keeping them separate makes them easier to read.

The block statement

There are a lot of statements such as conditional statements that in certain circumstances execute only one specified statement and no more. Suppose you want to have several statements executed and not just one? Well, there's a special statement called block statement that will allow you to do that. A block is defined as starting with a { and ending with a }. Within that block you may have as many statements of any kind (including variable definitions) as you like. The block statement shouldn't end with a ;, though usually it doesn't matter if you accidentally put one there. Example:

if(my_var < 3)
{
  statement_one();
  statement_two();
  statement_three();
}
    

The ';' statement

As stated ; is mostly used to terminate statements. However it's also a statement in its own right.

The ; on it's own will simply be a null statement causing nothing to happen. This is useful when you have test-clauses and loops (described later) that perform their intended purpose within the test or loop clause and aren't actually intended to do anything else. Just remember that anywhere you're allowed to have a statement, you can just put a ; as a statement that does nothing.

Conditionals

Conditional statements are used a lot in LPC, and there are several ways of writing them. A very important concept is that 0 and nil are considered as false and any other value as true in tests. This means that empty listings ({}), empty strings "" and empty mappings ([]) also are evaluated as true since they aren't 0 or nil. You have to use special functions to compute their size or determine content if you want test them.

The if and else statements

The most common conditional statement is the if statement. It's easy to use and can be combined with an else clause to do two different things based on a variable's value. It's written like this:

if (expression) statement;
e.g.
    if (a == 5)
        a -= 4;
    

If you want to handle the false case, you can add an else statement like this:

if (expression) true-statement else false-statement;
e.g.
    if (a == 5)
        a -= 4;
    else
        a += 18;

or

    if(a > 10)
      a -= 10;
    else {
      a += 100;
      b--;
      a -= 10;
    }
    

The switch statement

If one variable has to be tested for a lot of different values, the resulting list of `if-else-if-else' statements soon gets very long and hard to read. However, if the type of the value you are testing is an integer, a float or a string you can use a much denser and neater way of coding. Assume you have the following code you want to write:

if (name == "fatty")
{
    nat = "se";
    desc = "blimp";
}
else if (name == "plugh")
{
    nat = "no";
    desc = "warlock";
}
else if (name == "olorin")
{
    nat = "de";
    desc = "bloodshot";
}
else
{
    nat = "x";
    desc = "unknown";
}
    

A better way of writing this is:

switch (name)
{
case "fatty":
    nat = "se";
    desc = "blimp";
    break;

case "plugh":
    nat = "no";
    desc = "warlock";
    break;

case "olorin":
    nat = "de";
    desc = "bloodshot";
    break;

default:
    nat = "x";
    desc = "unknown";
}
    

The workings of this statement is simple: switch sets up the expression value within the parenthesis for matching. Then every expression following a case is compared to find a match.

Note that the case expression must be a constant value. It can't be a variable, function call or other expression.

After a match has been found the following statements are executed until a break statement is found. If no matching value can be found, the default statements are executed instead.

While it's not mandatory to have a default section, it's highly recommended since that usually means that something has happened that wasn't predicted when writing the program. It's usually very good to have an error message to notify the user (or you) that something unexpected happened.

If you forget to put in a 'break' statement the following 'case' expression will be executed. This might sound like something you don't want, but if in the example above the names `fatty' and `plugh' both should generate the same result you could write:

case "fatty":
    /* FALLTHROUGH */
case "plugh":
    < code >
    break;

... and save a bit of space. Writing code with switch doesn't make it any quicker to execute. It does make it a lot easier to read, which reduces the chance of making mistakes while coding. Remember to put the /* FALLTHROUGH */ comment there though, or it might be hard to remember later if it was intentional or an omission of a break statement. If you have code that's executed before falling through to the next case, this is especially important. A good idea is usually to add an extra linefeed after a break statement just to give some extra 'breathing space' to improve on legibility.

The ? : expression

This is a very condensed way of writing an if/else statement and return a value depending on how the test turned out. The ?: operator isn't a statement, it's an expression since it returns a value. It's listed here instead of being listed among the expressions because it's effectively a conditional, though.

Suppose you want to write the following:

if (test_expression)
    var = if_expression;
else
    var = else_expression;
    

You can write that in a much more condensed way:

var = test_expression ? if_expression : else_expression;
e.g.
    name = day == 2 ? "tuesday" : "another day";
    

Opinions vary as to whether writing the conditional with this operator makes the code easier or harder to read. A common rule of thumb is that one use of the ?: operator makes code clearer, but that several in a single statement only makes it worse. Something like this definitely isn't an improvement:

name = day == 2 ? time == 18 ? "miller time" : "tuesday" : "another day";
    

Loop statements

There are two loop statements in LPC which incorporate the use of conditional statements within them. That means they can be programmed to execute only until a certain condition is true.

The for statement

If you want a counter or an iterator, you should usually use the for statement. The syntax is as follows:

for (initalize_statement ; test_expression ; end_of_loop_statement)
    body_statement;
    
When first entered, the for statement executes the initialize_statement part. This part usually is used to initialize counters or values for the loop itself. Then the first loop starts. Every loop starts by executing the test_expression and examining the result. This is a truth conditional, so any answer not equal to 0 or nil will cause the loop to be run. If the test expression is true then the body_statement is executed, immediately followed by the end_of_loop_statement. In the body_statement you usually do what you want to have done for this iteration. In the end_of_loop_statement you usually increment or decrement counters as needed to prepare them for the test_expression in the next loop.

Throughout the previous section I used the word usually a lot. This is because you don't have to do it that way, there's no rule forcing you to make use of the statements in the way I said. For the moment let's stick to the regular way of using the for-statement. Later on I'll describe more refined techniques, and you can discover your own as well.

Assume you want to compute the sum of all integers from 7 to 123 and don't know the formula ((x2^2 + x1^2) / 2). The most straightforward way of doing that is a loop.

result = 0;
for (count = 7 ; count < 124 ; count++)
    result += count;
    

First of all, result is set to 0. Then the actual for statement is entered. It begins by setting the variable count to 7. Then the loop is entered, beginning by testing if count (= 7) is less than 124. It is, so result has count added to it. Then count is incremented and the loop is entered again. This goes on until the count value reaches 124. Since that isn't less than 124 the loop is ended.

The loop form above is pretty standard in C, but you may have realized there's another way you can write the same thing:

result = 0;
for (count = 7 ; count <= 123 ; count++)
    result += count;
    
This way works fine too, and you may find it more understandable. Then again, you may not.

Please note that the value of count after the for statement will be 124 and not 123. The test_expression must evaluate to false in order for the loop to end, and in this case the value for count then must be 124. This is true for both ways of writing the loop above!

The while statement

The while statement is pretty straightforward. You can probably guess from its name what it does. The statement will perform another statement over and over until a given while expression returns false. The syntax is:

while (<test expression>)
    

Note carefully that the test expression is checked first of all, before running the statement the first time. If it evaluates as false the first time, the body is never executed.

a = 0;
while (a != 4)
{
    a += 5;
    a /= 2;
}
    

The break and continue statement

Sometimes during the execution of switch, for or while statements it becomes necessary to abort execution of the block code, and continue execution outside. To do that you use the break statement. It stops the execution of that block and continues after it.

while (end_condition < 9999)
{
    /* If the time() function returns 29449494, abort execution */
    if (time() == 29449494)
        break;

    < code >
}

/* Continue here both after a break or when the full loop is done. */
< code >
    

Sometimes you merely want to start over from the top of the for or while loop you're running. To do that, you use the continue statement.

/* Add all even numbers */
sum = 0;
for (i = 0 ; i < 10000 ; i++)
{
    /* Start from the top of the loop if 'i' is an odd number */
    if (i % 2)
         continue;

    sum += i;
}
    
Notice that the i++ is executed when the loop is continued. Only the sum += i; is skipped.