All data in CIGAL are stored and manipulated in variables. A variable
can be a:
number - single number
array - one dimensional set of numbers
matrix - two dimensional set of numbers
image - special class of matrix
solid - three dimensional set of numbers
string - sequence of text characters
string list - table of strings
vector - three dimensional drawing instruction
vector list - array of vectors
pointer - pseudo-variable, can point to any variable
Variables fall into several broad classes depending on where their data
are stored. The classes of storage locations are:
stack - CIGAL's dynamic data memory
high - expanded memory stack
internal - CIGAL's internal control values and tables
device - memory in external devices (eg. image processor)
core - variables can access anywhere in core memory
file - data stored in disk files
Predefined Variables
The INTERNAL and DEVICE classes of variables are predefined within
CIGAL itself, whereas the rest are defined dynamically by the user. The
predefined variables generally refer to internal control flags and tables,
or hardware memory, whose values have special meaning. Changing values in
one of these variables automatically changes whatever property the
variable controls (see INTERNALS[1]).
User-defined Variables
The different classes of user defined variables are designed to provide
broad flexibility in letting you access data from different places. STACK
and CORE variables are both created by using the DECLARE command (see
DECLARE[2]). FILE variables can be declared if the files don't already
exist but usually you don't need to. Data can be accessed from a file as
a variable simply by using the appropriate naming syntax (see Variable
Names below).
Stack Variables
The most useful class of user defined variables are the STACK
variables. A stack variable is stored as part of CIGAL's large internal
data memory (see STACK[4]). Depending on how you declare the variable, it
can either be allocated a fixed part of the stack, or its allocation can
be dynamic, changing automatically as the length of the variable changes.
CIGAL takes care of allocating memory space within the stack as it is
needed, so you don't need to worry about details of where particular
variables are stored.
If your computer has expanded memory, then CIGAL can make use of that memory
as an extended stack. The part of the stack in expanded memory is referred
to as the "high" stack. You tell CIGAL how much expanded memory you have
by setting the internal variables EMEMBASE and EMEMSIZE (see EMEMBASE[4]
and EMEMSIZE[4]). Then CIGAL automatically allocates data storage space
for large variables in the high stack if space is available. Stack variables
have some advantages over the other classes of data variables. Most importantly,
stack variables are located within the computer's main memory and are therefore
available for immediate access. This provides a distinct improvement in
performance compared to accessing data in device or file variables. Second, you
can declare different stack variables to share the same memory storage (see
DECLARE[2]). This is analogous to a UNION declaration in C or an EQUIVALENCE
statement in FORTRAN, and can be very useful for some kinds of data handling
problems. The major drawback of stack variables is that the amount of storage
space available on the stack is limited by the amount of random access memory
(RAM) your system has. With expanded memory stack space can be very large, but
it is still limited. For data sets that are too large to fit within the available
stack space you should use file variables, which are limited only by the amount of
disk space you have.
Core Variables
A CORE variable is declared similar to a STACK variable except that instead of
referring to data stored in the stack memory, a core variable points explicitly
to some part of the computer's physical memory. This feature lets you create
windows anywhere you like into the computer's memory and access that data as
a standard CIGAL variable (see DECLARE[2]). For example, core variables can
be created for the video display memory or the computer's internal font tables.
Writing to such variables immediately changes the display on the terminal's screen.
If you are more ambitious you can also create core variables that point to the
computer's control registers and thereby directly read or modify any of the
system's control parameters.
File Variables
Data can be accessed directly from a disk file as a FILE variable. Basically, any
unrecognized variable name is assumed to be the name of a disk file (see
Variable Names below). CIGAL opens the file if it exists and reads the beginning
of it to see what kind of file it is. If it is an ASCII text file, then the whole
file is read into the stack to create a temporary stack file. This must be done
in order to determine how large the variable is. However, if the beginning of
the file indicates that the file is a binary data file, then it is treated as a
random access file and data blocks are read or written only as they are needed. In
this way file variables provide you with virtually unlimited data space. You can have
as many variables as you have disk files, and their size is limited only by the
amount of space you have on the disk drives.
Data Buffering
Except for STACK and CORE variables, which are always immediately accessible, data
from most other variable classes are transferred in and out of CIGAL via data buffers.
This means that a block of data is copied at a time. The data within the buffer are
then accessed as rapidly as for stack variables. To optimize data transfer efficiency
CIGAL maintains three buffers for each variable. Data buffering is completely
transparent to the user.
Variable Size
There are two aspects determining the size of an individual variable:
the size of each element and the number of elements. For the numerical
variables (NUMBER, ARRAY, MATRIX, IMAGE, and SOLID) the size of each
element can be declared to be any one of seven formats (see DECLARE[2]):
bit - 1 bit unsigned integer
crumb - 2 bit unsigned integer
nibble - 4 bit unsigned integer
byte - 8 bit unsigned integer
integer - 16 bit signed integer
long - 32 bit signed integer
real - 32 bit signed floating point
A NUMBER can contain only 1 element, but ARRAY, MATRIX, IMAGE, and SOLID
variables can contain as many elements as your storage space allows (the
maximum 'legal' length for each dimension is over 2 billion).
String Variables
For STRING variables, each element is a character which is treated the
same as an 8 bit BYTE. The length of a single STRING is limited to 512
characters. A STRING LIST variable is an unusual variable type in that
each element is a character string, and these strings can all have
different lengths. For this reason subscripts to string lists are also
unusual (see SUBSCRIPTS[1]). A string list's length is the number of
strings in the list (although each string is limited to 512 characters,
the total length of a string list has no limit).
Vector Variables
For VECTOR variables, each element is 64 bits long and is made up of
6 different values:
xvalue - 16 bit signed integer
yvalue - 16 bit signed integer
zvalue - 16 bit signed integer
key - 4 bit unsigned integer
width - 4 bit unsigned integer
intensity - 8 bit unsigned integer
VECTOR LIST variables are arrays of VECTOR variables and can be of any
length.
Pointers
A POINTER is a special kind of variable. By itself it has neither type
nor class and contains no data. However, when a pointer is assigned to
another variable the pointer takes on the properties of the variable to
which it points. Pointers are useful as a way of assigning a whole
variable to serve a particular task (see for example ZIMAGE[4]) or as a
way of selecting a subset of a larger variable. In the latter case the
pointer provides a window within the original variable. Once a pointer is
assigned to a variable, then it can be used just as the original variable
is used. Pointers are particularly useful for manipulating different
subsets of a variable independently. See POINTER[3] for more on pointers.
Variable Names
The general form for identifying CIGAL variables is:
The
# - number
^ - array
^^ - matrix
@ - image
@@ - solid
~ - string
~~ - string list
% - vector
%% - vector list
Most of the time these symbols are unnecessary because the variable's name
already identifies the type. For example, if you create an array as:
declare integer array lengths(100)
then you can refer to this array as either ^LENGTHS or LENGTHS, because
the program knows that LENGTHS is an array. However, for FILE or DEVICE
variables or for using indirect variable names (see below) the symbol is
required, so don't forget about them completely.
The NAME of a variable can be a character string, a number, or another
variable. For STACK, INTERNAL, and CORE variables the names are always
character strings of up to 12 characters. The first character must be a
letter and the rest can be either letters or numbers. The name is
assigned when the variable is created and must not match any other CIGAL
reserved words (see NAMES[1]).
For DEVICE variables the name is always a number (that is why the type symbol
is required). For example, the computer's video display memory is referred to
as image variable @0 (or @1, @2, etc. in modes that support multiple screen
memories). Similarly, the memory planes in an image processor are referred
to as variables: @10, @11, @12, etc. The red, green, and blue lookup tables
are referred to as ^10, ^11, ^12. For these numbered variables, a second
variable can be used to specify the number. For example if you have a
number variable called A assigned a value of 1, then variable @#a is the
same as variable @1. Note: in this case the second variable must be
preceded by its symbol in order to distinguish this from an image variable
called @a, which you might also have. See INTERNALS[1] for more details
on the meanings of the device variables.
The names of FILE variables have a slightly different format from the
other variable classes. File variables almost always require the symbol
identifier, so that the parser knows it is a variable. If the name after
the symbol is not recognized as a previously defined variable name, then
it is assumed to be a file name. This is also true if the name is in
quotation marks, or if the name is specified by a second character string
variable. For example, these are all valid file variable names:
@myfile
^'myfile.dat' ; quotes are needed because of '.'
@~str ; assuming STR is a string variable
containing the filename
File variables can also be identified by numbers. Doing so can be very
useful for applications that generate many different files in sequence.
To use numbered file variables you must first set the internal variable
DATADIR to indicate which directory is used to store the data files.
The file variable is then specified by number within the symbols '<>'.
In this case, the filename is constructed by appending the number to the
DATADIR string and adding an appropriate suffix. (Suffixes are '.img' for
IMAGE and SOLID, '.vec' for VECTOR LISTS, and '.dat' for all other data
types.)
For example, if DATADIR contains \MYDIR\FILE then
@<23> is the file \MYDIR\FILE23.IMG
^<81> is the file \MYDIR\FILE81.DAT
and if #A is 16, %% is the file \MYDIR\FILE16.VEC
Note: In the command READ, WRITE, and DELETE files can be specified by
number alone, without the normal SYMBOL<#> construction, because these
commands already expect the argument to be a filename.
The other way in which file variable names differ from other variables
is that a file may contain more than one variable, in which case the name
can specify which of the file's variables is being used. This is done by
indicating the variable number within '{}' after the rest of the name.
Variable numbers, like subscripts, begin with 0, which is easy to remember
if you think of how many you need to skip to get the one you want. If
you omit the {} specifier, {0} is assumed so that the first set of values in
the file is used. Examples:
^^junk{3} is the fourth matrix in file JUNK (i.e., skip 3)
@<2>{a} is the 17th image in file \MYDIR\FILE2.IMG
(assuming the same values as in the example above)
Subscripts
Each value within a variable, or any subset of a variable, can be
accessed directly using subscripts. See SUBSCRIPTS[1] for how to use
subscripts.
Local versus Global Variables
User defined variables (i.e., STACK and CORE variables) that are created within
macro programs can either be declared locally or globally. A local variable is
only defined for as long as the current macro is active and is automatically deleted
when the macro terminates. A local variable can be used by the macro in which it
is declared, and by any other macro that the first macro calls. A locally declared
variable will override any other variable declared with the same name. In this way
macros don't have to worry about always coming up with unique variable names, provided
they don't care if the variable is temporary. Macro variables can be declared as
GLOBAL (see DECLARE[2]) in which case the variable will remain declared even after
the macro terminates. Global variables can be used by all commands, regardless whether
they are in macros, menus, or entered interactively. Because of this, each globally
declared variable must have a unique name. An attempt to re-declare a global variable
as something different will generate an error. All variables declared at the interactive
level are automatically global.
There is a special group of number variables that are always defined and yet always
function as local variables. These are the 10 number 'registers' #0, #1, ... #9.
Of these, #1 through #9 are designed for use in macro programs so that you always have
some number variables available for counters, etc. The main advantage of these registers
is that their contents are saved whenever you call another macro, and are then restored
when you return from the macro. This means that if a sub-macro changes the value of #1,
for example, it won't affect the contents of #1 in the calling macro. This is not the case
for any other variables. If a macro changes a variable that was declared at a higher level
(i.e., in a calling macro or globally) then it will keep the new value when the macro
terminates. This can be a useful way of passing values from one level to another, but it can
also be a nuisance if you don't want a variable's value to be changed by a sub-macro. Using the
number registers avoids this problem.
The number register #0 is unusual in that many CIGAL commands use it to return an exit status.
For example, the READ, WRITE, INPUT, and OUTPUT commands return in #0 how many records were
successfully transferred. In addition, the RETURN command in a macro program can be used to
return a status value to the calling program so that this value is returned in the #0 register.
It is left up to the macro to test #0 if it cares about the termination status of any particular
command.