// %GROUP Sexp


// Lisp Interpreter in Java, G J Chaitin, 27 Dec 99

// To compile the interpreter, type 
//    javac lisp.java


import java.applet.*;
import java.awt.*;
import java.awt.event.*;

import java.util.Stack;
import java.math.BigInteger;

public class lisp extends Applet implements ActionListener {

TextArea  mexp, dspl;
Button    evl_button, clr_button;
Checkbox  echo_chkbx;

    private long 
    infinity = 999999999999999999L;
    private String buffer = null;
    private int pos;

    private Sexp 
    obj_lst = null,
    nil = mk_atom(""), // create impossible atom for empty list () 
    nil2 = mk_atom("nil"), // alternate name for empty list ()
    true2 = mk_atom("true"), // create true
    false2 = mk_atom("false"), // create false
    one = new Sexp(BigInteger.valueOf(1)), 
    zero = new Sexp(BigInteger.valueOf(0)), 
    quote = mk_atom("'"), 
    dbl_quote = mk_atom("\""), 
    if_then_else = mk_atom("if"), 
    lambda = mk_atom("lambda"),
    rparen = mk_atom(")"), // create right paren
    lparen = mk_atom("("), // create left paren
    time_err = mk_atom("impossible atom 1"), // create impossible atom for error return
    data_err = mk_atom("impossible atom 2"), // create impossible atom for error return
    out_of_time = mk_atom("out-of-time"),
    out_of_data = mk_atom("out-of-data"), 
    let = mk_atom("let"), 
    car = mk_atom("car"), 
    cdr = mk_atom("cdr"), 
    cadr = mk_atom("cadr"), 
    caddr = mk_atom("caddr"), 
    atom = mk_atom("atom"), 
    cons = mk_atom("cons"), 
    equal = mk_atom("="), 
    fappend = mk_atom("append"), 
    feval = mk_atom("eval"), 
    ftry = mk_atom("try"), 
    debug = mk_atom("debug"), 
    size = mk_atom("size"), 
    length = mk_atom("length"), 
    display = mk_atom("display"), 
    read_bit = mk_atom("read-bit"), 
    read_exp = mk_atom("read-exp"), 
    was_read = mk_atom("was-read"), // New! Undocumented!
    run_utm_on = mk_atom("run-utm-on"), // New! Undocumented!
    bits = mk_atom("bits"), 
    plus = mk_atom("+"), 
    times = mk_atom("*"), 
    minus = mk_atom("-"), 
    to_the_power = mk_atom("^"), 
    leq = mk_atom("<="), 
    geq = mk_atom(">="), 
    lt = mk_atom("<"), 
    gt = mk_atom(">"), 
    success = mk_atom("success"), 
    failure = mk_atom("failure"), 
    no_time_limit = mk_atom("no-time-limit"), 
    base10_to_2 = mk_atom("base10-to-2"), 
    base2_to_10 = mk_atom("base2-to-10"), 
    define = mk_atom("define"); 

    private Stack binary_data_stk   = new Stack();
    private Sexp  binary_data_lst   = nil;
    private Stack was_read_stk      = new Stack();
    private Sexp  was_read_lst      = nil;
    private Stack was_displayed_stk = new Stack();
    private Sexp  was_displayed_lst = nil;

    public void init() {

       evl_button = new Button("Run");
       evl_button.setBackground(Color.white); 
       clr_button = new Button("Clear");
       clr_button.setBackground(Color.white); 
       echo_chkbx = new Checkbox("Echo");
       echo_chkbx.setBackground(Color.white); 
       mexp = new TextArea("Enter M-expressions here, hit Run", 10, 80);
       dspl = new TextArea("All output is here", 10, 80);
       setFont(new Font("Monospaced", Font.PLAIN, 12));
       add(evl_button);
       add(clr_button);
       add(mexp);
       add(echo_chkbx);
       add(dspl);
       evl_button.addActionListener(this);
       clr_button.addActionListener(this);

         time_err.err = true; // create impossible atom for error return
         data_err.err = true; // create impossible atom for error return

         // bind nil to () 
         nil2.vstk.pop();
         nil2.vstk.push(nil);

   } // end init

   public void actionPerformed(ActionEvent evt) {
          Button source = (Button)evt.getSource();
          if (source.getLabel().equals("Clear")) {
             mexp.setText(""); 
             return;
          }
          evl_button.setBackground(Color.red); 
          dspl.setText("");
          run();
          evl_button.setBackground(Color.white); 
   } // end actionPerformed


   private Sexp jn(Sexp x, Sexp y) {
         if (y.at && y != nil) return x;
         return new Sexp(x,y);
   }

   private Sexp mk_atom(String x) {
         Sexp o = obj_lst;
         while (o != null) {
            if (o.hd.pname.equals(x)) return o.hd;
            o = o.tl;
         }
         Sexp z = new Sexp(x);
         obj_lst = new Sexp(z,obj_lst);
         return z;
   }

// concatenate two lists 
private Sexp append(Sexp x, Sexp y) { 
   if (x.at) return y;
   if (y.at) return x;
   x = reverse(x);
   while (!x.at) {
      y = jn(x.hd,y);
      x = x.tl;
   }
   return y;
}

// evaluate list of expressions
private Sexp evalst(Sexp x, long d) { 
   if (x.at) return nil;
   Sexp v = eval(x.hd,d);
   if (v.err) return v; // propagate error back up
   Sexp w = evalst(x.tl,d);
   if (w.err) return w; // propagate error back up
   return jn(v,w);
}

// push fresh bindings
private void push_env() {
   Sexp o = obj_lst;
   while (o != null) {
      o.hd.vstk.push(o.hd); // bind atom to self
      o = o.tl;
   }
   // bind nil to () 
   nil2.vstk.pop();
   nil2.vstk.push(nil);
}

// restore old bindings
private void pop_env() {
   Sexp o = obj_lst;
   while (o != null) {
      o.hd.vstk.pop();
      if (o.hd.vstk.empty()) 
      o.hd.vstk.push(o.hd); // bind atom to self
      o = o.tl;
   }
}

// evaluate expression e with assoc list a & depth limit d
private Sexp eval(Sexp e, long d) { 
   if (e.at) return (Sexp) e.vstk.peek(); // look up binding
   Sexp f = eval(e.hd,d); // evaluate the function
   if (f.err) return f; // propagate error back up
   if (f == quote) return e.two(); // quote
   if (f == if_then_else) { // if then else
      Sexp p = eval(e.two(),d); // eval predicate
      if (p.err) return p; // propagate error back up
      if (p == false2) return eval(e.four(),d);
      return eval(e.three(),d); // anything not false considered true
   }
   // evaluate the arguments
   Sexp args = evalst(e.tl,d);
   if (args.err) return args; // propagate error back up
   Sexp x = args.hd, y = args.two(); // pick up first and second arg
   Sexp z = args.three(); // pick up third arg
   Sexp v;

   if (f == debug) {
         out("debug",x.toS());
         return x;
   } // end of debug

   if (f == size) return new Sexp(BigInteger.valueOf(x.toS().length()));

   if (f == length) return new Sexp(BigInteger.valueOf(count(x)));

   if (f == display) {
         if (was_displayed_stk.empty()) 
            out("display",x.toS());
         else 
            was_displayed_lst = jn(x,was_displayed_lst);
         return x;
   } // end of display

   if (f == read_bit) return get_bit();

   if (f == read_exp) {
         // read to \n from binary data
         if (new_line2()) return data_err; // out of data ?
         v = get_exp("()"); // only parens are delimiters, no comments
         if (v == rparen) v = nil; // make sure it's a well-formed formula
         return v;
   } // end of read-exp

   if (f == was_read) return reverse(was_read_lst);

   if (f == bits) return to_bits(x);

   if (f == atom) if (x.at) return true2; else return false2;

   if (f == car)  return x.hd;

   if (f == cdr)  return x.tl;

   if (f == cons) return jn(x,y);

   if (f == equal) if (eq(x,y)) return true2; else return false2; 

   if (f == fappend) return append(x,y);

   if (f == plus) return new Sexp(x.nval.add(y.nval));

   if (f == minus) return new Sexp(BigInteger.valueOf(0).max(x.nval.subtract(y.nval)));

   if (f == times) return new Sexp(x.nval.multiply(y.nval));

   if (f == leq) if (x.nval.compareTo(y.nval) != 1) return true2; else return false2; 

   if (f == lt) if (x.nval.compareTo(y.nval) == -1) return true2; else return false2; 

   if (f == geq) if (x.nval.compareTo(y.nval) != -1) return true2; else return false2; 

   if (f == gt) if (x.nval.compareTo(y.nval) == 1) return true2; else return false2; 

   if (f == to_the_power) return new Sexp(x.nval.pow(y.nval.intValue())); //wrong if y very large!

   if (f == base10_to_2) return to_base2(x.nval);

   if (f == base2_to_10) return new Sexp(to_base10(x));

   if (d == 0) return time_err;  // out of time error
   d = d - 1L; // decrement depth

   if (f == feval) { 
         push_env(); 
         v = eval(x,d); 
         pop_env(); 
         return v;
   } // end of eval

   if (f == ftry) { 
         // try new-depth-limit exp binary-data
         binary_data_stk.push(binary_data_lst);
         binary_data_lst = z;
         was_read_stk.push(was_read_lst);
         was_read_lst = nil;
         was_displayed_stk.push(was_displayed_lst);
         was_displayed_lst = nil;
         // xx is new depth limit 
         long xx = x.nval.longValue();
         if (x.nval.compareTo(BigInteger.valueOf(infinity)) == 1) xx = infinity;
         if (x == no_time_limit) xx = infinity;
         push_env();
         if (xx < d) // new depth limit tougher
            v = eval(y,xx);
         else       // old depth limit wins
            v = eval(y,d);
         pop_env();
         Sexp displayed = reverse(was_displayed_lst);
         binary_data_lst = (Sexp) binary_data_stk.pop();
         was_read_lst = (Sexp) was_read_stk.pop();
         was_displayed_lst = (Sexp) was_displayed_stk.pop();
         if (v == data_err) return 
            jn(failure,jn(out_of_data,jn(displayed,nil))); // out of data stops here
         if (v != time_err) return 
            jn(success,jn(v,jn(displayed,nil))); // no error
         // out of time
         if (xx < d) return // new depth limit tougher
            jn(failure,jn(out_of_time,jn(displayed,nil))); // do not propagate error back up
         else       // old depth limit wins
            return time_err; // propagate error back up to prev try
   } // end of try

   // otherwise must be function definition
         Sexp vars = f.two(), body = f.three();
         //  bind
         bind(vars,args);
         v = eval(body,d);
         // unbind
         unbind(vars);
         return v;

} // end of eval

private void bind(Sexp vars, Sexp args) {
   if (vars.at) return;
   bind(vars.tl, args.tl);
   if (vars.hd.at && !vars.hd.nmb)
   vars.hd.vstk.push(args.hd);
}

private void unbind(Sexp vars) {
   if (vars.at) return;
   if (vars.hd.at && !vars.hd.nmb)
   vars.hd.vstk.pop();
   unbind(vars.tl);
}

private long count(Sexp x) {
   long k = 0;
   while (!x.at) {
      k = k + 1L;
      x = x.tl;
   }
   return k;
}
 
private Sexp reverse(Sexp list) {
   Sexp v = nil;
   while (!list.at) {
      v = jn(list.hd,v);
      list = list.tl;
   }
   return v;
}

private boolean eq(Sexp x, Sexp y) {
   if (x.nmb && y.nmb) return x.nval.equals(y.nval);
   if (x.nmb || y.nmb) return false;
   if (x.at && y.at) return x == y;
   if (x.at || y.at) return false;
   return eq(x.hd,y.hd) && eq(x.tl,y.tl);
}

private Sexp get_lst() { // get list of s-exps from m-exp
   Sexp v = get();
   if (v == rparen) return nil;
   Sexp w = get_lst();
   return jn(v,w);
}

private String next_token2(String delimiters) { // skip comments
   while (true) {
      String t = next_token(delimiters); // get next token 
      if (delimiters.indexOf('[') == -1) return t; // no comments
      if (!t.equals("[")) return t; 
      // skip comment
      while (true) {
         t = next_token2(delimiters);
         if (t.equals("]")) break;  
         // keep this from running past the end!
         if (pos == buffer.length() && t.equals(")")) return t; 
      }
   }
} // end next_token2

private Sexp get() { // get single s-exp from m-exp
   // get next token; parens, brackets, quotes are delimiters
   String t = next_token2("()[]\'\""); // comments allowed 
   Sexp a = null;
   if (!nval(t)) 
     a = mk_atom(t); // make token into atom
   else 
     a = new Sexp(new BigInteger(t)); // make number

   if (a == lparen) return get_lst(); // explicit list

   // primitive functions with no arguments
   if (a == read_bit ||
       a == read_exp ||                   
       a == was_read) return jn(a,nil);

   // primitive function with one argument
   if (a == dbl_quote) // S-exp contained in M-exp !
       return get_exp("()[]\'\""); 
       // parens, brackets, quotes are delimiters; comments allowed

   // primitive functions with one argument
   if (a == quote ||
       a == atom ||
       a == car ||
       a == cdr ||
       a == display ||
       a == debug ||
       a == size ||
       a == length ||
       a == base10_to_2 ||
       a == base2_to_10 ||
       a == feval ||
       a == bits) return jn(a,jn(get(),nil));

   // primitive functions with two arguments
   if (a == cons ||
       a == equal ||
       a == plus ||
       a == minus ||
       a == times ||
       a == to_the_power ||
       a == leq ||
       a == geq ||
       a == lt ||
       a == gt ||
       a == define ||
       a == fappend ||
       a == lambda) return jn(a,jn(get(),jn(get(),nil)));

   // primitive functions with three arguments
   if (a == if_then_else ||
       a == ftry) return jn(a,jn(get(),jn(get(),jn(get(),nil))));

   if (a == run_utm_on) { // cadr try no-time-limit 'eval read-exp
      Sexp v = get();
      v = jn(ftry,
          jn(no_time_limit,
          jn(jn(quote,
             jn(jn(feval,
                jn(jn(read_exp,nil),
                   nil)),
                nil)),
          jn(v,
             nil))));
      v = jn(cdr,jn(v,nil));
      v = jn(car,jn(v,nil));
      return v;
   }

   if (a == cadr) { // car of cdr
      Sexp v = get();
      v = jn(cdr,jn(v,nil));
      v = jn(car,jn(v,nil));
      return v;
   }

   if (a == caddr) { // car of cdr of cdr
      Sexp v = get();
      v = jn(cdr,jn(v,nil));
      v = jn(cdr,jn(v,nil));
      v = jn(car,jn(v,nil));
      return v;
   }

   if (a == let) { // let x be v in e
         Sexp x = get(), v = get(), e = get();
         if (!x.at) {
            v = jn(quote,
                jn(jn(lambda,
                   jn(x.tl,
                   jn(v,nil))), nil)); 
            x = x.hd;
         }
         // let (fxyz) be v in e
         return
         jn(jn(quote,
            jn(jn(lambda,
               jn(jn(x,nil),
               jn(e,nil))), nil)),
         jn(v,nil));
   } // end of let

   return a;
 
} // end get mexp

private boolean nval(String s) {
   int i = 0;
   while (i < s.length()) {
      char d = s.charAt(i);
      if (d < '0' || d > '9') return false;
      i = i + 1;
   }
   return true;
}

private BigInteger to_base10(Sexp s) {
   BigInteger n = BigInteger.valueOf(0);
   while (!s.at) {
      n = n.shiftLeft(1);
      if (!s.hd.pname.equals("0")) n = n.setBit(0);
      s = s.tl;
   }
   return n;
}

private Sexp to_base2(BigInteger n) {
   Sexp s = nil;
   while (!n.equals(BigInteger.valueOf(0))) {
      if (n.testBit(0)) 
         s = jn(one,s);
      else 
         s = jn(zero,s);
      n = n.shiftRight(1);
   }
   return s;
}

private void new_line() { // read M-exp & add \n
   buffer = mexp.getText().concat("\n");
   pos = 0;
}

private boolean new_line2() { // read to \n from binary data
   StringBuffer str = new StringBuffer();
   int i;
   char ch;
   while (true) {
      i = get_char();
      if (i == -1) return true; // data exhausted
      ch = (char) i;
      str.append(ch);
      if (ch == '\n') break;
   }
   buffer = str.toString();
   pos = 0;
   return false; // data not exhausted
}

private String next_token(String delimiters) { // token from line buffer
   StringBuffer token = new StringBuffer();
   while (true) { // get characters in token
      char ch;
      if (pos == buffer.length()) ch = ')'; // supply unlimited!
      else ch = buffer.charAt(pos++);
      echo.append(ch); 
      // keep only \n or printable ascii codes
      if (ch != 10 && (ch < 32 || ch >= 127)) continue;
      boolean is_delimiter = (delimiters.indexOf(ch) != -1);
      boolean is_white_space = (ch == ' ' || ch == '\n');
      boolean is_white_space_or_delimiter = (is_white_space || is_delimiter);
      if (token.length() == 0) { // token buffer empty
         if (is_white_space) continue;
         token.append(ch);
         if (is_delimiter) break;
      } // end token buffer empty
      else { // token buffer not empty 
         if (!is_white_space_or_delimiter) token.append(ch);
         if (is_delimiter) {
            pos = pos - 1; // so we rescan it again next time
            echo.setLength(echo.length()-1);
         } 
         if (is_white_space_or_delimiter) break;
      } // end token buffer not empty
   } // end get characters in token
   return token.toString();
} // end of next_token

StringBuffer echo; // used to accumulate one M-exp

private void run() { // run M-expressions

  new_line();

  int mexp_count = 0;
  while (true) { // loop thru M-exps

  echo = new StringBuffer();
  Sexp s = get();

  if (s == rparen && pos == buffer.length()) return; // ran off end
  if (mexp_count++ > 0) dspl.append("\n");
 
  String xxx = echo.toString();
  while (xxx.endsWith("\n")) xxx = xxx.substring(0,xxx.length()-1);
  while (xxx.startsWith("\n")) xxx = xxx.substring(1);
  if (echo_chkbx.getState()) dspl.append(xxx+"\n\n"); 

  if (s.bad()) {
     out("expression",s.toS());
     out("value","syntax error!");
     continue;
  } // end of bad syntax

  if (s.hd == define) {
    // define x to have value v
    // if x is (fxyz) defines f to have value &(xyz)v
    Sexp x = s.two();
    Sexp v = s.three();
    if (!x.at) {v = jn(lambda,jn(x.tl,jn(v,nil))); x = x.hd;}
    out("define",x.toS());
    out("value",v.toS());
    if (x.at && !x.nmb) {
       x.vstk.pop();
       x.vstk.push(v);
     }
     continue;
  } // end of definition

  // evaluate expression s
  out("expression",s.toS());
  String save_buffer; int save_pos; // evaling read-exp clobbers buffer, pos
  save_buffer = buffer; save_pos = pos;
  Sexp v = eval(s,infinity); // "infinite" depth limit
  buffer = save_buffer; pos = save_pos;
  if (v == data_err)  
     out("value","out of data!");
  else
     out("value",v.toS());

  } // run each mexp

} // end of run
    
private Sexp get_bit() { // read bit from binary data
   if (binary_data_lst.at) return data_err; // out of data
   Sexp v = binary_data_lst.hd; 
   binary_data_lst = binary_data_lst.tl;
   // anything not zero considered one
   if (!v.pname.equals("0")) v = one;
   was_read_lst = jn(v,was_read_lst);
   return v;
}

private Sexp to_bits(Sexp x) { // convert S-exp to bit string
    String str = x.toS().concat("\n");
    Sexp v = nil;
    int i = str.length();
    while (i > 0) {
       i = i - 1;
       int j = ((int) str.charAt(i)) % 256;
       int k = 0;
       while (k < 8) {
          if ((j % 2) != 0) v = jn(one,v);
          else              v = jn(zero,v);
          j = j >>> 1;
          k = k + 1;
       }
    }
    return v;
}

private int get_char() { // read character from binary data
  int k, v;
  k = 0; v = 0;
  while (k < 8) {
     Sexp b = get_bit();
     if (b.err) return -1; // out of data
     v = v << 1;
     if (b == one) v = v + 1;
     k = k + 1;
  }
  return v;   
}

private Sexp get_list(String delimiters) { // get list of s-exps from binary data or m-exp
   Sexp v = get_exp(delimiters);
   if (v == rparen) return nil;
   Sexp w = get_list(delimiters);
   return jn(v,w);
}

private Sexp get_exp(String delimiters) { // get single s-exp from binary data or m-exp
   String t = next_token2(delimiters);
   Sexp a = null;
   if (!nval(t)) 
      a = mk_atom(t); // make token into atom
   else
      a = new Sexp(new BigInteger(t)); // make number
   if (a == lparen) return get_list(delimiters); // explicit list
   return a;
} // end get sexp

private void out(String xx, String yy) {
  String x = new String(xx);
  String y = new String(yy);
  while (y.length() > 0) {
       String left, right;
       left = (x + "            ").substring(0,12);
       if (y.length() <= 50) {right = y; y = "";} 
                       else  {right = y.substring(0,50); y = y.substring(50);}
       dspl.append(left + right + "\n");
       x = "";
  }
} // end output routine

} // end lisp applet
