&
and @
)From outside the function, it is possible to store to the output or read
from the input. It is generally considered bad form to do so, but is
sometimes neccessary. This is done with the Input/Output Override prefix
operators. The &
operator forces use of the input queue, and
the @
operator forces use of the output queue. When the &
operator is used to store to the input queue (even if it would not be
necessary) the function's code does not execute. Execution can then
be forced later by storing a null queue to the function.
Remember that a function consists of three queues? An input
queue, an output queue, and an instruction queue? Well, the instruction queue
isn't called a queue for nothing. You can manipulate it just like any other
queue, as long as you use the instruction queue override prefix
(~
). Statments are enclosed in brackets []
to keep
them as one item, but otherwise can be treated just like numbers (except that
you can't do arithmetic with them, of course). The ability to manipulate the
instruction queue can create very interesting self-modifying code. See
Compound Data Types for examples of how this can be used.
code
Predefined QueueThere is another predefined queue, like in
and
out
. It is code
and it refers to the instruction
queue of the current function, just as in
and out
refer to the input and output queues of a given function. In the main
program, it refers to the main instruction queue.
OOP (Object Oriented Programming) can be done in Q-BAL, but it is primitive compared to other languages such as C++. Then again, Q-BAL is not a object-oriented language, so it's surprising that you can do it at all. It's really a consequence of the capabilities of functions, and not a real designed capability of the language.
A function can act as an object. If it needs to store "member variables" it can do so in a static local variable (see above). The instruction queue can be set to do different things depending on how many arguments are put in the input queue at the same time (or even what type of argument: see variable data types, below), thus acting as different functions. Data can also be stored in the output queue. One function can be assigned to another of the same data type, replacing the target's instruction queue, input queue, and output queue.
Since each function has its own in
and out
queues, it stands to reason that it would also have the corresponding
variable I/O queues, and this is in fact the case. Each function has its own
of all five variable I/O queues, and they function exactly the same as in the
main program. Furthermore, if a function's ?
queue is nonempty,
then input to the device it specifies is considered input to the function and
will cause execution of the instruction queue. In this way, Q-BAL can create
an event-driven program. For this purpose, there is a stdin handle that can
be stored to ?
, even though the main program does not require
it. Stdin can be accessed from a function through :in
but this
will not cause function execution. Also note that in order for this to work,
the ?
queue is static; that is, unchanged between function
calls.
File handling is done through variable I/O, but since the programmer needs
to tell the computer what file to open before I/O is possible, it is handled
through a function rather than a macro, and now that we know all about
functions, we can cover it. FILE
is a predefined function which
takes as input a string which is the filename, with either a local or global
path. If the filename does not exist, FILE
will create it. It
then returns a "file handle" which can be stored to ?
, allowing
I/O to that file. File I/O using FILE
is sequential and
appending. That is, if ?={filehandle}
, then
attachments from in?
read one character at a time from the top
of the file, sending an EOF when the file is over, and attachments to
out?
append characters to the end of the file. Assignments from
in?
read one line at a time, and assignments to
out?
write one line at a time (as is standard with I/O queues).
There are also other miscellanous file-handling predefined functions such as
FQ DELETE
, which takes a queue of files to delete.
Functions can declare local variables, in the normal manner of variable declaration, but these variables do not keep their values when the function is called multiple times. Many functions can get around this by keeping values on the input or output queues, but sometimes this isn't sufficient, especially for OOP-like functions. One way to get around this is by declaring a queue outside the function that is only used from inside that function, but this is cumbersome.
?
is StaticEach function has its own ?
queue which remains unchanged
from calling to calling, like a static variable. It is therefore possible to
store single values in the ?
queue between function callings, as
long as the function does not utilize variable I/O. This is dangerous,
however, because if the number stored happens to be the handle for some
device, then the function may be inadvertently called by input to that
device.
There is yet another area that variable I/O can handle: dynamic allocation
of memory. C or C++ programmers will be familiar with this concept. To
dynamically allocate memory for an object (of any data type), use
the function F ALLOC
. This function takes as input a string
indicating the data type of the desired object (for example,
"FQQ"
) and outputs a handle that can be stored to a
?
queue. When this handle is on top of the ?
queue,
then the variable I/O queues access the dynamically created object.
in?
functions exactly like &x
and out?
exactly like @x
, for an object named x
. The queues
'in?
and 'out?
are not well defined when
?
holds the handle of a dynamic variable. When a function
dynamically creates variables, they are static because their handle
(presumably in the ?
queue) is also static. This is the best way
for a function to create static variables.