[next-life
strick**20120306085012
 Ignore-this: bfedd10d944a46c76ad9b600a0ef20ad
] hunk ./terse-a1/res/raw/prelude.txt 124
+      '|link|/RESET|[RESET]|'.
+      '|link|/Home|[Home]|'.
hunk ./terse-a1/res/raw/prelude.txt 128
+      '|link|/DrawLife|[DrawLife]|'.
hunk ./terse-a1/res/raw/prelude.txt 140
+          '|link|/Home|Home|', 'Go Home.'.
+          '|link|/Reset|Reset|', 'Reset Interpreter.'.
hunk ./terse-a1/res/raw/prelude.txt 144
+          '|link|/DrawLife|[DrawLife]|', 'Conway''s Life'.
hunk ./terse-a1/res/raw/prelude.txt 296
-class DrawRect App
-meth DrawRect handle:query:
-  v = Vec new.
-  10 do: [:x
-    10 do: [:y
-      v ap: ('rect', 80 ti: x, 70 ti: y, 20, 15, 2, 'green').
-      v ap: ('text', 80 ti: x --pl: 25, 70 ti: y --pl: 30, JAM[x str. ','. y str] ).
-      ]].
-  DICT[ 'type', 'draw'.
-	'title', 'Drawing 100 Rectangles'.
-	'value', v.
-	'width', 300.
-	'height', 500.
-  ]
-
-
hunk ./terse-a1/res/raw/prelude.txt 313
+  ].
+
+class DrawLife App
+meth DrawLife canX
+  900
+meth DrawLife canY
+  500
+meth DrawLife numX
+  27
+meth DrawLife numY
+  15
+meth DrawLife delX
+  self canX idiv: self numX
+meth DrawLife delY
+  self canY idiv: self numY
+meth DrawLife initP
+  self numX iota collect: [ :x
+    self numY iota collect: [ :y
+      t = x pl// y ti/ 13 .
+      OR[t imod: 4 eq:: 0.
+         t imod: 5 eq:: 0.
+         t imod: 7 eq:: 0.].
+    ]
+  ] -- say.
+
+meth DrawLife draw
+  Jam[ 'Instance id='. self usrId str. ' hash='. self hash str. ] say: p.
+  p ifNil: [ p = self initP ].
+  tDebug = self numX iota collect: [ :x
+        self numY iota collect: [ :y
+           nei = 0.
+           3 do: [ :i. i = i mi: 1 pl:: x.
+             3 do: [ :j. j = j mi: 1 pl:: y.
+               nei = nei pl/ (p at: i at:: j).
+             ].
+           ].
+           nei.
+        ].
+      ].
+  tDebug say.
+  nextP = self numX iota collect: [ :x
+        self numY iota collect: [ :y
+           nei = 0.
+           3 do: [ :i. i = i mi: 1 pl:: x.
+             3 do: [ :j. j = j mi: 1 pl:: y.
+               nei = nei pl/ (p at: i at:: j).
+             ].
+           ].
+           p at: x at:: y --
+               n: [ nei eq: 3 ]
+               y: [ OR[nei eq: 3. nei eq: 4.] ].
+        ].
+      ].
+  p = nextP.
+  v = Vec new.
+  self numX do: [ :x
+    self numY do: [ :y
+      p at: x at:: y --
+        y: [
+             xx = x ti: self delX pl:: self delX idiv: 2.
+             yy = y ti: self delY pl:: self delY idiv: 2.
+             v ap: Vec[ 'rect'.  xx mi/ 6.  yy mi/ 6. 12. 12.].
+        ]
+        n: [
+             xx = x ti: self delX pl:: self delX idiv: 2.
+             yy = y ti: self delY pl:: self delY idiv: 2.
+             v ap: Vec[ 'line'.  xx mi/ 2.  yy mi/ 2.
+                                 xx pl/ 2.  yy pl/ 2.].
+        ].
+    ].
+  ].
+  v.
+
+meth DrawLife handle:query:
+  DICT[
+    'type', 'draw'.
+    'width', self canX.
+    'height', self canY.
+    'value', self draw.
+    'url', JAM[ '/'. self cls name. '.'. self usrId str. ].
+  ].
+
+class DrawRect App
+meth DrawRect handle:query:
+  v = Vec new.
+  10 do: [:x
+    10 do: [:y
+      v ap: ('rect', 80 ti: x, 70 ti: y, 20, 15, 2, 'green').
+      v ap: ('text', 80 ti: x --pl: 25, 70 ti: y --pl: 30, JAM[x str. ','. y str] ).
+      ]].
+  DICT[ 'type', 'draw'.
+	'title', 'Drawing 100 Rectangles'.
+	'value', v.
+	'width', 300.
+	'height', 500.
hunk ./terse-a1/res/raw/prelude.txt 409
+
+
hunk ./terse-a1/src/terse/a1/MainActivity.java 22
+
hunk ./terse-a1/src/terse/a1/TerseActivity.java 20
+import terse.vm.Static;
hunk ./terse-a1/src/terse/a1/TerseActivity.java 30
+import android.graphics.Bitmap;
hunk ./terse-a1/src/terse/a1/TerseActivity.java 67
+	HashMap<String, String> query;
hunk ./terse-a1/src/terse/a1/TerseActivity.java 84
-			try {
-				this.terp = new AndroidTerp(true, "aaa", "aaa.txt");
-			} catch (IOException e) {
-				// TODO Auto-generated catch block
-				e.printStackTrace();
-				terp_error = e.toString();
-			}
+			resetTerp();
+		}
+	}
+	void resetTerp() {
+		try {
+			this.terp = new AndroidTerp(true, "aaa", "aaa.txt");
+		} catch (IOException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+			terp_error = e.toString();
hunk ./terse-a1/src/terse/a1/TerseActivity.java 152
+		if (path.toLowerCase().equals("/reset")) {
+			resetTerp();
+			path = "/Home";
+		}
hunk ./terse-a1/src/terse/a1/TerseActivity.java 162
-		HashMap<String, String> query = new HashMap<String, String>();
+		query = new HashMap<String, String>();
hunk ./terse-a1/src/terse/a1/TerseActivity.java 304
-							String f1 = stringAt(d, "field1"); 
+							String f1 = stringAt(d, "field1");
hunk ./terse-a1/src/terse/a1/TerseActivity.java 306
-							String f2 = stringAt(d, "field2"); 
+							String f2 = stringAt(d, "field2");
hunk ./terse-a1/src/terse/a1/TerseActivity.java 308
-							f1 = (f1==null) ? "f1null" : f1;
-							v1 = (v1==null) ? "v1null" : v1;
-							f2 = (f2==null) ? "f2null" : f2;
-							v2 = (v2==null) ? "v2null" : v2;
+							f1 = (f1 == null) ? "f1null" : f1;
+							v1 = (v1 == null) ? "v1null" : v1;
+							f2 = (f2 == null) ? "f2null" : f2;
+							v2 = (v2 == null) ? "v2null" : v2;
hunk ./terse-a1/src/terse/a1/TerseActivity.java 313
-							startTerseActivity(action, query, 
-//									"field1", stringAt(d, "field1"), 
-//									"value1", stringAt(d, "value1"), 
-//									"field2", stringAt(d, "field2"), 
-//									"value2", stringAt(d, "value2"),
-									stringAt(d, "field1"), stringAt(d, "value1"),
-									stringAt(d, "field2"), stringAt(d, "value2"),
-									"text", text);
+							startTerseActivity(
+									action,
+									query,
+									// "field1", stringAt(d, "field1"),
+									// "value1", stringAt(d, "value1"),
+									// "field2", stringAt(d, "field2"),
+									// "value2", stringAt(d, "value2"),
+									stringAt(d, "field1"),
+									stringAt(d, "value1"),
+									stringAt(d, "field2"),
+									stringAt(d, "value2"), "text", text);
hunk ./terse-a1/src/terse/a1/TerseActivity.java 339
-					DrawView dv = new DrawView(this, v.vec);
+					DrawView dv = new DrawView(this, v.vec, d);
hunk ./terse-a1/src/terse/a1/TerseActivity.java 384
+		Dict dict;
hunk ./terse-a1/src/terse/a1/TerseActivity.java 386
-		public DrawView(Context context, ArrayList<Pro> list) {
+		public DrawView(Context context, ArrayList<Pro> list, Dict dict) {
hunk ./terse-a1/src/terse/a1/TerseActivity.java 389
+			this.dict = dict;
hunk ./terse-a1/src/terse/a1/TerseActivity.java 440
-						startTerseActivity(path, queryStr, "action", "up",
+						// If they gave us an url, use it.
+						String nextPath = stringAt(dict, "url");
+						// Otherwise, use the same path.
+						nextPath = nextPath == null ? path : nextPath;
+						startTerseActivity(nextPath, queryStr, "action", "up",
hunk ./terse-a1/src/terse/a1/TerseActivity.java 484
-		if (!(p instanceof Dict))
-			return null; // "?p?";
-		Dict d = (Dict) p;
-		Pro x = d.dict.get(d.terp().newStr(i));
-		if (!(x instanceof Str))
-			return null; // "?x?";
-		return ((Str) x).str;
+		return Static.stringAt(p, i);
hunk ./terse-a1/src/terse/a1/TerseActivity.java 488
-		if (!(p instanceof Vec))
-			return null; // "?p?";
-		Vec v = (Vec) p;
-		int sz = v.vec.size();
-		if (sz <= i)
-			return null; // "?sz?";
-		Pro x = v.vec.get(i);
-		if (!(x instanceof Str))
-			return null; // "?x?";
-		return ((Str) x).str;
+		return Static.stringAt(p, i);
hunk ./terse-a1/src/terse/a1/TerseActivity.java 492
-		if (!(p instanceof Vec))
-			return -1;
-		Vec v = (Vec) p;
-		int sz = v.vec.size();
-		if (sz <= i)
-			return -1;
-		Pro x = v.vec.get(i);
-		if (!(x instanceof Num))
-			return -1;
-		return (float) ((Num) x).num;
+		return Static.floatAt(p, i);
hunk ./terse-vm/src/terse/vm/Cls.java 24
+	
+	// Quick hack. TODO.
+	HashMap<Integer, Pro> insts; 
hunk ./terse-vm/src/terse/vm/Cls.java 38
+		this.insts = new HashMap<Integer, Pro>();
hunk ./terse-vm/src/terse/vm/Cls.java 175
+		terp.tCls.addMethod(new JavaMeth(terp.tCls, "findInst:", null,
+				"Return the name of the class.") {
+			public Pro apply(Frame f, Pro r, Pro[] args) {
+				Cls self = r.asCls();
+				Num num = args[0].asNum();
+				Pro z = num == null? terp.instNil : self.insts.get(num.num);
+				return z == null ? terp.instNil : z;
+			}
+		});
hunk ./terse-vm/src/terse/vm/Expr.java 213
+		// Single slot Call-Site cache.
+		Cls cacheCls = null;
+		int cacheGeneration = 0;
+		Meth cacheMeth = null;
+
hunk ./terse-vm/src/terse/vm/Expr.java 279
-				m = findMeth(r, msg, true);
-			}
-			if (m == null) {
-				toss("MessageNotUnderstood: message <%s> to instance of class <%s>: <%s>",
-						msg, r.cls.name, r);
+				// Use call-site cache.
+				if (r.cls == cacheCls && r.cls.generation == cacheGeneration) {
+					m = cacheMeth;
+				} else {
+					m = findMeth(r, msg, true);
+
+					if (m == null) {
+						toss("MessageNotUnderstood: message <%s> to instance of class <%s>: <%s>",
+								msg, r.cls.name, r);
+					}
+					// Set call-site cache.
+					cacheCls = r.cls;
+					cacheGeneration = cacheCls.generation;
+					cacheMeth = m;
+				}
hunk ./terse-vm/src/terse/vm/Expr.java 296
-				say("Sending message <%s> to <%s#%s> with <%s>", m.name, r.cls.name,
-						r.toString(), arrayToString(args));
+				say("Sending message <%s> to <%s#%s> with <%s>", m.name,
+						r.cls.name, r.toString(), arrayToString(args));
hunk ./terse-vm/src/terse/vm/Expr.java 303
-				String what = fmt("\n  * During SEND <%s %s> TO <%s#%s> WITH %s",
-						r.cls.name, m.name, r.cls.name, r.toString(), arrayToString(a));
+				String what = fmt(
+						"\n  * During SEND <%s %s> TO <%s#%s> WITH %s",
+						r.cls.name, m.name, r.cls.name, r.toString(),
+						arrayToString(a));
hunk ./terse-vm/src/terse/vm/Expr.java 310
-				say("Message <%s> to <%s#%s> returns <%s#%s>", m.name, r.cls.name,
-						r.toString(), z.cls.name, z.toString());
+				say("Message <%s> to <%s#%s> returns <%s#%s>", m.name,
+						r.cls.name, r.toString(), z.cls.name, z.toString());
hunk ./terse-vm/src/terse/vm/Parser.java 320
+		// NOTICE this is using "allVars" rather than JustAtThisClass vars.
hunk ./terse-vm/src/terse/vm/Static.java 3
-import java.io.BufferedReader;
-import java.io.FileOutputStream;
-import java.io.FileReader;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.io.PrintStream;
-import java.util.ArrayList;
+
hunk ./terse-vm/src/terse/vm/Static.java 5
-import java.util.HashMap;
hunk ./terse-vm/src/terse/vm/Static.java 7
-import terse.vm.Pro.Blk;
-import terse.vm.Pro.Buf;
hunk ./terse-vm/src/terse/vm/Static.java 8
-import terse.vm.Pro.Undefined;
hunk ./terse-vm/src/terse/vm/Static.java 9
-import terse.vm.Pro.Obj;
hunk ./terse-vm/src/terse/vm/Static.java 10
-import terse.vm.Pro.Sys;
hunk ./terse-vm/src/terse/vm/Static.java 11
-import terse.vm.Usr.Tmp;
hunk ./terse-vm/src/terse/vm/Static.java 189
-	public static Pattern htmlTagP = Pattern.compile("[A-Za-z][-A-Za-z0-9_.:]*");
+	public static Pattern htmlTagP = Pattern
+			.compile("[A-Za-z][-A-Za-z0-9_.:]*");
+
+	public static String stringAt(Pro p, String i) {
+		return stringAt(p, i, null);
+	}
+
+	public static String stringAt(Pro p, String i, String dflt) {
+		if (!(p instanceof Dict))
+			return dflt;
+		Dict d = (Dict) p;
+		Pro x = d.dict.get(d.terp().newStr(i));
+		if (!(x instanceof Str))
+			return dflt;
+		return ((Str) x).str;
+	}
+	public static Pro proAt(Pro p, String i) {
+		if (!(p instanceof Dict))
+			return p.terp().instNil;
+		Dict d = (Dict) p;
+		return d.dict.get(d.terp().newStr(i));
+	}
+
+	public static String stringAt(Pro p, int i) {
+		return stringAt(p, i, null);
+	}
+	public static String stringAt(Pro p, int i, String dflt) {
+		if (!(p instanceof Vec))
+			return dflt;
+		Vec v = (Vec) p;
+		int sz = v.vec.size();
+		if (sz <= i)
+			return dflt;
+		Pro x = v.vec.get(i);
+		if (!(x instanceof Str))
+			return dflt;
+		return ((Str) x).str;
+	}
+
+	public static float floatAt(Pro p, int i) {
+		if (!(p instanceof Vec))
+			return -1;
+		Vec v = (Vec) p;
+		int sz = v.vec.size();
+		if (sz <= i)
+			return -1;
+		Pro x = v.vec.get(i);
+		if (!(x instanceof Num))
+			return -1;
+		return (float) ((Num) x).num;
+	}
hunk ./terse-vm/src/terse/vm/Terp.java 135
+
+		this.tUsr.myVars = strs("p", "q");  // TODO: temp hack.
+		this.tUsr.recalculateAllVarsHereAndBelow();
+		
hunk ./terse-vm/src/terse/vm/Terp.java 316
-			String qDictRepr = qDict.repr();
+			String qDictRepr = qDict.repr();  // Inefficient. TODO.
hunk ./terse-vm/src/terse/vm/Terp.java 320
-			if (Send.understands(cls, "handle:query:")) {
+			Obj inst = null;
+			try {
+				id = Integer.parseInt(word[1]);
+				inst = (Obj) cls.insts.get(id);
+			} catch (Exception _) {
+				// pass.
+			}
+			if (inst != null) {
+				result_pro = inst.eval(fmt("self handle: (%s) query: (%s)", urlRepr,
+						qDictRepr));
+			} else if (Send.understands(cls, "handle:query:")) {
hunk ./terse-vm/src/terse/vm/Terp.java 333
-				result_pro = cls.eval(fmt(" handle: (%s) query: (%s)", urlRepr,
+				result_pro = cls.eval(fmt("self handle: (%s) query: (%s)", urlRepr,
hunk ./terse-vm/src/terse/vm/Terp.java 338
+				// TODO: LRU & mention() conflict with Cls.insts map.
hunk ./terse-vm/src/terse/vm/Terp.java 442
+				} else if (command.equals("vars")) {
+						String className = words[1];
+						String methodName = words[2];
+
+						// TODO:
+//						Cls cls = clss.get(className.toLowerCase());
+//						if (cls == null) {
+//							toss("loadInitFile: Global <%s> not a class, for meth <%s> :: %s",
+//									className, methodName);
+//						}
+//						cls.eval(fmt(
+//								"self definemethod: %s abbrev: %s doc: %s code: %s",
+//								newStr(methodName).repr(), newStr("").repr(),
+//								newStr("").repr(), newStr(more).repr()));
hunk ./terse-vm/src/terse/vm/Terp.java 599
-
hunk ./terse-vm/src/terse/vm/Usr.java 13
-	private static int next_id = Math.abs((int) System.currentTimeMillis()) % 999999999 + 101;
-	private int usrId = 0;  // Transient id, while in memory.
-	public String usrName = null;  // For saved, persistent objects.
+	private static int next_id = Math.abs((int) System.currentTimeMillis()) % 1499999999 + 101;
+	private int usrId = 0; // Transient id, while in memory.
+	public String usrName = null; // For saved, persistent objects.
hunk ./terse-vm/src/terse/vm/Usr.java 20
+
hunk ./terse-vm/src/terse/vm/Usr.java 29
+
hunk ./terse-vm/src/terse/vm/Usr.java 37
+
hunk ./terse-vm/src/terse/vm/Usr.java 41
+
hunk ./terse-vm/src/terse/vm/Usr.java 45
-	
+
hunk ./terse-vm/src/terse/vm/Usr.java 49
+
hunk ./terse-vm/src/terse/vm/Usr.java 54
-	static void addBuiltinMethodsForUsr(Terp terp) {
+	static void addBuiltinMethodsForUsr(final Terp terp) {
hunk ./terse-vm/src/terse/vm/Usr.java 56
-				.addMethod(new JavaMeth(terp.tUsrCls, "new", "",
-						"Make a new instance.  (This message is inherited to any subclass of Usr.)") {
+				.addMethod(new JavaMeth(
+						terp.tUsrCls,
+						"new",
+						"",
+						"Make a new instance.  "
+								+ "(This message is inherited to any subclass of Usr.)") {
hunk ./terse-vm/src/terse/vm/Usr.java 63
-						return new Usr(r.asCls());
+						Usr z = new Usr(r.asCls());
+						z.cls.insts.put(z.usrId(), z); // Hack -- save all
+														// insts.
+						return z;
hunk ./terse-vm/src/terse/vm/Usr.java 69
+		terp.tUsr.addMethod(new JavaMeth(terp.tUsr, "usrId", "",
+				"Number of this Usr instance.") {
+			public Pro apply(Frame f, Pro r, Pro[] args) {
+				Usr u = (Usr) r;
+				return terp.newNum(u.usrId());
+			}
+		});
hunk ./terse-vm/src/terse/vm/Usr.java 77
-	
+
hunk ./terse-vm/src/terse/vm/Usr.java 79
-	    Tmp(Terp t) {
-	        super(t.tTmp);
-	    }
+		Tmp(Terp t) {
+			super(t.tTmp);
+		}
hunk ./terse-vm/src/terse/vm/Usr.java 85
-		Usr[] memory;  // High index is front; 0 is back.
+		Usr[] memory; // High index is front; 0 is back.
hunk ./terse-vm/src/terse/vm/Usr.java 87
+
hunk ./terse-vm/src/terse/vm/Usr.java 92
+
hunk ./terse-vm/src/terse/vm/Usr.java 98
-						memory[j] = memory[j+1];
+						memory[j] = memory[j + 1];
hunk ./terse-vm/src/terse/vm/Usr.java 100
-					memory[size-1] = u;
+					memory[size - 1] = u;
hunk ./terse-vm/src/terse/vm/Usr.java 106
-				memory[i] = memory[i+1];
+				memory[i] = memory[i + 1];
hunk ./terse-vm/src/terse/vm/Usr.java 108
-			memory[size-1] = u;
+			memory[size - 1] = u;
hunk ./terse-vm/src/terse/vm/Usr.java 111
+
hunk ./terse-vm/src/terse/vm/Usr.java 117
-			return null;  // Not found.
+			return null; // Not found.
hunk ./terse-web/prelude.txt 124
+      '|link|/RESET|[RESET]|'.
+      '|link|/Home|[Home]|'.
hunk ./terse-web/prelude.txt 128
+      '|link|/DrawLife|[DrawLife]|'.
hunk ./terse-web/prelude.txt 140
+          '|link|/Home|Home|', 'Go Home.'.
+          '|link|/Reset|Reset|', 'Reset Interpreter.'.
hunk ./terse-web/prelude.txt 144
+          '|link|/DrawLife|[DrawLife]|', 'Conway''s Life'.
hunk ./terse-web/prelude.txt 296
-class DrawRect App
-meth DrawRect handle:query:
-  v = Vec new.
-  10 do: [:x
-    10 do: [:y
-      v ap: ('rect', 80 ti: x, 70 ti: y, 20, 15, 2, 'green').
-      v ap: ('text', 80 ti: x --pl: 25, 70 ti: y --pl: 30, JAM[x str. ','. y str] ).
-      ]].
-  DICT[ 'type', 'draw'.
-	'title', 'Drawing 100 Rectangles'.
-	'value', v.
-	'width', 300.
-	'height', 500.
-  ]
-
-
hunk ./terse-web/prelude.txt 313
+  ].
+
+class DrawLife App
+meth DrawLife canX
+  900
+meth DrawLife canY
+  500
+meth DrawLife numX
+  27
+meth DrawLife numY
+  15
+meth DrawLife delX
+  self canX idiv: self numX
+meth DrawLife delY
+  self canY idiv: self numY
+meth DrawLife initP
+  self numX iota collect: [ :x
+    self numY iota collect: [ :y
+      t = x pl// y ti/ 13 .
+      OR[t imod: 4 eq:: 0.
+         t imod: 5 eq:: 0.
+         t imod: 7 eq:: 0.].
+    ]
+  ] -- say.
+
+meth DrawLife draw
+  Jam[ 'Instance id='. self usrId str. ' hash='. self hash str. ] say: p.
+  p ifNil: [ p = self initP ].
+  tDebug = self numX iota collect: [ :x
+        self numY iota collect: [ :y
+           nei = 0.
+           3 do: [ :i. i = i mi: 1 pl:: x.
+             3 do: [ :j. j = j mi: 1 pl:: y.
+               nei = nei pl/ (p at: i at:: j).
+             ].
+           ].
+           nei.
+        ].
+      ].
+  tDebug say.
+  nextP = self numX iota collect: [ :x
+        self numY iota collect: [ :y
+           nei = 0.
+           3 do: [ :i. i = i mi: 1 pl:: x.
+             3 do: [ :j. j = j mi: 1 pl:: y.
+               nei = nei pl/ (p at: i at:: j).
+             ].
+           ].
+           p at: x at:: y --
+               n: [ nei eq: 3 ]
+               y: [ OR[nei eq: 3. nei eq: 4.] ].
+        ].
+      ].
+  p = nextP.
+  v = Vec new.
+  self numX do: [ :x
+    self numY do: [ :y
+      p at: x at:: y --
+        y: [
+             xx = x ti: self delX pl:: self delX idiv: 2.
+             yy = y ti: self delY pl:: self delY idiv: 2.
+             v ap: Vec[ 'rect'.  xx mi/ 6.  yy mi/ 6. 12. 12.].
+        ]
+        n: [
+             xx = x ti: self delX pl:: self delX idiv: 2.
+             yy = y ti: self delY pl:: self delY idiv: 2.
+             v ap: Vec[ 'line'.  xx mi/ 2.  yy mi/ 2.
+                                 xx pl/ 2.  yy pl/ 2.].
+        ].
+    ].
+  ].
+  v.
+
+meth DrawLife handle:query:
+  DICT[
+    'type', 'draw'.
+    'width', self canX.
+    'height', self canY.
+    'value', self draw.
+    'url', JAM[ '/'. self cls name. '.'. self usrId str. ].
+  ].
+
+class DrawRect App
+meth DrawRect handle:query:
+  v = Vec new.
+  10 do: [:x
+    10 do: [:y
+      v ap: ('rect', 80 ti: x, 70 ti: y, 20, 15, 2, 'green').
+      v ap: ('text', 80 ti: x --pl: 25, 70 ti: y --pl: 30, JAM[x str. ','. y str] ).
+      ]].
+  DICT[ 'type', 'draw'.
+	'title', 'Drawing 100 Rectangles'.
+	'value', v.
+	'width', 300.
+	'height', 500.
hunk ./terse-web/prelude.txt 409
+
+
hunk ./terse-web/src/terse/web/WebServer.java 36
-	Str TYPE;
-	Str LIST;
-	Str VALUE;
-	Str ACTION;
-	Str TITLE;
-	Str TEXT;
-	Str EDIT;
-	Str DRAW;
-	Str UPPER;
-	Str LOWER;
-	Str FIELD1;
-	Str FIELD2;
-	Str VALUE1;
-	Str VALUE2;
hunk ./terse-web/src/terse/web/WebServer.java 41
-	
+
hunk ./terse-web/src/terse/web/WebServer.java 58
-		this.TYPE = t.newStr("type");
-		this.LIST = t.newStr("list");
-		this.VALUE = t.newStr("value");
-		this.ACTION = t.newStr("action");
-		this.TITLE = t.newStr("title");
-		this.TEXT = t.newStr("text");
-		this.EDIT = t.newStr("edit");
-		this.DRAW = t.newStr("draw");
-		this.UPPER = t.newStr("upper");
-		this.LOWER = t.newStr("lower");
-		this.FIELD1 = t.newStr("field1");
-		this.FIELD2 = t.newStr("field2");
-		this.VALUE1 = t.newStr("value1");
-		this.VALUE2 = t.newStr("value2");
-		this.TABLE_PARAMS = new String[] { "cellpadding", "8", "border", "1" };
+		this.TABLE_PARAMS = strs("cellpadding", "8", "border", "1");
hunk ./terse-web/src/terse/web/WebServer.java 90
+		public Ht appendLink(String link, String label) {
+			Ht.tag(this, "a", strs("href", link), label);
+			return this;
+		}
+
hunk ./terse-web/src/terse/web/WebServer.java 159
-			Ht.tag(ht, "span", new String[] { "style", "color=#555555" }, ""
+			Ht.tag(ht, "span", strs("style", "color=#555555"), ""
hunk ./terse-web/src/terse/web/WebServer.java 164
-			Ht.tag(ht, "span", new String[] { "style",
-					"color: #338033; font-family: courier" }, a.toString());
+			Ht.tag(ht, "span", strs("style",
+					"color: #338033; font-family: courier"), a.toString());
hunk ./terse-web/src/terse/web/WebServer.java 227
-			String[] params = new String[] { "href", link };
+			String[] params = strs("href", link);
hunk ./terse-web/src/terse/web/WebServer.java 241
+	String maybeString(String s, String defaultString) {
+
+		return s == null ? defaultString == null ? "nil" : defaultString : s;
+	}
+
hunk ./terse-web/src/terse/web/WebServer.java 263
-			String text = maybeString(dict.dict.get(VALUE),
+			String text = stringAt(dict, "value",
hunk ./terse-web/src/terse/web/WebServer.java 265
-			String action = maybeString(dict.dict.get(ACTION),
+			String action = stringAt(dict, "action",
hunk ./terse-web/src/terse/web/WebServer.java 271
-			String[] textareaParams = new String[] { "name", "text", "wrap",
+			String[] textareaParams = strs( "name", "text", "wrap",
hunk ./terse-web/src/terse/web/WebServer.java 273
-					"width; 95%" };
-			if (dict.dict.containsKey(FIELD1)) {
-				String fname = maybeString(dict.dict.get(FIELD1), "?");
-				String fvalue = maybeString(dict.dict.get(VALUE1), "?");
+					"width; 95%");
+			if (stringAt(dict, "field1") != null) {
+				String fname = stringAt(dict, "field1", "?");
+				String fvalue = stringAt(dict, "value1", "?");
hunk ./terse-web/src/terse/web/WebServer.java 279
-				Ht.tag(form, "input", new String[] { "name", fname, "value",
-						fvalue }, "");
+				Ht.tag(form, "input", strs( "name", fname, "value",
+						fvalue), "");
hunk ./terse-web/src/terse/web/WebServer.java 283
-			if (dict.dict.containsKey(FIELD2)) {
-				String fname = maybeString(dict.dict.get(FIELD2), "?");
-				String fvalue = maybeString(dict.dict.get(VALUE2), "?");
+			if (stringAt(dict, "field2") != null) {
+				String fname = stringAt(dict, "field2", "?");
+				String fvalue = stringAt(dict, "value2", "?");
hunk ./terse-web/src/terse/web/WebServer.java 288
-				Ht.tag(form, "input", new String[] { "name", fname, "value",
-						fvalue }, "");
+				Ht.tag(form, "input", strs( "name", fname, "value",
+						fvalue), "");
hunk ./terse-web/src/terse/web/WebServer.java 294
-			Ht.tag(form, "input", new String[] { "type", "submit", "value",
-					"Submit" }, "");
-			Ht.tag(form, "input", new String[] { "type", "reset" }, "");
+			Ht.tag(form, "input", strs( "type", "submit", "value",
+					"Submit"), "");
+			Ht.tag(form, "input", strs( "type", "reset"), "");
hunk ./terse-web/src/terse/web/WebServer.java 298
-			Ht.tag(page, "form", new String[] { "method", "POST", "action",
-					addSingleLetterQueryPartsToLink(action, query) }, form);
+			Ht.tag(page, "form", strs( "method", "POST", "action",
+					addSingleLetterQueryPartsToLink(action, query)), form);
hunk ./terse-web/src/terse/web/WebServer.java 310
-			Vec v = dict.dict.get(VALUE).asVec();
+			Vec v = proAt(dict, "value").asVec();
hunk ./terse-web/src/terse/web/WebServer.java 369
-			String text = maybeString(dict.dict.get(VALUE),
+			String text = stringAt(dict, "value",
hunk ./terse-web/src/terse/web/WebServer.java 384
-			Vec list = (Vec) dict.dict.get(VALUE);
+			Vec list = (Vec) proAt(dict, "value");
hunk ./terse-web/src/terse/web/WebServer.java 414
-						maybeString(dict.dict.get(TITLE), "Untitled"));
+						stringAt(dict, "title", "Untitled"));
hunk ./terse-web/src/terse/web/WebServer.java 418
-						maybeString(dict.dict.get(TITLE), "Untitled"));
+						stringAt(dict, "title", "Untitled"));
hunk ./terse-web/src/terse/web/WebServer.java 426
+				String nextLink = stringAt(dict, "url");
+				if (nextLink != null) {
+					footer.appendLink(nextLink, "NEXT");
+				}
hunk ./terse-web/src/terse/web/WebServer.java 514
-					Str type = dict.dict.get(TYPE).asStr();
+					String type = stringAt(dict, "type");
hunk ./terse-web/src/terse/web/WebServer.java 518
-					} else if (type.equals(LIST)) {
+					} else if (type.equals("list")) {
hunk ./terse-web/src/terse/web/WebServer.java 520
-					} else if (type.equals(TEXT)) {
+					} else if (type.equals("text")) {
hunk ./terse-web/src/terse/web/WebServer.java 522
-					} else if (type.equals(EDIT)) {
+					} else if (type.equals("edit")) {
hunk ./terse-web/src/terse/web/WebServer.java 524
-					} else if (type.equals(DRAW)) {
+					} else if (type.equals("draw")) {
hunk ./terse-web/src/terse/web/WebServer.java 527
-						r = new BaseRenderer(dict, query); // Crude fallback if
-						// unknown
-						// type.
+						r = new BaseRenderer(dict, query); // Crude fallback.
hunk ./terse-web/src/terse/web/WebServer.java 600
-				String msg = super.say(s, objects);  // to stderr.
+				String msg = super.say(s, objects); // to stderr.