### Statements

TADS 3 has a number of extensions to the TADS 2 language. The new language features provide greater functionality and bring the language syntactically closer to Java and C. Some changes from TADS 2:

- The "`self.`" prefix is never needed when evaluating a property or method of `self`. The compiler now fully parses the source code before resolving symbol references, so the compiler can now always tell whether a symbol refers to an object, property, function, or local variable. (In TADS 2, it was necessary to use the explicit "`self.`" prefix when evaluating a property or method that the compiler had not already seen earlier in the source code defined as a property. Since this often changed during program development, many authors found it necessary to include the explicit prefix all the time, resulting in unnecessarily verbose code. The explicit prefix is never needed in TADS 3.)
- Forward function declarations are never required, for the same reason that explicit "`self.`" prefixes are not needed: the compiler processes the entire program's source code before resolving symbols, so it looks ahead by itself and requires no explicit forward declarations.
- A new `intrinsic` statement allows for defining intrinsic function sets, which are resolved dynamically at load-time. Because the program's source code can now specify the intrinsic functions that a program depends upon, the compiler is now completely independent of the function sets, hence the compiler will not have to be updated merely for a change to intrinsic functions, as it did in TADS 2. This statement is only needed for function set developers; refer to `tads.h` or `t3.h` for examples, but library and game authors shouldn't need to use this statement directly.
- A new `intrinsic class` statement allows for defining built-in classes, which are resolved dynamically at load-time.
- Objects, classes, and functions can now be declared as external, via the new `extern` statement, for separate compilation (however, these declarations are needed only in certain cases involving libraries, since the compiler will automatically set up external symbol definitions when compiling a group of source modules together with `t3make`).
- The function definition syntax now more closely resembles that of Java and C.
- The `modify` keyword can now be used to define a new version of a function that can refer to the version it replaces.
- Method definitions now more closely resemble those of Java and C; in particular, the equals sign separating a method's argument list from the open brace of the method code is no longer used.
- A method taking no arguments can be called with empty parentheses after the method name.
- A call to an undefined method can be captured with a new method, propNotDefined.
- Local variable declarations can now appear anywhere within a code block. A local variable definition that appears in the middle of a code block creates a variable that is in scope from that point in the code block to the closing brace of the code block. (TADS 2 only allowed local variable declarations at the start of a code block.)
- A `for` statement can include local variable declarations in the initializer part. Locals defined in a `for` statement's initializer are in scope starting with the initializer and ending with the end of the body of the `for` loop.
- The `break` and `continue` statements can optionally include labels, which makes it possible for a `break` or `continue` statement to specify a particular enclosing loop to which to apply the transfer. This feature allows a `break` statement to break out of multiple nested loops (or out of a loop from within an embedded `switch` statement), and allows a `continue` to continue with a loop enclosing an inner loop. This feature eliminates nearly all of the places where `goto` was useful (although `goto` is, of course, still available).
- The new `try`/`catch`/`finally` and `throw` statements provide a structured exception handling mechanism.
- New variable argument syntax that allows varying arguments to be handled as lists.
- New function and method call syntax that allows you to call a function with a list of arguments obtained from a list value, passing each element of the list as a separate argument value.
- Anonymous function syntax that allows you to write an in-line unnamed function that can be passed to iteration, enumeration, and notification routines.
- The new statement `foreach` provides convenient syntax for iterating over the contents of a collection.
- The new `property statement` allows a program to explicitly define property name symbols.

External declarations: In most cases, the compiler will automatically create the appropriate external symbol definitions for you. When you compile a group of modules together with `t3make`, the compiler automatically constructs symbol files for all of the modules, and then reads the symbol files as it compiles each module. This allows all of the symbols from all of the modules to be available when compiling each individual module. When you're constructing a library, though, you obviously won't have access to user-written game modules – you want your library to work with any user's game module, after all. The `extern` statement addresses this situation by allowing you to explicitly declare the functions, objects, and classes that you require the user-written code to provide. The syntax is:

        extern function function_name(arg1, arg2, arg3);

        extern object object_name;

        extern class class_name;

Function definition syntax: The function syntax used in TADS 2 is now obsolete; you should use the new syntax, which more closely resembles the syntax that Java and C use.

        function_name(arg1, arg2, arg3)

        {

            function_body;

        }

Modifying functions: The `modify` keyword can be used in a function definition. Modifying a function is just like replacing it (using the `replace` keyword), except that the new definition of the function can invoke the old definition of the function (i.e., the definition that's being replaced). This allows the program to apply incremental changes to a function, such as adding new special cases, without the need to copy the full text of the original function.

To invoke the previous definition of the function, use the `replaced` keyword. This keyword is syntactically like the name of a function, so you can put a parenthesized argument list after it to invoke the past function, and you can simply use the replaced keyword by itself to obtain a pointer to the old function. Here's an example.

        getName(val)

        {

          switch(dataType(val))

          {

          case TypeObject:

            return val.name;

          default:

            return 'unknown';

        }

        // later, or in a separate source module

        modify getName(val)

        {

          if (dataType(val) == TypeSString)

            return '\'' + val + '\'';

          else

            return replaced(val);

        }

Note how the modified function refers back to the original version: we add handling for string values, which the original definition didn't provide, but simply invoke the original version of the function for any other type. The call to `replaced(val)` invokes the previous definition of the function, which we're replacing.

Once a function is redefined using `modify`, it's no longer possible to invoke the old definition of the function directly by name. The only way to reach the old definition is via the `replaced` keyword, and that can only be used within the new definition of the function. However, note that you can obtain a pointer to the old function, and then invoke the old function through that pointer outside the bounds of the redefinition.

Explicit property declarations: A program can now explicitly declare property name symbols using the `property` statement:

        property prop1 [, prop2 [...]] ;

This statement declares each listed symbol as a property. Programs are not required to declare properties names, but it is occasionally useful to be able to do so. This is especially useful in libraries, because it is common in library code to call out to a method that the library requires the user code to provide in one of its own objects but which the library does not itself define. In such cases, when the library is compiled on its own (by the library author, not yet linking to user code), the compiler will generate a warning message about an undefined property if the property is not defined anywhere in the library code. This new syntax allows the library programmer to declare the symbols explicitly, which not only suppresses the warning message from the compiler, but also makes the meaning of the symbols clear to readers of the library source code.

Object method definition syntax: The syntax for defining methods in objects has changed slightly from TADS 2. For more consistency with Java and C++ method declarations, the equals sign ("=") that TADS 2 used to separate the method's argument list from the method body has been eliminated. The equals sign is still used for a simple property with a value. Here's an example:

        MyObject: object

            sdesc = "my object"    // use '=' for property values

            doInspect(actor)       // but not for methods

            {

                "It's a pretty ordinary-looking object.";

            }

            doOpen() { "Okay."; }  // no '=' even with no arguments

            doClose { "Done."; }   // same here

        ;

Note that calls to `doOpen` and `doClose` in this example would allow empty parentheses after the method name but don't require them. The language doesn't distinguish between an empty argument list and no argument list in this situation. So, you could make a call to either `MyObject.doOpen` or `MyObject.doOpen()`, and both would behave the same way.

For more details, refer to the Object Definitions section.

Locals in a `for` initializer: You can define new local variables in the initializer part of a `for` statement by using the `local` keyword in the initializer. For example:

        for (i = 1, local j = 3, local k = 4, l = 5 ; i < 5 ; ++i) // ...

This declares two new local variables, `j` and `k`, and uses the existing variables `i` and `l`. Note that `l` is not a new local, even though it comes after the `local k` definition, because each `local` keyword in a `for` initializer defines only one variable. Note also that an initial value assignment is required for each new local declared.

The new locals declared in a `for` initializer are local in scope to the `for` statement and its body (this is the same rule that Java uses, although note that it differs from the (undesirable) way C++ works). The effect is exactly as though an extra open brace ("{") followed by a `local` statement for each new local appeared immediately before the `for` statement, and an extra close brace ("}") appeared immediately after the end of the body of the loop.

Note that this new feature changes the formal syntax of the `for` initializer in a subtle way:

    for_statement: for ( for_init ; for_condition ; for_reinit ) body

    for_init:  for_init_item , for_init

            |  for_init_item

    for_init_item:  assignment_expr

                 |  local symbol = assignment_expr

The subtle change is that a for initializer (`for_init`) was, in TADS 2, simply a comma expression, but it is now a series of one or more initializer items separated by commas, where each initializer item is either a local variable declaration or an assignment expression. Apart from the new ability to declare new local variables, this syntax change has no practical effect, since a series of assignment expressions separated by commas is identical to a comma expression.

Labeled `break` and `continue`: The `break` and `continue` statements can optionally specify a target label. When a label is used with one of these statements, it must refer to a statement that encloses the `break` or `continue`. In the case of `continue`, the label must refer directly to a loop statement: a `for`, `while`, or `do`-`while` statement. The target of a `break` may be any enclosing statement.

When a label is used with `break`, the statement transfers control to the statement immediately following the labeled statement. If the target statement is a loop, control transfers to the statement following the loop body. If the target is a compound statement (a group of statements enclosed in braces), control transfers to the next statement after the block's closing brace. Targeted `break` statements are especially useful when you want to break out of a loop from within a switch statement:

    scanLoop:

        for (i = 1 ; i < 10 ; ++i)

        {

            switch(val[i])

            {

            case '+':

                ++sum;

                break;

            case '-':

                --sum;

                break;

            case 'eof':

                break scanLoop;

            }

        }

Targeted `break` statements are also useful for breaking out of nested loops:

    matchLoop:

        for (i = 1 ; i <= val.length() ; ++i)

        {

            for (j = 1 ; j < i ; ++j)

            {

                if (val[i] == val[j])

                    break matchLoop;

            }

        }

Varying arguments as lists: New syntax allows a function that takes a varying number of arguments to receive the varying part of the argument list as a named list parameter, rather than using the traditional `getarg()` mechanism. To declare a function or method taking varying arguments as a list of values, replace the ellipsis with the list parameter name enclosed in square brackets:

    // old way: formatStr(fmt, ...)

    // new way:

    formatStr(fmt, [vals]) { /* function body */ }

This declares a function that takes one or more arguments. The first argument is named `fmt`, and all of the remaining arguments are placed into a list named `vals`. If the function is called with a single argument, `vals` will contain an empty list, because there are no arguments after `fmt`. If the function is called with two arguments, `vals` will be a list containing the second argument value. If the function is called with four arguments, `vals` will be a list whose elements are (in order) the second, third, and fourth argument values.

This new syntax works the same as the traditional `getarg()` mechanism, and in fact you can still use `getarg()` to retrieve the argument values even if the new syntax is used to declare a function. The new syntax is simply an alternative notation that in many cases facilitates more readable code.

Of course, the old ellipsis notation is still supported, so you can still write variable-argument functions using the traditional notation if you prefer.

#### Lists as variable arguments: In addition to being able to receive varying arguments as a list, you can pass a list value as though it were a list of individual argument values. To do this, place an ellipsis after the list argument value in the function or method call's argument list:

       local lst = [1, 2, 3];

       formatStr('x[%d, %d] = %d', lst...);

Rather than passing two arguments to `formatStr()` (i.e., a string and a four-element list), this passes four arguments (a string, the integer 1, the integer 2, and the integer 3), as though all four had been passed as separate arguments – in other words, the call is identical to this:

       formatStr('x[%d, %d] = %d', 1, 2, 3);

This notation allows you to call a function taking a variable argument list given a list value. This makes it possible to layer calls to functions and methods with variable argument lists, since an intermediate function can itself take a variable argument list and later pass the same arguments to another variable argument function. This type of layering was not possible in TADS 2, since there was no way for code to pass a variable argument list obtained from its caller when calling a function or method.

The foreach statement: The new statement `foreach` provides a convenient syntax for writing a loop over the contents of a collection, such as a list or a Vector.

The syntax of the `foreach` statement is:

       foreach ( foreach_lvalue in expression ) body

The foreach_lvalue specifies a local variable or other "lvalue" expression which serves as the looping variable. This can be any lvalue (any expression that can be used on the left-hand side of an assignment operator), or it can be the keyword `local` followed by the name of a new local variable; if `local` is used, a new local variable is created with scope local to the `foreach` statement and its body.

The expression is any expression that evaluates to a Collection object, such as a list or Vector value.

The statement loops over the elements of the collection. For each element, the statement assigns the current element to the lvalue, then executes the body.

Here's an example that displays the elements of a list.

       local lst = [1, 2, 3, 4, 5];

       foreach (local x in lst)

          "<<x>>\n";

The `foreach` statement uses the Collection intrinsic class's createIterator() method to create an Iterator object for the collection expression, then uses the Iterator object's getNext() method to traverse the elements of the collection. Therefore, the order in which the statement visits the elements of the collection is exactly the same as the order used by the Iterator for the collection.

Note that, because the `foreach` statement uses the Collection.createIterator() method to create the iterator, the iteration uses a "snapshot" of the collection created at the start of the loop. Because the iterator uses this frozen snapshot, changes made to the collection during the loop will not affect the iteration. For example, consider the following:

       local i = 1;

       local vec = new Vector(10).fillValue(nil, 1, 10).applyAll({x: i++});

       foreach (local x in vec)

       {

          vec.applyAll({v: v+1});

          "<<x>>\n";

       }

The first two lines create a vector and initialize its elements to the integers 1 through 10, using the applyAll method of the vector. The `foreach` body modifies the entire vector – adding 1 to every element – then prints out the current value. At first glance, we might expect the values displayed to be something like this: 1, 3, 5, 7, 9…; we might expect this because of the applyAll() call updates every element of the vector on every iteration of the loop. This isn't what happens, though: because the `foreach` statement iterates over a frozen snapshot of the vector, we actually print out the original contents of the vector: 1, 2, 3, 4, and so on. After we're finished with the iteration, though, if we look at the vector, we'll find it modified as we'd expect. In addition, even within the loop, if we were to refer directly to the vector through the variable `vec`, we'd find it modified as we'd expect – the snapshot pertains only the iteration variable, and doesn't "freeze" the vector itself. To see this, consider this more complex example:

       local i = 1;

       local vec = new Vector(10).fillValue(0, 1, 10).applyAll({x: i++});

       i = 1;

       foreach (local x in vec)

       {

          vec.applyAll({v: v+1});

          "x = <<x>>, vec[<<i>>] = <<vec[i]>>\n";

          ++i;

       }

This would display the following, showing that the vector has been modified – and the modifications are visible within the loop – even though the modifications are not visible to the iteration variable:

       x = 1, vec[1] = 2

       x = 2, vec[2] = 4

       x = 3, vec[3] = 6

       x = 4, vec[4] = 8

       x = 5, vec[5] = 10

       x = 6, vec[6] = 12

       x = 7, vec[7] = 14

       x = 8, vec[8] = 16

       x = 9, vec[9] = 18

       x = 10, vec[10] = 20

Although we've belabored this snapshot behavior as though it were some pitfall you must take care to avoid, it should be emphasized that, in most practical cases, this feature relieves you from having to worry about how the iteration will proceed. Even if you're making changes to the contents of the collection during the loop, you can be confident that they'll have no effect on the iteration. The snapshot feature makes it easy to iterate over collections without having to worry about the details of how changes would affect the order of the elements, or the number of elements, or anything else.