// file:    ClosedStringList.java
// author:  Robert Keller
// purpose: closed list of Strings
//          This class can be used as a closed-list stack
//          or as open list (but be careful).
//          In the stack operations, push and 

// The empty list is called nil.
// To create an empty modifiable stack, use the constructor ClosedStringList
//
// cons(F, R)   creates a list from a String F and a list R
// R.cons(F)    creates a list from a String F and a list R
// L.first()    returns the first of a non-empty list L
// L. rest()    returns all but the first of a non-empty list
// L.isEmpty()  tells whether L is empty.
// L.toString() creates a String representation of L
// L.push(F)    modifies L in-place to have new top element F
// L.pop()      returns the top element of L and removes it

import java.io.PrintStream;

class ClosedStringList
{
StringCell handle;		// pointer to the first cell in the list


ClosedStringList()		// create new empty modifiable list
  {
  handle = null;
  }


String first()			// return the first element of the list
  {
  return handle.first;
  }


ClosedStringList rest()		// return the rest of the list
  {
  return new ClosedStringList(handle.rest);
  }


boolean isEmpty()		// check for emptiness
  {
  return handle == null;
  }


// create new list given first and rest

static ClosedStringList cons(String first, ClosedStringList rest)
  {
  return new ClosedStringList(new StringCell(first, rest.handle));
  }


// create new list with first and this as rest

ClosedStringList cons(String first)
  {
  return cons(first, this);
  }


// create list with StringCell as its first cell

ClosedStringList(StringCell handle)
  {
  this.handle = handle;
  }


// note that nil != null

final static ClosedStringList nil = new ClosedStringList(null);	


// mutating methods for Stack

// add a new first element to list, destructively changing this

void push(String s)
  {
  handle = new StringCell(s, handle);
  }


// remove first element and return it, destructively changing this

String pop()
  {
  String top = first();
  handle = rest().handle;
  return top;
  }


// make a String representation of the list

public String toString()
  {
  StringBuffer b = new StringBuffer();
  ClosedStringList L = this;
  b.append("(");
  if( !L.isEmpty() )
    {
    b.append(L.first());
    L = L.rest();
    while( !L.isEmpty() )
      {
      b.append(" ");
      b.append(L.first());
      L = L.rest();
      }
    }
  b.append(")");
  return b.toString();
  }


// Test program

public static void main(String arg[])
  {
  // The first tests are of the open-list variety, for contrast
  // These are followed by tests of the stack aspect

  // Create initial list

  System.out.println();
  System.out.println("Treating L as open list");

  ClosedStringList L = nil.cons("delta").cons("gamma").cons("beta");

  System.out.println("L = " + L); 

  System.out.println("L.first() = " + L.first()); 

  System.out.println("L.rest(L) = " + L.rest()); 

  System.out.println("L.rest().first() = " + L.rest().first());

  System.out.println("L.rest().rest() = " + L.rest().rest());

  System.out.println("nil = " + nil); 

  System.out.println("L = " + L); 


  // The next tests exploit the closed-list aspect, treating S as a stack

  System.out.println();
  System.out.println("Treating S as a stack");

  ClosedStringList S = new ClosedStringList();

  System.out.println("S = " + S); 

  S.push("alpha");
  System.out.println("S = " + S); 

  S.push("beta");
  System.out.println("S = " + S); 

  S.push("gamma");
  System.out.println("S = " + S); 

  S.push("delta");
  System.out.println("S = " + S); 

  while( !S.isEmpty() )
    {
    System.out.println("S = " + S); 
    System.out.println("S.pop() = " + S.pop());
    }

  System.out.println("S = " + S);
  }
}


// StringCell is the building block for ClosedStringList

class StringCell
{
String first;
StringCell rest;

StringCell(String first, StringCell rest)
  {
  this.first = first;
  this.rest = rest;
  }
}


/* output of ClosedStringList.main

Treating L as open list
L = (beta gamma delta)
L.first() = beta
L.rest(L) = (gamma delta)
L.rest().first() = gamma
L.rest().rest() = (delta)
nil = ()
L = (beta gamma delta)

Treating S as a stack
S = ()
S = (alpha)
S = (beta alpha)
S = (gamma beta alpha)
S = (delta gamma beta alpha)
S = (delta gamma beta alpha)
S.pop() = delta
S = (gamma beta alpha)
S.pop() = gamma
S = (beta alpha)
S.pop() = beta
S = (alpha)
S.pop() = alpha
S = ()

*/

