 |
The GLOBAL Tour
Scoping-out Variables and Functions
|
Let's quickly go over the kinds of primitive objects that we use while
building bots on the Palace. If you were writing
official docs,
you might call this list "Iptscrae data types."
- Simple values, like 3, 523, or
"Henry" (simple numeric values
are sometimes known as scalars)
- Arrays of values, like [ 3 21 52 ] or [
"prop1" "prop2" ]
the values inside an array may even be other arrays... that is,
once made, an array can be treated like any other value.
- Atom lists
streams of commands and values inside
{ and } marks. Atom lists too can contain
other atom lists.
We can store values like "Istanbul" or arrays like
[ "Constantinople" "New Amsterdam" ] in
variables using the = sign.
We can name and store atom lists,
to be used as as functions,
using DEF. We can "run" such an
atomlist at at time by passig it to the iptscrae command
EXEC.
Here are a couple of examples.
; variable creation and assignment using =
1 TRUE =
TRUE NOT FALSE =
"wiggly" worldType =
[ "Scooby" "Scrappy" ] dogDoos =
; declaring an atom list function with DEF
{
{ WHOTARGET PRIVATEMSG }
{ SAY } WHOTARGET IFELSE
} select_say DEF
|
Note that the select_say function itself contains multiple
atom lists
IFELSE takes both "yes" and "no" atomlists as
arguments. Many iptscrae commands take atomlists and sublists, particularly
EXEC, ALARMEXEC, IF, WHILE, DEF, and all of the various
ON SIGNON, ON MACRO3, ON OUTCHAT, etc "event handlers."
To use select_say we can replace
SAY
with
select_say EXEC
(don't forget the EXEC!!!)
the result will act just like SAY in most cases, but is
also smart enough to know when you really wanted to
*whisper* because you had someone clicked.
Iptscrae doesn't distinguish between functions, subroutines,
procedures, procs, applets and such thingies... they're all just
"atom lists." If an atom list is named and stored away using
DEF, you can call it a function or proc or whatever.
For clarity, I try to always give
variables multi-word names like worldType or
longVariableName, and functions names_like_this
that is,
underscore_separators. Finally, for the rare value that's
defined as an unchanging constant, I usually use
ALLCAPS
in the computer brain, what's TRUE tends
to stay that way.
In programmer-speak, all of these
names are typically known as identifiers.
Iptscrae itself actually ignores
identifiers' case, so don't make the mistake of thinking that, say,
"msx" "msX" and "MSX" are different
names.
Scopes
When an identifier is declared within the bubble of an { atom list }
its definition is restricted to that atom list, and the subsequent
sublists contained in the same atom list. Outside the scope of its enclosing
bubble, it doesn't exist!
In other words, it you use a
variable called creamCheese in your INCHAT handler,
don't expect to be able to get that creamCheese back in the
OUTCHAT. They're living in two separate universes. You can
have two separate creamCheese variables, one for each of the
handlers, and neither will ever smear the other.
Unless, that is, they are
both declared as GLOBAL variables.
If an identifier is declared as
GLOBAL before it's used, its value(s) will be stored in a
special shared portion of the Palace's memory. Any other atom list
that likewise refers to the GLOBAL identifier (by using a
GLOBAL command of its own) can subsequently read and use it
just like a "local" identifier. The GLOBAL command
gives us a way to share values and functions between all the parts of
our bot script.
A Global Caveat |
As of this writing, GLOBAL doesn't work with
arrays. Simple-value variables, yes. Atom lists, yes. Arrays, no.
Go figure.
The way to get around this problem is to assign arrays into a string,
the later extract it using STRTOATOM:
[ 1 2 3 42 911 ] theArray =
; putting array values into a string
0 i =
""
{
theArray i GET ITOA & " " &
i ++
} { i theArray LENGTH < } WHILE
theString =
; extracting array values from a string
"\x5b " theString & " \x5d newArray =" & STRTOATOM EXEC
|
Hex values
shown here are for the characters "[" and "]"
|
If an atom list is contianed within another atom list,
it will inherit the definitions of
the enclosing list. You don't have to be redeclaring lots of
GLOBAL vars every time you use IF or
WHILE as long as you do make the appropriate
declarations in the surrounding code. My personal habit is to pile-up
the GLOBAL declarations at the beginning of each event handler.
There are several kinds of { atom
lists } that don't have any enclosing scope at the time they
execute, and so they must always include GLOBAL
declarations for every shared variable and function that they
might use.
- Event Handlers
- These are the big blocks like ON SIGNON and
ON OUTCHAT.
- Functions Launched by EXEC
- Once they start executing, they "pop" up to the
top of the scope chain. They can still get values passed on
the stack (like select_say
above), but shared
values should be declared as GLOBAL within the
EXEC'd atomlist.
- Functions Launched (and Delayed) by ALARMEXEC
- These are even more restrictive than EXEC
functions, because they can't know what the stack will be
like by the like they actually get around to executing.
ALARMEXEC is the most isolated case
everything
must be either declared GLOBAL or completely
self-contained within the atom list.
As an alternative to using global data,
you could try a stringy workaround.
Trickier still, you need to
remember that the global values that an ALARMEXEC
atom list might access will be accessed then, at the
time of the alarm
not at
the time you schedule via ALARMEXEC!!!
- Command-line Iptscrae
- You can execute Iptscrae commands directly from your text box,
by escaping the text with a leading / slash mark.
Since the stack state can't be known in advance, this is roughly as
isolated as ALARMEXEC and needs
GLOBAL declarations for anything you want to use from your
Cyborg.ipt.
When a variable or function is declared GLOBAL, its name and
value persists between events. Not just between different
OUTCHATs and alarm events, but between rooms and even between
palaces (when connected via NETGOTO in scripts like "warp" or
through a palace jumpstation like "the slabs"). Once declared, the
only way to effectively wipe out a global value is to shut down your
entire palace session, via the "Disconnect" menu. Not attractive, but
that's life.
A few clever folks have turned this
potential problem into a useful tool
for example, the now-common
ICameFrom global macro can let any palace script know about
the recent history of users, who may have come there via link from
another palace.