commit b8f1f048daff1b016c995c02056eff5c2ee08885
parent 69640b8ad79b8112d5ace2409934d3e8eba71c43
Author: tomas <tomas@logand.com>
Date:   Tue,  6 Oct 2009 21:08:38 +0200
simplified parser and stream io
Diffstat:
| M | wl.java |  |  | 428 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------- | 
1 file changed, 295 insertions(+), 133 deletions(-)
diff --git a/wl.java b/wl.java
@@ -1,10 +1,22 @@
 // wl.java -- (c) 2009 Tomas Hlavaty
 
-import java.util.Stack;
 import java.util.HashMap;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.FileInputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.io.ByteArrayOutputStream;
+import java.io.UnsupportedEncodingException;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
 
 class wl {
 
+    void dbg(String M) {System.out.println(M);}
+    void dbg(String M, Any E) {System.out.println(M); print(E);}
+    void dbg(String M, Any E, Any F) {System.out.println(M); print(E); print(F);}
+
     void err(Object X, String M) {
         if(X instanceof Any) throw new RuntimeException(M + ": " + str((Any) X));
         throw new RuntimeException(M + ": " + X);
@@ -43,21 +55,42 @@ class wl {
     }
 
     // reader
-    final static Any Qte = new Any(null, null, null, null);
     final static Any Lp = new Any(null, null, null, null);
     final static Any Rp = new Any(null, null, null, null);
 
-    String L;
-    int N, I, D;
+    final Any Qte = new Any("quote", NIL, NIL, null);
+    final Any Dot = new Any(".", NIL, NIL, null);
+
+    class In {
+        Character c;
+        InputStream s;
+        public In(InputStream S) {c = null; s = S;}
+    }
+
+    final Any In = new Any("*In", null, null, new In(System.in));
+    final Any Out = new Any("*Out", null, null, System.out);
 
-    void init(String L) {this.L = L; this.N = L.length(); this.I = 0; this.D = 0;}
+    Character peek() {
+        In I = (In) In.cxr;
+        try {
+            if(null == I.c) I.c = (char) I.s.read();
+        } catch(Exception e) {}
+        return I.c;
+    }
+    Character xchar() {
+        peek();
+        In I = (In) In.cxr;
+        Character Z = I.c;
+        I.c = null;
+        return Z;
+    }
 
     boolean charIn(Character C, String L) {return 0 <= L.indexOf(C);}
-    Character peek() {return this.I < this.N ? this.L.charAt(this.I) : null;}
-    Character xchar() {return this.I < this.N ? this.L.charAt(this.I++) : null;}
+
     void skip() {
-        while(this.I < this.N && charIn(this.L.charAt(this.I), " \t\n\r"))
-            this.I++;
+        Character Z;
+        while((Z = peek()) != null && charIn(Z, " \t\n\r"))
+            xchar();
     }
     void comment() {
         while('#' == peek()) {
@@ -114,102 +147,21 @@ class wl {
             case '#': comment(); return null;
             case '"': xchar(); return text();
             case '\'': xchar(); return Qte;
-            case '(': xchar(); this.D++; return Lp;
-            case ')': xchar(); this.D--; return Rp;
+            case '(': xchar(); return Lp;
+            case ')': xchar(); return Rp;
             default: return symbol();
             }
     }
 
-    // parser
-    Any parseBuild(Stack<Any> Os) {
-        Any Z = NIL;
-        Any Dot = null;
-        while(0 < Os.size()) {
-            Any X = Os.pop();
-            if(Lp == X) return Dot != null ? cons(Dot, cons(Z, NIL)) : Z;
-            Dot = null;
-            if(X.isSym() && (".".equals(X.nm))) {
-                if(Z.isCons() && NIL == Z.cdr) {
-                    Z = Z.car;
-                    Dot = X;
-                } else err("Bad dotted pair");
-            } else if(X == Qte)
-                Z = cons(cons(this.Sd.get("quote"), Z.car), Z.cdr);
-            else Z = cons(X, Z);
-        }
-        err("Missing mark");
-        return NIL; // make compiler happy:-{
-    }
-    public Any parse(String L) {
-        init(L);
-        Stack<Any> Os = new Stack<Any>();
-        Os.push(Lp);
-        // TODO circular list .
-        // TODO interpret while reading ~ for each top-level sexp
-        // TODO no wasteful consing
-        while(null != peek()) {
-            Any X = token();
-            if(null != X) {
-                if(Rp == X) Os.push(parseBuild(Os));
-                else Os.push(X);
-            }
-        }
-        Any Z = parseBuild(Os);
-        if(0 < Os.size()) err(Os, "Incomplete input");
-        return Z;
-    }
-
-    // printer
-    public String str(Any L) {
-        StringBuilder A = new StringBuilder();
-        if(L.isSym()) A.append(L.nm);
-        else if(L.isInt()) A.append(L.cxr.toString());
-        else if(L.isCons()) {
-            if(this.Sd.get("quote") == L.car) {
-                A.append('\'');
-                A.append(str(L.cdr));
-            } else {
-                A.append('(');
-                while(L.isCons()) {
-                    A.append(str(L.car));
-                    L = L.cdr;
-                    if(NIL != L) A.append(' ');
-                }
-                if(NIL != L) {
-                    A.append(". ");
-                    A.append(str(L));
-                }
-                A.append(')');
-            }
-        } else if(L.isStr()) {
-            A.append('"');
-            String S = (String) L.cxr;
-            for(int I = 0; I < S.length(); I++) {
-                Character C = S.charAt(I);
-                if('\\' == C) A.append("\\\\");
-                else if('"' == C) A.append("\\\"");
-                else A.append(C);
-            }
-            A.append('"');
-        } else err(L, "Don't know how to print");
-        return A.toString();
-    }
-
-
     // evaluator
     HashMap<String, Any> Sd = new HashMap<String, Any>();
 
     Any intern(String Nm) {
-        if(!this.Sd.containsKey(Nm)) this.Sd.put(Nm, new Any(Nm, NIL, NIL, null));
-        return this.Sd.get(Nm);
+        if(!Sd.containsKey(Nm)) Sd.put(Nm, new Any(Nm, NIL, NIL, null));
+        return Sd.get(Nm);
     }
-    // void def(String Nm, Fn F) {
-    //     if(this.Sd.containsKey(Nm)) this.Sd.get(Nm).cxr = F;
-    //     else this.Sd.put(Nm, new Sym(Nm, null, null, F));
-    // }
 
     Any run(Any E) {
-        System.out.println("run: " + str(E));
         Any Z = NIL;
         while(NIL != E) {
             Z = eval(E.car);
@@ -218,7 +170,7 @@ class wl {
         return Z;
     }
     Any eval(Any E) {
-        System.out.println("eval: " + str(E));
+        //dbg("eval", E);
         Any Z = NIL;
         if(E.isInt()) Z = E;
         else if(E.isSym()) Z = E.cdr;
@@ -229,19 +181,28 @@ class wl {
                 if(X.isFn()) Z = ((Fn) X.cxr).fn(E);
                 else err(E, "TODO ap"); //Z = ap(E);
             } else err(E, "Unexpected function type");
-        } else Z = E; // string
-        //alert("ev: " + str(E) + " => " + Z);
+        } else if(E.isStr()) Z = E;
+        else err(E, "Don't know how to eval");
+        //dbg("eval", E, Z);
         return Z;
     }
 
-    void fn(String Nm, Fn F) {this.Sd.put(Nm, new Any(Nm, null, null, F));}
+    void fn(String Nm, Fn F) {
+        Any Z = Sd.get(Nm);
+        if(null != Z) Z.cxr = F;
+        else Sd.put(Nm, new Any(Nm, null, null, F));
+    }
 
     public wl() {
         Sd.put("NIL", NIL);
         Sd.put("T", T);
-        
-        fn("run", new Fn() {public Any fn(Any E) {return run(E);}});
-        fn("eval", new Fn() {public Any fn(Any E) {return eval(E);}});
+        Sd.put("quote", Qte);
+        Sd.put(".", Dot);
+        Sd.put("*In", In);
+        Sd.put("*Out", Out);
+
+        fn("run", new Fn() {public Any fn(Any E) {return run(E.cdr.car);}});
+        fn("eval", new Fn() {public Any fn(Any E) {return eval(eval(E.cdr.car));}});
         fn("quote", new Fn() {public Any fn(Any E) {return E.cdr;}});
         fn("car", new Fn() {public Any fn(Any E) {return eval(E.cdr.car).car;}});
         fn("cdr", new Fn() {public Any fn(Any E) {return eval(E.cdr.car).cdr;}});
@@ -283,6 +244,18 @@ class wl {
             }
             return Z;
         }});
+        fn("%", new Fn() {public Any fn(Any E) {
+            Any X = E.cdr;
+            Any Z = eval(X.car);
+            if(NIL == Z) return NIL;
+            while(NIL != X.cdr) {
+                X = X.cdr;
+                Any Y = eval(X.car);
+                if(NIL == Y) return NIL;
+                Z.cxr = (Integer) Z.cxr % (Integer) Y.cxr;
+            }
+            return Z;
+        }});
         fn("loop", new Fn() {public Any fn(Any E) {
             // TODO @
             while(true) {
@@ -303,27 +276,240 @@ class wl {
             return eval(X.car) == eval(X.cdr.car) ? T : NIL;
         }});
 
+        fn("peek", new Fn() {public Any fn(Any E) {
+            Character X = peek();
+            return null == X ? NIL : mkStr(X.toString());
+        }});
+        fn("char", new Fn() {public Any fn(Any E) {
+            Character X = xchar();
+            return null == X ? NIL : mkStr(X.toString());
+        }});
+        fn("skip", new Fn() {public Any fn(Any E) {skip(); return NIL;}});
+        fn("read", new Fn() {public Any fn(Any E) {return read();}});
+        fn("print", new Fn() {public Any fn(Any E) {
+            PrintStream S = (PrintStream) Out.cxr;
+            Any Z = NIL;
+            int I = 0;
+            for(Any X = E.cdr; NIL != X; X = X.cdr) {
+                if(0 < I++) S.print(' ');
+                Z = eval(X.car);
+                print(Z);
+            }
+            return Z;
+        }});
+
         fn("in", new Fn() {public Any fn(Any E) {
             Any X = E.cdr;
-            Any N = eval(E.car);
-            String F;
+            Any N = eval(X.car);
+            String F = null;
             if(N.isStr()) F = (String) N.cxr;
             else if(N.isSym()) F = N.nm;
             else err(E, "File name expected");
-            //FileInputStream S = new FileInputStream(N);
-            //InputStreamReader R = new InputStreamReader(S, "UTF-8");
+            In S = null;
+            try {
+                S = new In(new FileInputStream(F));
+                In I = (In) In.cxr;
+                In.cxr = S;
+                Any Z = run(X.cdr);
+                In.cxr = I;
+                S.s.close();
+                return Z;
+            } catch(FileNotFoundException e) {
+                err(E, "File not found");
+            } catch(IOException e) {
+                err(E, "Error closing input");
+            }
+            return NIL; // make the compiler happy:-{
+        }});
+        fn("out", new Fn() {public Any fn(Any E) {
+            Any X = E.cdr;
+            Any N = eval(X.car);
+            String F = null;
+            if(N.isStr()) F = (String) N.cxr;
+            else if(N.isSym()) F = N.nm;
+            else err(E, "File name expected");
+            try {
+                FileOutputStream B = new FileOutputStream(F);
+                PrintStream S = new PrintStream(B);
+                OutputStream O = (OutputStream) Out.cxr;
+                Out.cxr = S;
+                Any Z = run(X.cdr);
+                Out.cxr = O;
+                S.close();
+                return Z;
+            } catch(FileNotFoundException e) {
+                err(E, "File not found");
+            }
+            return NIL; // make the compiler happy:-{
+        }});
+        fn("load", new Fn() {public Any fn(Any E) {
+            Any Z = NIL;
+            for(Any X = E.cdr; NIL != X; X = X.cdr) {
+                Any N = eval(X.car);
+                String F = null;
+                if(N.isStr()) F = (String) N.cxr;
+                else if(N.isSym()) F = N.nm;
+                else err(E, "File name expected");
+                In S = null;
+                try {
+                    S = new In(new FileInputStream(F));
+                    In I = (In) In.cxr;
+                    In.cxr = S;
+                    do Z = eval(read());
+                    while(null != peek());
+                    In.cxr = I;
+                    S.s.close();
+                    return Z;
+                } catch(FileNotFoundException e) {
+                    err(E, "File not found");
+                } catch(IOException e) {
+                    err(E, "Error closing input");
+                }
+            }
+            return Z;
+        }});
+
+        fn("def", new Fn() {public Any fn(Any E) {
+            Any X = E.cdr;
+            Any N = eval(X.car);
+            if(!N.isSym()) err(E, "Symbol expected");
+            if(!Sd.containsKey(N.nm)) err(E, "Symbol not interned");
+            Any V = eval(X.cdr.car);
+            Sd.get(N.nm).cdr = V;
+            return N;
+        }});
+        fn("val", new Fn() {public Any fn(Any E) {
+            Any Z = NIL;
+            Any X = eval(E.cdr.car);
+            if(X.isSym()) Z = X.cdr;
+            else if(X.isStr()) Z = X;
+            else if(X.isCons()) Z = X.car;
+            else err(E, "Expected sym|str|cell");
+            return Z;
+        }});
+        fn("cons", new Fn() {public Any fn(Any E) {
+            Any X = E.cdr;
+            return cons(eval(X.car), eval(X.cdr.car));
+        }});
+
+        fn("gc", new Fn() {public Any fn(Any E) {
+            Runtime r = Runtime.getRuntime();
+            r.gc();
             return NIL;
         }});
-        //FileOutputStream fos = new FileOutputStream("test.txt");
-        //OutputStreamWriter out = new OutputStreamWriter(fos, "UTF-8"); 
+        fn("heap", new Fn() {public Any fn(Any E) {
+            Runtime R = Runtime.getRuntime();
+            long A = R.freeMemory();
+            long B = R.totalMemory();
+            long C = R.maxMemory();
+            long Z = B - A;
+            if(NIL != E.cdr) {
+                if(T == eval(E.cdr.car)) Z = A;
+                else Z = C;
+            }
+            return mkInt("" + Z / (1024 * 1024));
+        }});
     }
 
+    void repl() {
+        PrintStream S = (PrintStream) Out.cxr;
+        do {
+            prompt();
+            Any Z = eval(read());
+            // TODO only on terminal
+            S.print("-> ");
+            print(Z);
+            S.println();
+        } while(null != peek());
+    }
+    void prompt() {
+        // TODO only on terminal
+        PrintStream S = (PrintStream) Out.cxr;
+        S.print(": ");
+    }
+    Any read() {
+        // TODO handle \n from user
+        return read1();
+    }
+    Any read1() {
+        Any Z = token();
+        if(Qte == Z) Z = cons(Qte, read1());
+        else if(Lp == Z) Z = readL();
+        return Z;
+    }
+    Any readL() {
+        // TODO circular list .
+        // TODO cons .
+        Any Z = NIL;
+        Any X;
+        while(null != (X = read1()) && Rp != X)
+            Z = cons(X, Z);
+        return flip(Z);
+    }
+    void print(Any E) {
+        PrintStream S = (PrintStream) Out.cxr;
+        if(E.isSym()) S.print(E.nm);
+        else if(E.isInt()) S.print(E.cxr);
+        else if(E.isCons()) {
+            if(Qte == E.car) {
+                S.print('\'');
+                print(E.cdr);
+            } else {
+                S.print('(');
+                while(E.isCons()) {
+                    print(E.car);
+                    E = E.cdr;
+                    if(NIL != E) S.print(' ');
+                }
+                if(NIL != E) {
+                    S.print(". ");
+                    print(E);
+                }
+                S.print(')');
+            }
+        } else if(E.isStr()) {
+            S.print('"');
+            String X = (String) E.cxr;
+            for(int I = 0; I < X.length(); I++) {
+                Character C = X.charAt(I);
+                if('\\' == C) S.print("\\\\");
+                else if('"' == C) S.print("\\\"");
+                else S.print(C);
+            }
+            S.print('"');
+        } else err(E, "Don't know how to print");
+    }
 
+    String str(Any E) {
+        ByteArrayOutputStream B = new ByteArrayOutputStream();
+        PrintStream S = new PrintStream(B);
+        PrintStream O = (PrintStream) Out.cxr;
+        Out.cxr = S;
+        print(E);
+        Out.cxr = O;
+        try {
+            String Z = B.toString("UTF-8");
+            S.close();
+            return Z;
+        } catch(UnsupportedEncodingException e) {
+            err(E, "Unsupported encoding");
+        }
+        return null; // make the compiler happy:-{
+    }
+    Any flip(Any E) {
+        Any Z = NIL;
+        while(E.isCons()) {
+            Any X = E;
+            E = E.cdr;
+            X.cdr = Z;
+            Z = X;
+        }
+        return Z;
+    }
 
     public static void main(String args[]) {
         wl X = new wl();
-        System.out.println(X.str(X.parse("(- 1 2 3)")));
-        System.out.println(X.str(X.run(X.parse("(- 1 2 3)(- 3 5)(/ "))));
+        X.repl();
     }
 }
 
@@ -343,29 +529,5 @@ class wl {
 //         } else throw "Function expected";
 //     }
 
-//     //def("apply", ap);
-
-//     def("val", function(E) {
-//             var Z;
-//             var X = ev(E.cdr.car);
-//             if(typeof X == "number") throw "Variable expected";
-//             else if(isSym(X)) Z = X.cdr;
-//             else if(isCons(X)) Z = X.car;
-//             else Z = X; // string
-//             return Z;
-//         });
-//     def(".cons", function(E) {return cons(ev(E.cdr.car), ev(E.cdr.cdr.car));});
-
-//     // TODO def !!!!!!!!!!!!!
-
-//     // OK
-
-// //     Sd["%"] = function(E) {var X = Os.pop(); Os.push(Os.pop() % X);};
 // //     Sd["eq"] = function() {var Y = Os.pop(); var X = Os.pop(); Os.push(X == Y);};
 // //     Sd["lt"] = function() {var Y = Os.pop(); var X = Os.pop(); Os.push(X < Y);};
-
-// // function eq(X, Y) {
-// //     if(X === Y) return T;
-// //     //if(X == Y) return T;
-// //     return NIL;
-// // }