Chapter 6

SOOP – A Simple Object Oriented Language

 

 

6.1  Using SOOP with MuLE 

A SOOP window is created by executing the file "launcher.s" in DrScheme.  In the MuLE interface window that appears, click on the "SOOP - functional" button or select from the pull-down menu.  This opens a SOOP window by binding a variable to the result of a call to the procedure make-SOOP.   Once in the SOOP environment, existing SOOP program files can be opened and loaded into the simple editing window of the SOOP interpreter by selecting the “open” button.  The opened file will appear in the upper window and may be modified before executing with the SOOP interpreter.  Everything typed in the lower window will be interpreted as an expression by SOOP. 

 

 

6.2  The SOOP Intrepreter

The SOOP interface is a double paned window.  Both the upper definitions window and the lower interface window can be used to enter in programs, though it is recommended that the definitions window be used for this purpose.  The interface window accepts one expression at a time and executes it when the enter key is hit, whereas the definitions window allows for several expressions on different lines to be entered at once.  They will only be executed when the "execute" button is clicked.  All of the output will appear in the interface window.  Besides this benefit, the definitions window allows for the saving and retrieving of files, opposed to the interface window which does not.  If the user wishes to alternate between the two, bear in mind the environment, or list, that has been created is used by, and thus is changed by, either window.

 

At the command level, SOOP understands three meta-commands: list, help and clear.  Each command is executed in SOOP by entering the command, without parentheses, at the SOOP prompt. 

 

list    A debugging instruction that will list all of the environment (current variable) bindings.

 

clear   Clears the environment (current variable) bindings.  This is especially useful between program executions.

 

help    Displays the SOOP grammar, i.e. valid SOOP expressions.

 

There are only two legal operations in the SOOP environment, binding a variable to an instantiation of an object (via the assign keyword) and sending a message to an object (via the send keyword).  Examples are the following:

           

            (assign x make-box)

     (send x msg)

 

Where msg is a legal message for the class box.

 

Simplistic comments are allowed in SOOP programs.  If the first two characters on a line of a SOOP program are // then the line is ignored by the SOOP interpreter.  There can be no white space or other characters before the //, i.e. the // must be in columns one and two of the line.

 

 

6.3  Classes

 

There are currently 7 classes recognized in SOOP:

·The class box (a variable)

·The class block (a set of messages)

·The class if (a form of if expression)

·The class loop (a form of while expression)

·The class stack (a stack)

·The class queue (a queue)

·The class array (an array)

 

Classes may be instantiated but not created through the run-time environment.  To create a new class in SOOP, a new class (really a function that returns a function) must be added to the file “SOOPObj.s”, and the class name must be added to the list MAKEWORD which is defined as a local variable at the beginning of the SOOP function.  All class names must begin with the word make-.  Thus the seven listed classes have the names: make-box, make-block, make-if, make-loop, make-stack, make-queue, and make-array.

 

 

6.4  Objects

 

Objects are instantiated through the assign keyword.  To create an instantiation one must assign a name to the class name.  To create two instantiations of a box, for example, the following commands would be used:

 

            (assign x make-box)

     (assign y make-box)

 

When these commands are entered two different windows will appear on the screen, one for x and one for y.  They will both be box objects.

 

Every object is represented in SOOP by a text window.  These windows may be written into, but the act of writing in a box does NOT change the values stored in the object.  To change the state of an object, one must send the object messages (see below).  The text windows are only meant to provide a visual representation of the state of an object.

 

Multiple messages can be cascaded, but parentheses are not allowed.  Thus and a box object may be sent the message (send x + send y * 3) which is interpreted as (send x + (send y * 3)).  However, the latter message would result in an error because parentheses are not allowed.

 

Different objects recognize different messages, but to send a message the keyword send must be used.  The syntax for sending a message is:

 

            (send x msg)

 

where x is a name given to an instantiation of an object and msg is a legal message for that object.

 

Every SOOP object recognizes the following messages:

 

type               returns the class of the object as a string.

 

empty?            Returns true if the value (for boxes), test condition (for select/loop classes), block, queue, stack, or array is empty.

 

print             prints the value of all values, expressions, or messages contained in the object.

 

 

6.4.1   The Box Object 

 

Boxes provide variables to the SOOP environment.  A box object stores a numerical value and accepts certain messages that allow manipulation of their value.  In particular, a box understands the following messages:

 

type                                       returns “box”.

 

empty?             returns true if there is no value stored in the box and false otherwise.

 

set-name<msg>       sets the name of the window to “msg”

 

get                                         returns the value stored in the box.

 

set-val<expr>             executes the expression <expr> and sets the value of the box to the result.

 

<arith_op> msg | obj            performs one of + - * / >  < on the result of the evaluating message, msg or on getting the value from the object obj.  Note that an object name can be used without the formal message pattern of send obj get.  This is an exception to the requirement that all messages be sent with an explicit send.  A simple binary operation can also be used here without the formal send syntax, i.e.

                                                                        (send x + y)

                                                            is a legal message and is interpreted as

                                                                        (send x + send y get)

 

Messages can be cascaded as mentioned above, though parentheses are not allowed.

 

Examples of box commands include the following:

 

(assign x make-box)

(send x set-name var1)

(send x set-val 10)

(assign y make-box)

(send y set-name var2)

(send y set-val 5)

(assign z make-box)

(send z set-name var3)

(send z set-val send x + y)

(send z set-val send x + send y * z)

 

Note in the penultimate expression, the box named z gets the value 15.  In the last expression, the z box gets the value 85. 

 

 

6.4.2 The Block Object

 

Block objects store a sequence of messages.  When the block is executed, each message is sent sequentially.

 

To create a block object the assign syntax is used.  For example, the expression

                                   

(assign blk1 make-block)

 

creates an instance of the block object with the name blk1.  There are, at this time, no messages in the block.  These messages can be added by sending the add-expression message and deleted by sending the delete-expression message as described below.

 

The legal messages for a block are as follows:

 

type                                                   returns “block”.

 

empty?             returns true if there is nothing in the block.

                       

set-name<msg>     sets the name of the window to “msg”.

 

add–expression<msg>            adds the expression “msg” to the end of the end of the block of messages to be executed.

                       

delete–expression<msg>            deletes the last expression from the block.

           

empty–all                                       sets the block to empty.

                       

execute            executes the messages in its block one at a time.

 

Examples of legal messages for a block include the following:

 

(assign blk1 make-block)

(send blk1 set-name new-block)

(send blk1 add-expression send x set-val 3)

(send blk1 add-expression send y set-val 23)

(send blk1 add-expression send z set-val send x + y)

(send blk1 execute)

(send blk1 delete-expression)

(send blk1 execute)

 

 

6.4.3 The If Object

 

If objects contain a test message, a true message and a false message.  The true and false messages may be blocks.  To create an if object instantiation, the assign syntax is used with the make-if procedure.  For example:

 

(assign if1 make-if)

 

At this point the name if1 is bound to an instance of the if object.  None of the messages (test, true, false) has been initialized yet.  Before the if object can be used, each of these messages must be initialized (see below).  Once the test message has been initialized, the if object can be executed by passing the object the message execute.  For example, the if1 object can be executed by the syntax:

 

            (send if1 execute)

 

Both true and false messages are optional.

 

The legal messages for an “if” object are as follows.

 

type                                                   returns “if”.

 

empty?             returns true if object’s test condition contains the symbol ‘empty.

           

set-name<msg>     sets the name of the window to “msg”.

 

add-test <expr>            adds the expression <expr> as the test condition.

                       

add-true <expr>            adds the expression <expr> as the true condition.

                       

add-false<expr>            adds the expression <expr> as the false condition.

                       

empty-all            sets all conditions and expressions to empty.

                       

execute            executes the test condition by sending the appropriate message.  If the

result is “true”, the true message is sent, otherwise the false message is

sent.

 

Examples of legal “If” object syntax include the following:

 

(assign if1 make-if)

(send if1 set-name new-if)

(send if1 add-test send x < 3)

(send if1 add-true send y set-val 23)

(send if1 add-false send z set-val send x + y)

(send if1 execute)

(assign blk1 make-block)

(send if1 add-true blk1)

 

Note that the penultimate statement above creates a block object.  The final statement sets the true condition of the “if“object to be the block.

 

 

6.4.4 The Loop Object

 

Loop objects contain a test message and a body message (which may be a block).  When a loop object receives the message execute, the test message (which must be a message that begins with a send) is sent and, if the result is “true”, the message(s) in the body are sent.   The test message is then sent again and, if the result is again “true”, the body message(s) are again sent.  This continues until the test message returns “false”.

 

Loop objects are created using the assign syntax.  To create a loop named lp1 the following expression could be entered in SOOP:

 

(assign lp1 make-loop)

 

At this point the loop is uninitialized.  To initialize the loop, the message add-test and add-body can be used as described below.  The body is optional.  To execute the loop, the loop object must be sent the message execute:

 

            (send lp1 execute)

 

Valid loop messages are as follows.

 

type                                                   returns “loop”.

 

empty?             returns true if the test condition contains the symbol ‘empty.

           

set-name<msg>            sets the name of the window to “msg”.

 

add-test <expr>                                   adds the expression <expr> as the test

condition.

                       

add-body <expr>                                   adds the expression <expr> as the

body.

                       

add-false<expr>            adds the expression <expr> as the false condition.

                       

emtpy-all            sets all conditions and expressions to empty.

                       

execute            executes the test condition by sending the appropriate message.  If the

result is “true”, the body message is sent, otherwise execution halts.

 

Examples of syntax:

 

(assign loop1 make-loop)

(send loop1 set-name new-loop)

(send loop1 set-name new-loop)

(send loop1 set-body send x set-val send x – 1)

(send loop1 execute)

 

 

6.4.5 The Queue Object

 

A queue object is a list in which elements are entered one at a time through one end and removed in the same order they were entered.  In other words, the first element entered in the queue will be the first element that can be removed from the queue.

 

To create a queue object instantiation, the assign syntax is used with the make-queue procedure.  For example:

 

(assign myqueue make-queue)

 

At this point the name myqueue is bound to an instance of the queue object.  The queue is completely empty at this point.  The queue object executes multiple messages.  It accepts the following messages:

 

type                                                   returns “queue”.

 

empty?             returns “true” if the queue is empty.

           

set-name<msg>     sets the name of the window to “msg”.

 

enqueue<msg>            adds the message "msg" to the end of the end of the list of messages to be executed.            

 

dequeue            deletes the first expression from the queue.     

 

atfront                                            gets the first expression from the queue.

 

emtpy-all            sets queue to empty.

    

 

Examples of syntax:

 

(assign myqueue make-queue)

(send myqueue enqueue 'a)

(send myqueue enqueue 3)

(send myqueue dequeue)

(send myqueue atfront)

(send myqueue dequeue)

 

 

6.4.6 The Stack Object

 

A stack object is a list in which elements are entered one at a time through one end and removed in the opposite order they were entered.  In other words, the last element entered in the stack will be the first element that can be removed from the stack.

 

To create a stack object instantiation, the assign syntax is used with the make-stack procedure.  For example:

 

(assign stk1 make-stack)

 

At this point the name stk1 is bound to an instance of the stack object.  The stack is completely empty at this point.  The stack object executes multiple messages.  It accepts the following messages:

 

type                                                   returns “stack”.

 

empty?             returns “true” if the stack is empty.

 

set-name<msg>     sets the name of the window to “msg”.

 

push <msg>            puts the message "msg" to the end of the end of the list of messages to be executed.

 

pop                                                     deletes the last expression from the stack.

 

top                                                     gets the last expression in the stack.

 

empty-all                                       sets the stack to empty.

 

 

Examples of syntax

 

(assign stk1 make-stack)

(send stk1 push 'a)

(send stk1 push 3)

(send stk1 pop)

(send stk1 top)

(send stk1 pop)

 

 

6.4.7 The Array Object

 

An array object is a static list of elements that can be accessed using an index number.  To create an array object instantiation, the assign syntax is used with the make-array procedure.  For example:

 

(assign array1 make-array)

 

At this point the name array1 is bound to an instance of the array object.  The array is completely empty at this point.  The array object executes multiple messages.  It accepts the following messages:

 

type                                                   returns “array”.

 

empty?             returns “true” if the array is empty.

 

set-name<msg>            sets the name of the window to “msg”.

set-size<num>                            sets the size of the array.             

 

add<msg><num>            adds the “msg” to the “num” position in the array.

 

get<num>            gets the msg at the “num” position of the array.

 

empty-all                                       sets the array to empty.

 

Examples of syntax:

 

            (assign array1 make-array)

     (send array1 set-size 5)

     (send array1 add 5 4)

 

 

6.5  A Complete BNF Grammar for SOOP

 

<soop>

: : =

<soop_command>

<soop_command>

: : =

help   |   clear   |   list   |   <soop_stmt_list>

<soop_stmt_list>

: : =

<soop_stmt>   |   <soop_stmt> <soop_stmt_list>

<soop_stmt>

: : =

( assign <id> <makes> )   |   ( send <id> <obj> )

<makes>

: : =

make-block    |

make-stack    |

make-if           |

make-loop      |

make-box       |

make-queue  |

make – array

<obj>

: : =

<obj_expr>   |   <obj_msg>

<obj_expr>

: : =

<block_expr> |

<stack_expr> |

<if_expr>                    |

<loop_expr>              |

<box_expr>                |

<queue_expr>           |

<array_expr>

<obj_msg>

: : =

<block_msg>             |

<stack_msg>             |

<if_msg>                    |

<loop_msg>              |

<box_msg>                |

<queue_msg>           |

<array_msg>

<block_msg>

: : =

set-name <id>           |

reset                           |

empty –all                   |

<set_expression> <block_msg>     |

delete-expression                             |

<set_execute> <block_msg>

<block_expr>

: : =

type   |   empty?

<set_expression>

: : =

add-expression <block>   |

add-expression send <id> <obj>

<set_execute>

: : =

send <id> execute

<block>

: : =

<block_msg>   |   <block_expr>

<stack_msg>

: : =

set-name <id>           |

push <obj_expr>      

reset               |

pop                 |

empty-all

<stack_expr>

: : =

top   |   type   |   empty?

<if_msg>

: : =

set-name <id>                       |

<set_test> <if_msg> |

reset                                       |

<set_true> <if_msg>            |

<set_false> <if_msg>           |

<set_execute> <if_msg>     |

empty-all

<if_expr>

: : =

type   |   empty?

<set_test>

: : =

add-test <bool_expr>

<set_true>

: : =

add-true send <id> <obj>   |

add-true <block>

<set_false>

: : =

add-false send <id> <obj>   |

add-false <block>

<loop_msg>

: : =

set-name <id>           |

empty-all                     |

reset                           |

<set_test> <loop_msg>       |

<set_body> <loop_msg>     |

<set_execute> <loop_msg>

<loop_expr>

: : =

type   |   empty?

<set_body>

: : =

add-body send <id> <obj>   |

add-body <block>

<box_msg>

: : =

set-name <id>           |

set-val <number>      |

reset

<box_expr>

: : =

type   |   empty?   |   get   |   send <id> <op> <obj_expr>

<op>

: : =

+   |   -   |   *   |   /   |   >   |   < |

<queue_msg>

: : =

set-name <id>                       |

enqueue <obj_expr> |

reset                           |

dequeue                     |

empty-all

<queue_expr>

: : =

atFront   |   type   |   empty?

<array_msg>

: : =

set-name <id>                       |

add <obj_expr> <number>  |

reset                                       |

set-size <number>                |

empty-all

<array_expr>

: : =

type   |   empty?   |   get <number>

 

 

6.6  Also See References Attached.