Symbols

The backbone of Proteus is the symbol table that is retained and stored during the pre-initialization stage of program compilation. The symbol table allows a program to access the global symbols that were defined during compilation and makes it possible to translate strings into object references, properties, and function references; as well as to translate these references into strings.

The gSymbols Global

The global symbols table is accessed in Proteus through the symbolTablePreinit object. During pre-initialization this object is stored in the proteusGlobal property symbolTableLU. This property can be addressed through the shorthand global macro gSymbols from anywhere in your program file as long as you #include the proteus.h header file at the top of your program file.

 

The symbolTablePreinit object combines two LookupTables. One table contains the global symbols table produced by the compiler. This table associates single-quoted string keys with symbolic values. The other table reverses the associations to symbolic keys and single-quoted string values.

 

The methods provided by the symbolTablePreinit object encapsulate and hide the two tables, so that the user need not be aware that they are dealing with two tables. The datatype of the argument is immaterial, if the key is a single-quoted string then the table keyed by single-quoted strings is searched. If not then the other table is searched.

 

Retrieving the Symbol for a Value

The symbolTable object provides the method getSymbol(val) that returns the symbol associated with the key val found in the global symbols table. If the argument is not a key in the global symbol table then the method returns nil.

 

Retrieving the Single-quoted String for a Value

The symbolTable object provides the method getSString(val) that returns the single-quoted string associated with the key val found in the global symbols table. If the argument is not a key in the global symbol table then the method returns an empty string.

 

Checking for the Presence of a Key in the Global Symbols Table

The symbolTable object provides the following methods for determining if the appropriate key is present in the global symbols table:

 

 

Objects without Symbolic References

The global symbol table is very useful for translating between symbolic references and string representations. Any object that has a symbolic reference, commonly called an object name, will have a value in the global symbol table. Objects that have been dynamically-created or anonymously-defined, however, will not have a value in the global symbol table, and will return an empty string for getSString() and nil for getSymbol() messages. Proteus takes advantage of this fact by providing the isDynamOrAnon() message.

 

The isDynamOrAnon() message will return true if the object is either dynamically-created or anonymously-defined. If the object has a symbolic reference it is neither, and the method will return nil.

 

An object definition such as the following would return nil for isDynamOrAnon(),

 

global: object;

 

because global would appear in the global symbol table and can be programmatically referenced by its object name.

 

An object dynamically-created from global using the createInstance() message would return true for isDynamOrAnon() and would need to be referenced through an object property or local variable.

 

Objects defined anonymously, such as the loaf of bread from the TADS 3 sample game:

 

++ Food 'fresh golden-brown brown loaf/bread/crust' 'loaf of bread'

    "It's a fresh loaf with a golden-brown crust. "

 

    /*

     *   we want to provide a special message when we eat the bread, so

     *   override the direct object action handler for the Eat action;

     *   inherit the default handling, but also display our special

     *   message, which will automatically override the default message

     *   that the base class produces

     */

    dobjFor(Eat)

    {

        action()

        {

            /* inherit the default handling */

            inherited();

 

            /* show our special description */

            "You tear off a piece and eat it; it's delicious.  You tear off

            a little more, then a little more, and before long the whole loaf

            is gone. ";

        }

    }

;

 

 would return true for isDynamOrAnon(). Programmatically, and for all practical purposes, anonymous objects are indistinguishable from dynamically-created ones. Anonymously-defined objects persist throughout game play, while dynamically-created ones are subject to garbage collection once they become de-referenced, but otherwise there is no programmatically significant difference between them.

Unnamed Internals

A close examination of the global symbol table reveals that it harbors some strange denizens. These symbolic references appear as hexadecimal values in the symbol table. They are symbolic references that aren’t defined in either the library or author’s code, instead they are created during compilation and are the result of modifications to object definitions resulting from the modify keyword.

 

When an author modifies an object definition using the modify keyword, the compiler automatically generates a hexadecimal symbolic reference (object name) for the definition occurring previously in the compilation process. A new object is created, deriving from the object being modified, and the new object directly defines the code defined by the modify keyword. This newly generated object is then given the symbolic reference (object name) of the object that is the operand of the modify keyword.

 

To better understand the process. Assume we code the following definitions in our program:

 

       global: object

       {

              myAttribute1 = true

       }

 

       modify global

       {

              myAttribute1 = nil

              myAttribute2 = true

       }

 

Now when we compile the program, as the compiler encounters the modify keyword it generates a new hexadecimal symbolic reference for the previous definition of global, and places this reference into the global symbol table:

 

       4fe: object

       {

              myAttribute1 = true

       }

 

Next it creates an object deriving from our re-referenced object, 4fe, and gives this object the symbolic reference (object name) that is the operand of the modify keyword:

 

       global: 4fe

       {

              myAttribute1 = nil

              myAttribute2 = true

       }

 

As the compiler proceeds through the code sequentially it follows the same process for each modification that it encounters. Each additional modification to global will produce a new unnamed internal, and the code defined by the modify keyword will be used to create an object derived from this unnamed internal, and given the symbolic reference of the modify keyword.

 

Although these compile-time generated unnamed internals can only be referenced programmatically through object properties or local variables, they are not subject to garbage collection and are similar to anonymously-defined objects.

 

The Proteus symbol table can be used to determine whether an object is an unnamed internal, based on whether the object has a hexadecimal symbolic reference. Object class objects can receive the message isUnnamedInternal(), which will return true if the object is an unnamed internally-generated object, or nil if the object is not.

 

If x is a local variable referencing an object, in the following statement

 

       val = x.isUnnamedInterna();

 

the variable val will either be assigned true or nil.

 

 

This file is part of the TADS 3 Proteus Library Extension

Copyright © 2001-2004 Kevin Forchione. All rights reserved.