CS 334
Programming Languages
Spring 2000

Assignment 8
Due Thursday, 4/25/2002


You will need to use the GJ compiler installed on the Dells for this assignment. Instructions on using GJ are available on-line.


  1. Eiffel allows you to change the type of an instance variable in a subclass to a type which is a subtype of that declared in the supertype. Thus if class A has instance variable x: C, then it is legal in Eiffel for subclass B to redeclare x: D, where D is a subclass of C. Give an example of a class A and subclass B such that when the type of instance variable x is changed as above from a class C to a subclass D, a type error results.
    Hint: Let m be a method of A which assigns a value of type C to x. Show what can go wrong in B when m is inherited and the type of x changes.

  2. We provided the definitions of Eiffel classes LINKABLE and BILINKABLE in class:
    class LINKABLE [G]
    
    feature
    
       item: G;              -- value held
       right: like Current;  -- Right neighbor
    
       putRight (other: like Current) is
             -- Put `other' to the right of current cell.
          do
             right := other
          ensure
             chained: right = other
          end;
    
    end -- class LINKABLE
    
    
    class BILINKABLE [G] inherit
    
            LINKABLE [G]
               redefine
                  putRight
               end
               
    feature -- Access
    
       left: like Current;   -- Left neighbor
    
       putRight (other: like Current) is
             -- Put `other' to the right of current cell.
          do
             right := other;
             if (other /= Void) then
                other.simplePutLeft (Current)
             end
           end;
    
        putLeft (other: like Current) is
               -- Put `other' to the left of current cell.
            do
               left := other;
               if (other /= Void) then
                  other.simplePutRight (Current)
               end
            ensure
               chained: left = other
            end;
    
    feature {BILINKABLE}
    
       simplePutRight (other: like Current) is 
             -- set `right' to `other'
          do
             right := other
          end;
    
       simplePutLeft (other: like Current) is
             -- set `left' to `other'
          do
             left := other
          end;
    
    invariant
    
       rightSymmetry:
          (right /= Void) implies (right.left = Current);
       leftSymmetry:
          (left /= Void) implies (left.right = Current)
          
    end -- class BILINKABLE
    
    While there are no type errors in the definition of BILINKABLE, please write some code which shows that BILINKABLE is not a subtype of LINKABLE. That is, write a method or procedure which takes a parameter of type LINKABLE (and perhaps others) and which is type-correct when a value of type LINKABLE is passed in, yet which crashes if an object of type BILINKABLE is passed in.

  3. The following are interfaces for singly and doubly-linked nodes and a linked list in GJ. Note that NodeIfc and DbleNodeIFc are parameterized by the type of their next field, T, so that they can be used with F-bounded polymorphism (you can think of setNext as a binary method) While T is the type of the next field of the node, String is the type of the values held in the node:
    // Interface for node objects w/ values of type String
    
    public interface NodeIfc<T>{
    
       // Returns following node.
       T getNext();
    
       // Hook up newNext to the right of this
       void setNext(T newNext);
    
       // Returns value stored in node
       String getValue();
    
       // Sets value stored in node to newVal
       void setValue(String newVal);
    }
    -----------------------------------------
    
    // Interface for doubly-linked node objects w/ values of type String
    
    public interface DbleNodeIfc<T> extends NodeIfc<T>{
    
       // Returns preceding node.
       T getPrevious();
    
       // Set node bfore this to be newPrevious
       void setPrevious(T newPrevious);
    }
    
    -----------------------------------------
    // Interface for Ordered List of Strings, parameterized by the type of node.
    // Note use of F-bounded polymorphism.
    import java.util.Enumeration;
    
    public interface OrdListIfc< N implements NodeIfc<N>>{
    
       // Return true iff val is in list
       boolean inList(String val);
    
       // Add newNode to list (in proper order).  Duplicates are OK.
       void add(N newNode);
    
       // Return an Enumeration which will go through all elements of list in order.
       Enumeration getEnumerator();
    
       // If val is in list, delete first copy and return true, else return false.
       boolean delete(String val);
    }   
    
    Please write GJ classes:

    1. SNode for singly-linked nodes (that implements NodeIfc<SNode>).

    2. DNode for doubly-linked nodes (that implements DbleNodeIfc<DNode>).
      Warning: The methods setNext and setPrevious in DNode must each set two links so the new elements are hooked up properly. You may find it easiest to add protected auxiliary methods to just set single links in order to get these to work properly.

    3. OrdList<N implements NodeIfc<N>> for linked lists (that implements OrdListIfc<N>).

      The class OrdList should result in a singly linked list if it is instantiated with SNode, and a doubly-linked list if instantiated with DNode. (Note: SNode and DNode cannot be subclasses.)

    4. Depending on the instantiation, OrdList can represent either singly or doubly-linked lists, but doubly-linked lists usually provide more specialized implementations and methods. Write class DOrdList<N implements DbleNodeIfc<N>> which extends OrdList and adds a tail instance variable and method

         // Return Enumeration which goes through elements of list in reverse order.
         public Enumeration getRevEnumerator();
      

      You will also have to override methods add and delete to ensure that the tail instance variable gets updated properly. See if you can write them so that you call the inherited method from the superclass and then simply execute a few extra instructions to make sure that tail has the proper value.

  4. Building on your code from last week, please rewrite your environment-based (call-by-value) PCF interpreter in Java or GJ. You will need a way to hold environments (a Vector would be fine). The evaluation of terms will follow the same rules as your ML interpreter. Test your program by providing a main program that will evaluate a sample PCF term. Don't forget that you are now using an imperative data structure to hold the environment. That is, when you evaluate an expression with a given environment, it may change the environment. To avoid having to worry about how to restore the environment (which is not so easy because we have functions returning functions -- a pure stack discipline won't work!), clone the environment when you pass it as a parameter to a method to evaluate the term.