A procedure definition,
is interpreted by creating a procedure object consisting of the following things:
A procedure call,
is interpreted by performing the following steps:
Let's look at how LOBO interprets the following program:
After the (to bar (y) ...) and (to foo (x) ...) statements have been interpreted (by adding the procedure objects for bar and foo to the procedure a-list) the environment stack, the program stack, and the graphic accumulator look like this:
environment stack | program stack | graphic accumulator |
() | ((foo 20)) |
The procedure call to foo is interpreted by pushing a frame (containing the binding between foo's parameter x and its value 20) onto the environment stack and pushing a return statement and the statements comprising the body of foo onto the program stack:
environment stack | program stack | graphic accumulator |
(((x 20))) | ((forward x) (bar 10) (forward (* 1.5 x)) (return)) |
The (forward x) statement can now be interpreted (using the top frame of the environment stack to resolve the reference to x). The call to bar in the body of foo is now interpreted:
environment stack | program stack | graphic accumulator |
(((x 20))) | ((bar 10) (forward (* 1.5 x)) (return)) |
This is accomplished by pushing a frame (containing the binding between bar's parameter y and its value 10) onto the environment stack; and pushing a return statement and the repeat statement which comprises bar's body onto the program stack:
environment stack | program stack | graphic accumulator |
(((y 10)) ((x 20))) | ((repeat 4 (forward y) (right 90)) (return) (forward (* 1.5 x)) (return)) |
After interpreting the repeat statement (using the top frame of the environment stack to resolve the reference to y in its body),
environment stack | program stack | graphic accumulator |
(((y 10)) ((x 20))) | ((return) (forward (* 1.5 x)) (return)) |
the return statement is interpreted by popping the environment stack. The effect of the return statement is to restore the environment to what it was before bar was called:
environment stack | program stack | graphic accumulator |
((((x 20))) | ((forward (* 1.5 x)) (return)) |
Now the (forward (* 1.5 x)) statement in the body of foo is interpreted. Note that the reference to x is resolved by looking up its value in the top frame of the restored environment. Afterwards, the only thing which needs to be done is interpret the return statement by popping the environment stack. This implements the return from the call to foo:
environment stack | program stack | graphic accumulator |
((((x 20))) | ((return)) |
We are done!!
environment stack | program stack | graphic accumulator |
() | () |
* Because procedure calls are handled this way, LOBO is an example of a dynamically scoped language. Scheme is lexically scoped.