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 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.
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.
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.
The symbolTable object provides the following methods for determining
if the appropriate key is present in the global symbols table:
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.
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.