Phantasmal MUD Lib for DGD

Phantasmal Site > DGD > Untitled document (index.base.html) > (untitled)

Scope and prototypes

Scope is a term defining where a function or variable declaration is valid. Since programs are read top down (just like you read this page), declarations of functions and variables are available to the below the actual declaration. However, the scope might be further limited.

A variable that is declared inside a function is only valid until the end of that function (the terminating }) is reached. If it's declared in a block inside that function, such as inside a for loop, it's only valid until the end of that block (the } matching the beginning { of that block).

<top of file>
int GlobCount;

/* Only GlobCount is available here */

void
var_func(int arg)
{
    int var_1;

    /* GlobCount, arg and var_1 are available here */
    < code >

    {
        string var_2;

        /* GlobCount, arg, var_1 and var_2 are available in this block */
        < code >
    }

    /* GlobCount, arg and var_1 are available here */
    < code >

    {
        int var_2;
        mapping var_3;

        /* GlobCount, arg, var_1, var_2 and var_3 are available here
          Note that this var_2 is a NEW var_2, an int not a string */
        < code >
    }

    /* GlobCount, arg and var_1 are available here */
    < code >
}

/* Here only GlobCount and the function var_func are available */
    

Function declarations follow the same rule of scope, though you can't declare a function inside another function. Suppose you have these two functions where the first uses the second:

int
func_1()
{
    < code >
    func_2("test");
}

void
func_2(string data)
{
    < code >
}
    

Then you have a problem, because the first function tries to use the second function before it is declared. This may result in an error message, and it's bad practice in any case. To take care of this you can rearrange the functions so that func_2 comes before func_1 in the listing. This isn't always the best layout, and it isn't always possible. It's usually better to write a function prototype. The function prototype should be placed at the top of the file after the inherit and #include statements (described later) but before any code. It should look very much like the function declaration itself. In this case:

< top of file, inherit and #include statements >

void func_2(string data);

< the actual code >
    

Operator expressions

The LPC language defines a large set of operator expressions. These are simply expressions that operate on other expressions. What follows here is a list of them. This section uses condensed notation to save space and reduce complexity.

E
Any expression, including compound expressions.
V
A variable.

Miscellaneous operators

(E)
The expression inside the parentheses is evaluated before anything outside the parenthesis. This is useful for isolating expressions that need to be done in a specific order. It's also useful when you are uncertain about operator precedence, or when you want to make it obvious to readers of your code what the precedence is.
E1, E2
The first expression is evaluated first and the result stored, then E2 is evaluated and the result is thrown away. Finally, the stored result of E1 is returned as the value of the entire expression. The statement a = 1, 2, 3; will set 'a' to contain '1'.
V = E
The variable is given the value of the expression. The resulting value of this entire expression is also the value of E. For instance, a = b = 4; will set a and b to be 4. It can also be written a = (b = 4) to illustrate the order of execution.

Arithmetic operators

E1 + E2

The expressions are evaluated and the results added to each other. You can add integers, floats, strings, arrays and mappings. Strings, arrays and mappings are simply concatenated - pasted together to the end of the first argument.

It's also possible to add integers to strings, they will then be converted to strings and pasted to the end of the string.

E1 - E2

E2 is subtracted from E1. You can subtract integers, floats and any type from arrays of the same type. For arrays the item, if it exists in the array it is subtracted from, is removed from the array. If it doesn't exist in the array, the array is returned intact.

E1 * E2
E1 is multiplied by E2. This only works on integers and floats.
E1 / E2
E1 is divided by E2. This only works on integers and floats.
E1 % E2
The remainder (also called the modulus) of the expression 'E1 / E2' is returned. This only works with integers. For instance, 14 % 3 will yield 2 as the remainder.
-E
Return E, negated arithmetically. This only works on integers and floats. For either one, the value returned is equal to zero minus E, or negative E.
E++, ++E

The expression E is incremented by one. If the operator is in front of the expression (called "prefix") then the incremented value is returned, otherwise the previous value is. For instance, if the variable ctr is equal to 3 then ++ctr would return 4 and ctr++ would return 3. This only works on integers.

The value of ++a is also an lvalue, which means it can be assigned to. If you don't immediately know what that means, don't do that.

'a = 3; b = ++a;' will yield the result 'a = 4, b = 4', while
'a = 3; b = a++;' will yield the result 'a = 4, b = 3'.
        
E--, --E
The expression 'E' is decremented by one. If the operator is in front of the expression, the decrement is done before the value is returned, otherwise afterwards. This only works on integers.
'a = 3; b = --a;' will yield the result 'a = 2, b = 2', while
'a = 3; b = a--;' will yield the result 'a = 2, b = 3'.
        

Boolean operators

Boolean operators are applicable only to integers with the exception of the & and | operators, which also work on arrays. Internally an integer is 32 bits long. However, in the following examples I will only show the ten last bits as the others are 0 and can be ignored with the one exception of the ~-operator.

E1 & E2
E1 and E2. Every bit which is set in both E1 and E2 will also be set in the result. Any bit which is zero in either one will be zero in the result.
1011101001   (= 745)
1000100010 & (= 546)
------------
1000100000   (= 544) => 745 & 546 = 544
Used on two arrays, this function will return a new array that holds all elements that are members of both of the argument arrays. Thus, it performs a kind of set-intersection on the arrays.
E1 | E2
E1 or E2. Every bit which is set in either E1 or E2 will be set in the result. Any bit which is zero in both E1 and E2 will be zero in the result.
1011101001   (= 745)
1000100010 | (= 546)
------------
1011101011   (= 747) => 745 | 546 = 747
Used on two arrays, this function will return an array containing any element which is in either one of the original two arrays. If E1 and E2 share no members in common, this is the same as E1 + E2.
E1 ^ E2
E1 xor (exclusive or) E2. A bit which is zero in both or one in both is zero in the result. A bit which is set in either E1 or E2, but not both, is one in the result.
1011101001   (= 745)
1000100010 ^ (= 546)
------------
0011001011   (= 203) => 745 ^ 546 = 203
~E
1-complement of E (invert E). This is a unary operator, meaning it takes only one argument. A bit which is one in the argument will be zero in the result and vice-versa.
00000000000000000000001011101001 ~ (= 745)
----------------------------------
11111111111111111111110100010110   (= -746) => ~745 = -746
The above example might be hard to understand unless you really know your binary arithmetic. However, trust me when I say that this is not a typo, it's the way it should look. ~745 is different from -745 -- one is a one's complement and the other is a two's complement. Read about twos-complement binary arithmetic and all will be made clear.
E1 << E2
E1 is shifted left E2 steps. This multiplies the value of E1 by two to the power of E2. For instance, if E2 was three, E1 << E2 would be E1 * 8.
5 << 4 => 101(b) << 4 = 1010000(b) = 80
        
E1 >> E2
E1 is shifted right E2 steps. This divides the value of E1 by two to the power of E2, rounded down. For instance, if E2 was five, E1 >> E2 would be equal to E1 / 32.
1054 >> 5 => 10000011110(b) >> 5 = 100000(b) = 32
        

Conditional (logical) operators

E1 || E2
Returns true if E1 or E2 evaluates as true. Will not evaluate E2 if E1 is true. The fact that it won't evaluate the second argument if the first is true is called "short circuit evaluation".
E1 && E2
Returns true if both E1 and E2 evaluates as true. Will not evaluate E2 if E1 is false. The fact that it won't evaluate the second argument if the first is false is called "short circuit evaluation".
!E
Returns true if E is false and vice versa.

Comparative operators

E1 == E2
Returns true if E1 is equal to E2. This operator can be used on all types, but see the special section later on arrays and mappings. Equality works differently on them than you might think.
E1 != E2
Returns true if E1 isn't equal to E2. This operator can be used on all kinds of types, but see the special section later on arrays and mappings. It works differently on them than you might think.
E1 > E2
Returns true if E1 is greater than E2. This operator can be used on all types except arrays and mappings.
E1 < E2
Returns true if E1 is less than E2. Can be used on all types except arrays and mappings.
E1 >= E2
Returns true if E1 is greater or equal to E2. This operator can be used on all types except arrays and mappings.
E1 <= E2
Returns true if E1 is less or equal to E2. This operator can be used on all types except arrays and mappings.

Prefix allocation

All of the arithmetic and boolean operator expressions can be written in a shorter way if what you want to do is use an operator on a variable and a value (or two variables) and then store the result in the variable.

Say that what you want to do is this a = a + 5;. A much neater way of writing that is a += 5;. This does exactly the same thing with less keystrokes and less chance of error in typing. Many people also find it more readable.

You write all the others in the same way. So the result variable comes first, then the operator directly followed by = and then the value to operate on. Make sure not to put a space between the operator and the equals sign, and make sure to put the operator before the equals sign.

a >>= 5;       /* a = a >> 5; */
b %= d + 4;    /* b = b % (d + 4); */
c ^= 44 & q;   /* c = c ^ (44 & q); */

c =+ 7;      /* c = (+7), probably not what you wanted. */
c + = 7;     /* Error!  Don't use the extra space. */

Precedence and Order of evaluation

The table below summarizes the rules for precedence and associativity of all operators, including those which we have not yet discussed. Operators on the same line have the same precedence, rows are in order of decreasing precedence, so, for example, *, / and % all have the same precedence, which is higher than that of + and -.

Note that the precedence of the bitwise logical operators &, ^ and | falls below == and !=. This implies that bit-testing expressions like the one below must be fully parenthesized to give proper results.

if ((x & MASK) == 0) ...
    
() []
Left to right
! ~ ++ -- - (type) * &
Right to left
* / %
Left to right
+ -
Left to right
<< >>
Left to right
< <= > >=
Left to right
== !=
Left to right
&
Left to right
^
Left to right
|
Left to right
&&
Left to right
||
Left to right
?:
Right to left
= += == etc.
Right to left
,
Left to right

Note that in the list, (type) denotes a typecast. Note also that in every case, a unary operator is higher precedence than its binary equivalent. So the expression -7 - 5 is equal to (-7) - 5 instead of -(7-5).