5.3. The Full Interpreter

The full interpreter performs simplifications and, if it gets an action after simplifying, performs actions. Let's follow a simple evaluation by the interpreter, using the following program.

  def main = 
    printList ['H','o','w',' ',
               'm','a','n','y',' ',
               's','e','c','o',
               'n','d','s','?'];
    readInt ~> 
     (n ->
      let m = n/60 in
       let h = n/3600 in
        let secs = n - 60*m in
         let mins = m - 60*h in
          printList 
           [h, ':', mins, ':', secs]
         end
        end
       end
      end
     )
  end

The value of main is the following tree. To help in reading it, I have written the parameter name with each FUNCTION node.

 
                 -----~>------
                /             \
              ~>               FUN(2)(n)
             /  \                |
    printList   FUN(1)       --APPLY--
       |         |          /         \
       :      readInt     FUN(3)(m)    --(/)--
      / \                  |          /       \
   'H'   :               APPLY      PARAM     60
        / \             /     \      (2)
     'o'  …            /       \
                    FUN(4)(h)  --(/)--
                     |        /       \
                   APPLY    PARAM     3600
                  /     \    (2)
         FUN(5)(secs)    -
          |             / \
        APPLY          /   \
       /     \       PARAM  *
FUN(6)(mins)  -       (2)  / \
      |      / \         60  PARAM
      |  PARAM  *             (3)
      |   (3)  / \
      |       60  PARAM
      |            (4)
   printList
      |
      :
     / \
 PARAM  :
  (4)  / \
    ':'   :
         / \
    PARAM   :
     (6)   / \
        ':'   :
             / \
        PARAM   []
         (5)

Simplification of main makes no change, since its root is labeled by ~>. The top-level interpreter gets that tree back. Since the result is an action (because its root is labeled ~>) the top-level interpreter calls performAction on that tree.

PerformAction starts by simplifying the left subtree of the top ~> node, which makes no change to it. So performAction does a recursive call on the left subtree.

Now performAction is passed the following tree.

      printList
         |
         :
        / \
      'H'  :
          / \
        'o'  …

It performs the printList, causing the program to write

How many seconds?
The result of this printList is [ ], which performAction returns to its caller (as a tree).

Now we are back to the performAction that was called on the lower ~> node. It has received result [ ] from its left subtree, and now it

  1. simplifies its right subtree (the FUN(1) node), which makes no change, and

  2. builds and simplifies tree

               APPLY
              /     \
           FUN(1)    []
            |
           readInt
    

The result of simplifying the action is just readInt. performAction performs that action by reading an integer.

Imagine that it reads 1000. Now performAction returns 1000 (as a tree) to the copy of performAction that was originally called on the root of the tree.

The top performAction builds and simplifies tree

                                    APPLY
                                   /     \
                               FUN(2)(n)  1000
                                 |
                             --APPLY--
                            /         \
                          FUN(3)(m)    --(/)--
                            |          /       \
                         APPLY      PARAM     60
                        /     \      (2)
                       /       \
                    FUN(4)(h)  --(/)--
                     |        /       \
                   APPLY    PARAM     3600
                  /     \    (2)
         FUN(5)(secs)    -
          |             / \
        APPLY          /   \
       /     \       PARAM  *
FUN(6)(mins)  -       (2)  / \
      |      / \         60  PARAM
      |  PARAM  *             (3)
      |   (3)  / \
      |       60  PARAM
      |            (4)
   printList
      |
      :
     / \
 PARAM  :
  (4)  / \
    ':'   :
         / \
    PARAM   :
     (6)   / \
        ':'   :
             / \
        PARAM   []
         (5)

Simplification yields the following tree (after several steps).

     printList
        |
        :
       / \
      0   :
         / \
      ':'   :
           / \
         16   :
             / \
          ':'   :
               / \
             40   []

performAction continues by simplifiying that list, which replaces each : node by a CONS node, and then prints the members of the list from left to right, which shows

0:16:40
Finally, performAction returns [ ] (the answer from printList) to its caller, the top-level interpreter, which stops.