home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Otherware
/
Otherware_1_SB_Development.iso
/
mac
/
developm
/
language
/
quinta.cpt
/
Quinta
/
Quinta.doc2.txt
< prev
next >
Wrap
Text File
|
1990-04-04
|
23KB
|
542 lines
The Quinta Environment
To some extent, the Quinta system can be self documenting.
Messages and Responses are elements of the Quinta dictionary
and may be pushed onto the stack as objects for examination. To
push a message onto the stack, enclose it in underscores. For
example, to push the message *,
_*_
Several important operations are possible for message objects.
For example, with a message object on TOS, the message send
will send that message as if it were typed into the Quinta
interpreter. The message print will print information about the
message including any comments attached to it and the classes
which may respond to it. For a response object, the message
print will print information about the response.
A class may be pushed onto the stack by simply typing its name
into the interpreter. The resulting object is an instance of class
class.
By pushing a list of classes onto the stack, and sending the
message responses, a list of responses will be returned to the
stack. This list contains the responses defined for that set of
classes. Pushing a list of n classes specifies messages of order n.
For example:
integer float 2 >list
The above line builds a list with 2 class objects.
responses
This message pushes a list of all responses to messages of order 2
which are defined when the stack has an integer in level 1 and a
float in level 2. Of course, this includes any response defined
for any ordered combination of n subclasses of the n classes in
the list.
For a response object, the message msg pushes the message for
which this response is the definition. The message home pushes
the list of classes for which this response if the definition. To
summarize: A message can be sent to the stack. A message of
order n requires n receivers. The classes of these n receivers
must have a response defined to that message. This response is a
dictionary structure in itself. The home of this response is that
set of classes.
The message pdict will print all messages in the dictionary. This
can be a very useful method for the Quinta interpreter to
document itself.
The Macintosh implementation of Quinta is an interactive
interpreter with a text window used for all input and output.
Upon executing the Quinta application, there is a pause as the
dictionary is built. Then, it is your turn to act. The enter key is
the significant key. Like MPW, Quinta will not process input
until the enter key is pressed. The return key is not equivalent.
Multiple lines may be sent by selecting all the lines and pressing
enter. In fact, when processing input, Quinta first checks to see
if the selection is just that. Is there is a region of text darkened ?
If so, that text alone is processed. Otherwise, only the text on
the current line is processed. Therefore, if a string of messages
or response definition or other entry spans more than one line,
the entire selection must be selected before pressing enter.
The editor is a simple editor with no advanced features. Menus
are available and the editor is capable of operating on multiple
files, but the Worksheet is the primary window and file. To
write a Quinta program, use the editor (or any editor) to store
your message, class, and response definitions. Then, open that
file from within Quinta. Copy your Quinta source into the
worksheet. Select (highlight by dragging) all the lines you wish
to be executed (entered into the dictionary). Then, press Enter
(not Return), to process the source. The shortcut for all of the
above is the Load entry under the Command menu. This will
prompt you for a file of Quinta source, and it will execute all of
the code contained in that file. Try this procedure on the
example program sieve.q to see how it all works. In addition,
there is a message called load. Given a filename in a string, this
allows files to be loaded into the dictionary under Quinta
control.
The Class Hierarchy
From the interpreter, the message classes will push a list on the
stack containing all of the classes currently in the interpreter.
Typing the name of any class will push that class object onto the
stack.
The most important class of all Quinta classes is generic.
Generic is the parent class of all other object classes (analogous
to Smalltalk's OBJECT). The responses defined for generic are
for messages of a very general nature. These messages should
be acceptable for receiving objects of any class. However,
responses for class generic are not acceptable when the stack is
empty. Furthermore, there will be times that we wish to
perform certain actions regardless of the condition of the stack.
For this reason, there is another "class" above generic, called
control. There are no instances of class control which can be
created. We think of the interpreter itself as the only object of
class control. The interpreter is considered to always be on the
bottom of the stack, for the purposes of receiving messages. In
terms of a physical paradigm, it is most accurate to say that we
consider the stack to be resting on the interpreter. We send
messages to the stack, and the objects on TOS respond.
However, when we send a message to the stack and the stack is
empty or cannot respond, it comes to rest on the interpreter,
which either responds appropriately or assumes the role of
communicating an error message.
An example of a message with a response for class control is
pdict. This message simply prints every message in the
dictionary. It requires no parameters or input. It does not
examine or modify the stack. It simply provides information to
the user about the interpreter itself.
The class logical is used for conditionals. A logical object
represents either true or false. The messages true and false push
the only two unique instances of class logical.
The class quantity is the parent class for all types of data which
are manipulated using arithmetic operations. There are no
instances of class quantity. There may be, however, a number
of subclasses of class quantity. One such subclass is real. This
class also has no instances. Subclasses of real include integer and
float. These are general purpose classes for numerical
computations and representations. Any token beginning with a
digit or unary sign will be interpreted as a real. If it contains a
decimal point, it is considered a float. Floating point operations
in Quinta are always at extended precision. The interpreter
recognizes integers and floating point numbers when entered.
Exponential notation is ok for floating point numbers, but unless
the number contains a decimal point, it is assumed to be an
integer. Therefore,
345e56
will be read by the interpreter incorrectly. However,
345.e56
will work.
The class date is used for manipulations of dates. It is a subclass
of integer, and represents the number of seconds since 12:00 am
January 1, 1904. The message today of class control will push
the current date onto the stack. Since date is a descendant of
integer, dates may be manipulated arithmetically. Keep in
mind, that the units on date are seconds. So, to add one day to a
date, add 86400, not 1.
The class string is used for all data represented as strings of
characters. Strings in Quinta may be essentially any length and
are recognized by the interpreter when surrounded by double
quotes.
The class message is used to allow Quinta messages to operate
on other messages as a construct of the dictionary. The class
response is used to represent responses. Both respond to the
message print, for the purpose of displaying information from
the dictionary. A message object is recognized by the
interpreter when surrounded by underscores. For any message
on the stack, the message responses will push a list of all possible
responses to that message.
Class block is used for defining Quinta code. A block is a
stream of tokens which can be stored in a response. A block is
recognized by the interpreter when surrounded by [ brackets ].
A block, once created, may be used to defined a response to a
message for some set of classes. For examples, see above. In
addition, a block may be executed by sending the message send.
Blocks may be nested. Finally, blocks may be used in the dountil
and whiledo loops. See below for a discussion of the looping
constructs in Quinta.
Class aggregation is the parent class for collections of objects.
For example, class list is one of the subclasses in the aggregation
class tree. Currently list is the only significant structure
supported by aggregation.
A list object is a list of other objects. They are represented
internally in a manner similar to Lisp lists and respond to the
messages car and cdr, as in Lisp. Car pushes the head of the list.
Cdr pushes the tail of the list, which will ALWAYS be another
list, even if the list has zero or one members. A subclass of list,
called dim is used to specify dimensions of matrices. Two
integers can respond to the message >dim to create a new dim
object.
Class matrix is used for matrix operations. Matrices are arrays
of floating point numbers. For speed, it was decided that this
implementation of matrices would only handle numbers, not
general objects. Standard operations on matrices are supported
including inversion, determinant, transverse, multiplication, dot
product, etc.
Class variable is used for operations on Quinta variables. A
variable object is recognized by the interpreter when enclosed in
single quotes. Any object my be stored in a variable, such as
object 'var' sto
Any variable my have its contents recalled using rcl and any
variable may be purged from the dictionary using purge.
Class member is used to represent member variables of class
instances. Any user defined class may have member variables
for encapsulation of data. Member variables, in general, act as
regular variables, but their internal implementation is different.
When an object of a user defined class with member variables is
on TOS, sending the name of one of those member variables
enclosed in single quotes will push the object of class member
onto the stack. The object itself will be removed. Then, when
something is stored into the member variable using sto, the
surrounding object is returned to the stack.
Class constant is a subclass of variable which does not allow
modification of its contents. Any variable may be converted to a
constant using the message >const.
Messages
The following is a summary of some of the messages of Quinta.
The control response for the message messages will push a list of
every message in the dictionary (this can be a rather large list).
Any part of a line after a semicolon is considered a comment.
selfdoc adds comments to a number of messages in the
dictionary. These are not added at initialization to save
memory. Any message will have its comment printed (if it has
one) when the message itself is printed. All messages can be
printed using the message pdict.
>str converts an object to a string. If there is no response
specially programmed for the class of the receiver, the name of
the class of the receiver is the result.
>float converts an integer to a float
PI pushes the floating point approximation to pi
allmsg pushes a list of every message in the dictionary.
size pushes the size of the receiver. Defined for lists,
matrices, and strings
cos most standard trigonometric operations are defined.
These functions as well as exponential functions and the like are
defined only for class float. To define them for integers as well:
float integer inherit
(This uses the multiple inheritance feature of Quinta)
beep A simple beep, using whatever beep is currently the
system beep
depth gives the integer depth of the stack
if used for conditionals - not object oriented. the structure
if-else-endif (else optional) can enclose code for decision
making. Logical objects may respond. For example,
3 6 < if "no" else "yes" endif
div integer division operator. normally, division of 2
integers using /, yields a float. div yields an integer, discarding
the remainder.
dot dot products of matrices
idn given an integer, pushes the identity matrix of that size
norm normal distribution function approximation for floats
polyfit polynomial curve fits - see example program fit.q
rand random number
recurse recursively send the message currently being
executed. faster than sending the message itself
today the current date, as a date object
sl shifts an integer left, also sr, slb, srb
sinv given a variable, inverts the contents, also sneg, sto+, sto-
min minimum of two numbers, also max
The Debugger
Quinta has a source level debugger which is activated using the
Command menu. When the debugger is on, user defined
responses halt after every message, to allow the user to
manipulate data and monitor the program. Once the debugger
has halted a response, variables may be examined or modified.
In fact, any Quinta operation is legal. The message step causes a
single step. The message go turns the debugger off. Within a
response being debugged, halt causes a breakpoint. If an error
occurs while in the debugger, the debugger is turned off. The
message rstk will attempt to print a trace of the Quinta return
stack. This will give some indication of how the program has
progressed.
Loops
Quinta supports several flavors of loops. They fall into two
main kinds: object oriented, and structured. Structured loops
are legal only within response definitions. The kinds of
structured loops are: for-next, do-until, and while-repeat-end.
Below, (flag) refers to a logical object. Their usages:
(start) (end) (localname) for (contents of loop) (localvariable)
next
1 5 "q" for q print 'q' next
do (contents of loop) (flag) until
do q 1 + 'q' sto q 5 > until
while (flag) repeat (contents of loop) end
while q 5 < repeat q 1 + 'q' sto end
For the for-next loop, the local variable is a loop index. It is
created when the for loop begins. It's value is not destroyed
when the loop exits.
Object oriented loops, as I call them, are much nicer when
considered in a purely object oriented programming
framework. There are currently two messages which work
with object oriented loops: dountil and whiledo. Each of these
expects two block objects to be on the stack. The block object in
level 2 is the body of the loop; ie. the code to be executed on each
pass. The block object in level 1 may contain any messages
desired, but it must leave a logical object on the stack. This is
used for the termination condition of the loop. The dountil loop
checks for termination AFTER each pass. The whiledo loops
checks for termination BEFORE each pass. Therefore, the
dountil loop is guaranteed to executed its body at least once
while the whiledo loop is not. An example:
[ 1 "x" local [ 'x' ++ x print ] [ x 10 > ] dountil ]
send
This loop prints the numbers 2 through 11.
Conditionals
The best way to handle conditionals in Quinta is using the
message cond. Cond is of order 3 and requires:
3: a block of code for the ELSE case
2: a block of code for the THEN case
1: a logical object
If the logical is true, then the block in level 2 is sent. Otherwise,
the block in level 3 is sent. Example,
[ "Nope!" ] [ "OK" ] true cond
Data Constants in Quinta Code
Data constants are textual representations of objects which are
recognized by the interpreter and automatically converted and
pushed onto the stack. Currently, only integers, floats, strings,
and blocks are recognized by the interpreter. The only way to
construct other objects is to use messages defined for that
purpose. For example, pushing n objects onto the stack and then
pushing n as an integer allows one to use the message >str to
build a list with those n objects. Also, the message new is
defined for the class class. It creates a new object for the given
class. The value of this new object is, in general, undefined. A
builder response may be defined for a class to initialize the value
of a newly created instance, using setbld. Builder responses are
analgous to C++ constructor functions. Builder responses to
NOT automatically execute the builder response of the parent
class. Executing the builder response for any class can be
accomplished by using the message bld sent to the desired class
object. See sieve.q for examples of builder responses.
Predefined Quinta classes do not currently have any builder
responses.
Multiple Inheritance
Quinta supports multiple inheritance. However, the feature has
not been tested extensively. All subclasses are created with a
principle parent class. After that, two classes may establish a
parent - child relationship using the message inherit. For
example, to make integer a subclass of logical (thus allowing all
integers to act as logical values, similar to C):
logical integer inherit
Note that this feature is best used with your own subclasses.
Most Quinta predefined classes should not inherit messages in
their current implementation. For example, the following is an
invitation for a system crash:
string integer inherit ; Don't do this
Interacting with the Interpreter
After each press of enter, Quinta prints the current status of the
stack, including the depth and the objects in the first 4 levels.
This is reconfigurable; see below. Quinta also will report a
number of error messages. For example, consider the
following message:
wrong
Quinta will respond by saying that the message wrong is not in
the dictionary. Messages that occur within responses and user
programs are handled similarly. Quinta always attempts to
diagnose the location of the error and report this to the user.
The Window menu may be used to switch back and forth
between edit windows in the Quinta interpreter.
Dictionary Memory Usage
The Quinta dictionary uses a good deal of memory. The
algorithms I have used for the interpreter are designed with
priority given to speed of execution. The consequence of this
decision was ENORMOUS memory usage. For each message,
this memory usage is exponentially related to the order of the
message. For this reason, Quinta does not allow messages of
order greater than 3. A number of methods were used to keep
memory usage minimal, and as a result, some insertions into the
dictionary can be rather slow. For example, the creation of an
order 3 message defined for three generic objects on the stack
would require a great deal of memory and work. The reason is,
this message would have to be inherited to all of the descendants
of class generic in all combinations of 3. However, a message of
order 3 defined for 3 logical objects would be more benign:
logical has no subclasses (in the core version). Multiple
inheritance can cause a delay as well. However, all algorithms
have been designed to be as fast as possible at runtime. Extra
work is done when manipulating the dictionary specifically so
that the actual accesses to the dictionary will be more rapid.
Because of the enormous memory usage involved with multiple
order messages, sometimes it is best to keep the order of the
message low and allow the possibility of stack underflow. For
example, the message put is predefined in the interpreter and is
used for modifying lists and matrices. For a list, it should
require:
3: an object to be placed into the list
2: the list itself
1: the index into the list where the object should be put
However, this would require a response definition for class
integer:list:generic. This requires well over 100 kilobytes of
storage for the response table of the put message! Instead, I
have defined put to be of class 2. The implementation blindly
assumes that there is an object in level 3. This dramatically
reduces memory requirements.
The message subclass and respond are even better examples. To
be correctly implemented, they should be of order 4. Order 4
messages require an unbelievable amount of storage.
For any message, the amount of storage (in bytes) which it
requires is printed when its message object is printed using the
message print. The message pdict will, of course, include this
info with every message it prints.
Interpreter Configuration
The interpreter has a number of limitations built into itself.
These limitations are as follows:
1 Maximum length of a message or class name
32
2 # of levels of stack to print
4
3 Maximum members for user classes
128
4 Maximum total number of messages
1023
5 Maximum total number of classes
48
6 Maximum number of subclasses per class
64
7 Maximum main stack depth
1000
8 Maximum number of locals at one time
64
9 Maximum nesting of Quinta responses
64
10 Maximum number of messages in a response
128
11 First Quinta message to be sent
Qmain
All of these parameters are configurable from within Quinta.
By sending the message getconfig, a list of these parameters will
be pushed onto the stack. The user may modify this list and then
send the message setconfig. Changes will take effect next time
the interpreter application is opened. Be very careful setting
these parameters ! In general, do not change the first Quinta
message unless you are sure you know what you are doing.
The length of the configuration list will probably be changed in
future versions of Quinta. Currently the list contains 11 items,
and all future versions will leave those 11 items intact. In other
words, item 7 will always be the main stack depth. However, I
may add an item 12, 13, etc.
QCore
Upon opening the Quinta application, the dictionary is built and
then the interpreter looks for a file called Qcore. If this file is
found, all of the Quinta source code inside it is executed. This
can be a very powerful way to extend the interpreter in Quinta.
Oft used messages and classes may be installed into this file and
they will be available each time Quinta is used. Personalized
messages may be installed.
Origins
The design of Quinta has been influenced by a number of
different sources. It was originally designed to be an object
oriented version of Forth. I have also included similarities to
Lisp, Smalltalk and the HP-28 calculator programming
language. However, although Quinta has received some
inspiration from the excellent Hewlett Packard 28 calculator
line, it is not intended to be an emulator for those products (or
any other products/languages).
Credits
The user interface code for the Quinta interpreter is built on
Transkel and TransEdit, by Paul Dubois. Although I have made
some modifications to his code, the basic structure of his work
remains.