diff lib/js/urweb.js @ 1260:25ebd8c4fafb

Thunking recursive JavaScripted function ASTs, to reduce page load time dramatically
author Adam Chlipala <adamc@hcoop.net>
date Sat, 22 May 2010 14:09:06 -0400
parents 83b1853d1e58
children 003df929ee08
line wrap: on
line diff
--- a/lib/js/urweb.js	Tue May 18 14:47:56 2010 -0400
+++ b/lib/js/urweb.js	Sat May 22 14:09:06 2010 -0400
@@ -1,10 +1,10 @@
 // Detect browser quirks that we should be aware of.
 
 function needsDynPrefix() {
-  var span = document.createElement("span");
-  span.innerHTML = "<script>alert('test');</script>";
-  var scripts = span.getElementsByTagName("script");
-  return scripts.length == 0;
+    var span = document.createElement("span");
+    span.innerHTML = "<script>alert('test');</script>";
+    var scripts = span.getElementsByTagName("script");
+    return scripts.length == 0;
 }
 
 var dynPrefix = needsDynPrefix() ? "<span style=\"display:none\">A</span>" : "";
@@ -40,196 +40,196 @@
 // Lists
 
 function cons(v, ls) {
-  return { next : ls, data : v };
+    return { next : ls, data : v };
 }
 function rev(ls) {
-  var acc = null;
-  for (; ls; ls = ls.next)
-    acc = cons(ls.data, acc);
-  return acc;
+    var acc = null;
+    for (; ls; ls = ls.next)
+        acc = cons(ls.data, acc);
+    return acc;
 }
 function concat(ls1, ls2) {
-  var acc = ls2;
-  ls1 = rev(ls1);
-  for (; ls1; ls1 = ls1.next)
-    acc = cons(ls1.data, acc);
-  return acc;
+    var acc = ls2;
+    ls1 = rev(ls1);
+    for (; ls1; ls1 = ls1.next)
+        acc = cons(ls1.data, acc);
+    return acc;
 }
 function member(x, ls) {
-  for (; ls; ls = ls.next)
-    if (ls.data == x)
-      return true;
-  return false;
+    for (; ls; ls = ls.next)
+        if (ls.data == x)
+            return true;
+    return false;
 }
 function remove(x, ls) {
-  var acc = null;
+    var acc = null;
 
-  for (; ls; ls = ls.next)
-    if (ls.data == x)
-      return concat(acc, ls.next);
-    else
-      acc = cons(ls.data, acc);
+    for (; ls; ls = ls.next)
+        if (ls.data == x)
+            return concat(acc, ls.next);
+        else
+            acc = cons(ls.data, acc);
 
-  return ls;
+    return ls;
 }
 function union(ls1, ls2) {
-  var acc = ls2;
+    var acc = ls2;
 
-  for (; ls1; ls1 = ls1.next)
-    if (!member(ls1.data, ls2))
-      acc = cons(ls1.data, acc);
+    for (; ls1; ls1 = ls1.next)
+        if (!member(ls1.data, ls2))
+            acc = cons(ls1.data, acc);
 
-  return acc;
+    return acc;
 }
 function length(ls) {
-  var acc = 0;
+    var acc = 0;
 
-  for (; ls; ls = ls.next)
-    ++acc;
+    for (; ls; ls = ls.next)
+        ++acc;
 
-  return acc;
+    return acc;
 }
 
 
 // Error handling
 
 function whine(msg) {
-  alert(msg);
-  throw msg;
+    alert(msg);
+    throw msg;
 }
 
 function pf(loc) {
-  throw ("Pattern match failure (" + loc + ")");
+    throw ("Pattern match failure (" + loc + ")");
 }
 
 function runHandlers(kind, ls, arg) {
-  if (ls == null)
-    alert(kind + ": " + arg);
-  for (; ls; ls = ls.next)
-    try {
-      exec({c:"a", f:{c:"a", f:ls.data, x:{c:"c", v:arg}}, x:{c:"c", v:null}});
-    } catch (v) { }
+    if (ls == null)
+        alert(kind + ": " + arg);
+    for (; ls; ls = ls.next)
+        try {
+            exec({c:"a", f:{c:"a", f:ls.data, x:{c:"c", v:arg}}, x:{c:"c", v:null}});
+        } catch (v) { }
 }
 
 var errorHandlers = null;
 
 function onError(f) {
-  errorHandlers = cons(f, errorHandlers);
+    errorHandlers = cons(f, errorHandlers);
 }
 
 function er(s) {
-  runHandlers("Error", errorHandlers, s);
-  throw {uw_error: s};
+    runHandlers("Error", errorHandlers, s);
+    throw {uw_error: s};
 }
 
 var failHandlers = null;
 
 function onFail(f) {
-  failHandlers = cons(f, failHandlers);
+    failHandlers = cons(f, failHandlers);
 }
 
 function doExn(v) {
-  if (v == null || v.uw_error == null) {
-    var s = (v == null ? "null" : v.message ? v.message : v.toString());
-    if (v != null && v.fileName && v.lineNumber)
-      s += " (" + v.fileName + ":" + v.lineNumber + ")";
-    runHandlers("Fail", failHandlers, s);
-  }
+    if (v == null || v.uw_error == null) {
+        var s = (v == null ? "null" : v.message ? v.message : v.toString());
+        if (v != null && v.fileName && v.lineNumber)
+            s += " (" + v.fileName + ":" + v.lineNumber + ")";
+        runHandlers("Fail", failHandlers, s);
+    }
 }
 
 var disconnectHandlers = null;
 
 function flift(f) {
-  return {env:cons(f,null), body:{c:"v", n:1}};
+    return {env:cons(f,null), body:{c:"v", n:1}};
 }
 
 function onDisconnect(f) {
-  disconnectHandlers = cons(flift(f), disconnectHandlers);
+    disconnectHandlers = cons(flift(f), disconnectHandlers);
 }
 
 function discon() {
-  runHandlers("Disconnect", disconnectHandlers, null);
+    runHandlers("Disconnect", disconnectHandlers, null);
 }
 
 var connectHandlers = null;
 
 function onConnectFail(f) {
-  connectHandlers = cons(flift(f), connectHandlers);
+    connectHandlers = cons(flift(f), connectHandlers);
 }
 
 function conn() {
-  runHandlers("Connect", connectHandlers, null);
+    runHandlers("Connect", connectHandlers, null);
 }
 
 var serverHandlers = null;
 
 function onServerError(f) {
-  serverHandlers = cons(f, serverHandlers);
+    serverHandlers = cons(f, serverHandlers);
 }
 
 function servErr(s) {
-  window.setTimeout(function () { runHandlers("Server", serverHandlers, s); }, 0);
+    window.setTimeout(function () { runHandlers("Server", serverHandlers, s); }, 0);
 }
 
 
 // Embedding closures in XML strings
 
 function cs(f) {
-  return {closure: f};
+    return {closure: f};
 }
 
 function isWeird(v) {
-  return v.closure != null || v.cat1 != null;
+    return v.closure != null || v.cat1 != null;
 }
 
 function cat(s1, s2) {
-  if (isWeird(s1) || isWeird(s2))
-    return {cat1: s1, cat2: s2};
-  else
-    return s1 + s2;
+    if (isWeird(s1) || isWeird(s2))
+        return {cat1: s1, cat2: s2};
+    else
+        return s1 + s2;
 }
 
 var closures = [];
 var freeClosures = null;
 
 function newClosure(f) {
-  var n;
-  if (freeClosures == null) {
-    n = closures.length;
-  } else {
-    n = freeClosures.data;
-    freeClosures = freeClosures.next;
-  }
-  closures[n] = f;
-  return n;
+    var n;
+    if (freeClosures == null) {
+        n = closures.length;
+    } else {
+        n = freeClosures.data;
+        freeClosures = freeClosures.next;
+    }
+    closures[n] = f;
+    return n;
 }
 
 function freeClosure(n) {
-  closures[n] = null;
-  freeClosures = cons(n, freeClosures);
+    closures[n] = null;
+    freeClosures = cons(n, freeClosures);
 }
 
 function cr(n) {
-  return closures[n];
+    return closures[n];
 }
 
 function flatten(cls, tr) {
-  if (tr.cat1 != null)
-    return flatten(cls, tr.cat1) + flatten(cls, tr.cat2);
-  else if (tr.closure != null) {
-    var cl = newClosure(tr.closure);
-    cls.v = cons(cl, cls.v);
-    return "cr(" + cl + ")";
-  } else
-    return tr;
+    if (tr.cat1 != null)
+        return flatten(cls, tr.cat1) + flatten(cls, tr.cat2);
+    else if (tr.closure != null) {
+        var cl = newClosure(tr.closure);
+        cls.v = cons(cl, cls.v);
+        return "cr(" + cl + ")";
+    } else
+        return tr;
 }
 
 function flattenLocal(s) {
-  var cls = {v : null};
-  var r = flatten(cls, s);
-  for (cl = cls.v; cl != null; cl = cl.next)
-    freeClosure(cl.data);
-  return r;
+    var cls = {v : null};
+    var r = flatten(cls, s);
+    for (cl = cls.v; cl != null; cl = cl.next)
+        freeClosure(cl.data);
+    return r;
 }
 
 
@@ -237,292 +237,292 @@
 // Dynamic tree management
 
 function populate(node) {
-  var s = node.signal;
-  var oldSources = node.sources;
-  try {
-    var sr = execF(s, null);
-    var newSources = sr._sources;
+    var s = node.signal;
+    var oldSources = node.sources;
+    try {
+        var sr = execF(s, null);
+        var newSources = sr._sources;
 
-    for (var sp = oldSources; sp; sp = sp.next)
-      if (!member(sp.data, newSources))
-        sp.data.dyns = remove(node, sp.data.dyns);
+        for (var sp = oldSources; sp; sp = sp.next)
+            if (!member(sp.data, newSources))
+                sp.data.dyns = remove(node, sp.data.dyns);
 
-    for (var sp = newSources; sp; sp = sp.next)
-      if (!member(sp.data, oldSources))
-        sp.data.dyns = cons(node, sp.data.dyns);
+        for (var sp = newSources; sp; sp = sp.next)
+            if (!member(sp.data, oldSources))
+                sp.data.dyns = cons(node, sp.data.dyns);
 
-    node.sources = newSources;
-    node.recreate(sr._data);
-  } catch (v) {
-    doExn(v);
-  }
+        node.sources = newSources;
+        node.recreate(sr._data);
+    } catch (v) {
+        doExn(v);
+    }
 }
 
 function sc(v) {
-  return {data : v, dyns : null};
+    return {data : v, dyns : null};
 }
 function sv(s, v) {
-  s.data = v;
+    s.data = v;
 
-  for (var ls = s.dyns; ls; ls = ls.next)
-    if (!ls.dead)
-      populate(ls.data);
+    for (var ls = s.dyns; ls; ls = ls.next)
+        if (!ls.dead)
+            populate(ls.data);
 }
 function sg(s) {
-  return s.data;
+    return s.data;
 }
 
 function ss(s) {
-  return {env:cons(s, null), body:{c:"r", l:
-    cons({n:"sources", v:{c:"c", v:cons(s, null)}},
-      cons({n:"data", v:{c:"f", f:sg, a:cons({c:"v", n:1}, null)}}, null))}};
+    return {env:cons(s, null), body:{c:"r", l:
+            cons({n:"sources", v:{c:"c", v:cons(s, null)}},
+                 cons({n:"data", v:{c:"f", f:sg, a:cons({c:"v", n:1}, null)}}, null))}};
 }
 function sr(v) {
-  return {env:null, body:{c:"c", v:{_sources : null, _data : v}}};
+    return {env:null, body:{c:"c", v:{_sources : null, _data : v}}};
 }
 function sb(x,y) {
-  return {env:cons(y,cons(x,null)),
-    body:{c:"=",
-      e1:{c:"a", f:{c:"v", n:2}, x:{c:"c", v:null}},
-      e2:{c:"=",
-        e1:{c:"a",
-          f:{c:"a", f:{c:"v", n:2}, x:{c:".", r:{c:"v", n:0}, f:"data"}},
-          x:{c:"c", v:null}},
-        e2:{c:"r", l:cons(
-          {n:"sources", v:{c:"f", f:union, a:cons({c:".", r:{c:"v", n:1}, f:"sources"},
-            cons({c:".", r:{c:"v", n:0}, f:"sources"}, null))}},
-          cons({n:"data", v:{c:".", r:{c:"v", n:0}, f:"data"}}, null))}}}};
+    return {env:cons(y,cons(x,null)),
+            body:{c:"=",
+                e1:{c:"a", f:{c:"v", n:2}, x:{c:"c", v:null}},
+                e2:{c:"=",
+                    e1:{c:"a",
+                        f:{c:"a", f:{c:"v", n:2}, x:{c:".", r:{c:"v", n:0}, f:"data"}},
+                        x:{c:"c", v:null}},
+                    e2:{c:"r", l:cons(
+                                      {n:"sources", v:{c:"f", f:union, a:cons({c:".", r:{c:"v", n:1}, f:"sources"},
+                                                                              cons({c:".", r:{c:"v", n:0}, f:"sources"}, null))}},
+                                      cons({n:"data", v:{c:".", r:{c:"v", n:0}, f:"data"}}, null))}}}};
 }
 function scur(s) {
-  return execF(s, null)._data;
+    return execF(s, null)._data;
 }
 
 function lastParent() {
-  var pos = document.body;
+    var pos = document.body;
 
-  while (pos.lastChild && pos.lastChild.nodeType == 1)
-    pos = pos.lastChild;
+    while (pos.lastChild && pos.lastChild.nodeType == 1)
+        pos = pos.lastChild;
 
-  pos = pos.parentNode;
+    pos = pos.parentNode;
 
-  return pos;
+    return pos;
 }
 
 function parent() {
-  return thisScript ? thisScript.parentNode : lastParent();
+    return thisScript ? thisScript.parentNode : lastParent();
 }
 
 function addNode(node) {
-  if (thisScript)
-    thisScript.parentNode.replaceChild(node, thisScript);
-  else
-    lastParent().appendChild(node);
+    if (thisScript)
+        thisScript.parentNode.replaceChild(node, thisScript);
+    else
+        lastParent().appendChild(node);
 }
 
 var thisScript = null;
 
 function runScripts(node) {
-  if (node.getElementsByTagName) {
-    var savedScript = thisScript;
+    if (node.getElementsByTagName) {
+        var savedScript = thisScript;
 
-    var scripts = node.getElementsByTagName("script"), scriptsCopy = [];
-    var len = scripts.length;
-    for (var i = 0; i < len; ++i)
-      scriptsCopy[i] = scripts[i];
-    for (var i = 0; i < len; ++i) {
-      thisScript = scriptsCopy[i];
+        var scripts = node.getElementsByTagName("script"), scriptsCopy = [];
+        var len = scripts.length;
+        for (var i = 0; i < len; ++i)
+            scriptsCopy[i] = scripts[i];
+        for (var i = 0; i < len; ++i) {
+            thisScript = scriptsCopy[i];
 
-      try {
-        eval(thisScript.text);
-      } catch (v) {
-        doExn(v);
-      }
-      if (thisScript.parentNode)
-        thisScript.parentNode.removeChild(thisScript);
+            try {
+                eval(thisScript.text);
+            } catch (v) {
+                doExn(v);
+            }
+            if (thisScript.parentNode)
+                thisScript.parentNode.removeChild(thisScript);
+        }
+
+        thisScript = savedScript;
     }
-
-    thisScript = savedScript;
-  }
 }
 
 
 // Dynamic tree entry points
 
 function killScript(scr) {
-  scr.dead = true;
-  for (var ls = scr.sources; ls; ls = ls.next)
-    ls.data.dyns = remove(scr, ls.data.dyns);
-  for (var ls = scr.closures; ls; ls = ls.next)
-    freeClosure(ls.data);
+    scr.dead = true;
+    for (var ls = scr.sources; ls; ls = ls.next)
+        ls.data.dyns = remove(scr, ls.data.dyns);
+    for (var ls = scr.closures; ls; ls = ls.next)
+        freeClosure(ls.data);
 }
 
 function dyn(pnode, s) {
-  var x = document.createElement("script");
-  x.dead = false;
-  x.signal = s;
-  x.sources = null;
-  x.closures = null;
+    var x = document.createElement("script");
+    x.dead = false;
+    x.signal = s;
+    x.sources = null;
+    x.closures = null;
 
-  var firstChild = null;
+    var firstChild = null;
 
-  x.recreate = function(v) {
-    for (var ls = x.closures; ls; ls = ls.next)
-      freeClosure(ls.data);
-
-    var next;
-    for (var child = firstChild; child && child != x; child = next) {
-      next = child.nextSibling;
-
-      killScript(child);
-      if (child.getElementsByTagName) {
-        var arr = child.getElementsByTagName("script");
-        for (var i = 0; i < arr.length; ++i)
-          killScript(arr[i]);
-      }
-
-      if (child.parentNode)
-        child.parentNode.removeChild(child);
-    }
-
-    var cls = {v : null};
-    var html = flatten(cls, v);
-    if (pnode != "table" && pnode != "tr")
-      html = dynPrefix + html;
-    x.closures = cls.v;
-
-    if (pnode == "table") {
-      var dummy = document.createElement("body");
-      dummy.innerHTML = "<table>" + html + "</table>";
-      runScripts(dummy);
-      var table = x.parentNode;
-
-      if (table) {
-        firstChild = null;
-        var tbody;
-
-        var arr = dummy.getElementsByTagName("tbody");
-
-        var tbody;
-        if (arr.length > 0 && arr[0].parentNode == dummy.firstChild) {
-          tbody = arr[0];
-          var next;
-          for (var node = dummy.firstChild.firstChild; node; node = next) {
-            next = node.nextSibling;
-
-            if (node.tagName != "TBODY")
-              tbody.appendChild(node);
-          }
-        } else
-          tbody = dummy.firstChild;
+    x.recreate = function(v) {
+        for (var ls = x.closures; ls; ls = ls.next)
+            freeClosure(ls.data);
 
         var next;
-        firstChild = document.createElement("script");
-        table.insertBefore(firstChild, x);
-        for (var node = tbody.firstChild; node; node = next) {
-          next = node.nextSibling;
-          table.insertBefore(node, x);
+        for (var child = firstChild; child && child != x; child = next) {
+            next = child.nextSibling;
+
+            killScript(child);
+            if (child.getElementsByTagName) {
+                var arr = child.getElementsByTagName("script");
+                for (var i = 0; i < arr.length; ++i)
+                    killScript(arr[i]);
+            }
+
+            if (child.parentNode)
+                child.parentNode.removeChild(child);
         }
-      }
-    } else if (pnode == "tr") {
-      var dummy = document.createElement("body");
-      dummy.innerHTML = "<table><tr>" + html + "</tr></table>";
-      runScripts(dummy);
-      var table = x.parentNode;
 
-      if (table) {
-        var arr = dummy.getElementsByTagName("tr");
-        firstChild = null;
-        var tr;
-        if (arr.length > 0 && table != null)
-          tr = arr[0];
-        else
-          tr = dummy.firstChild;
+        var cls = {v : null};
+        var html = flatten(cls, v);
+        if (pnode != "table" && pnode != "tr")
+            html = dynPrefix + html;
+        x.closures = cls.v;
 
-        var next;
-        firstChild = document.createElement("script");
-        table.insertBefore(firstChild, x);
-        for (var node = tr.firstChild; node; node = next) {
-          next = node.nextSibling;
-          table.insertBefore(node, x);
+        if (pnode == "table") {
+            var dummy = document.createElement("body");
+            dummy.innerHTML = "<table>" + html + "</table>";
+            runScripts(dummy);
+            var table = x.parentNode;
+
+            if (table) {
+                firstChild = null;
+                var tbody;
+
+                var arr = dummy.getElementsByTagName("tbody");
+
+                var tbody;
+                if (arr.length > 0 && arr[0].parentNode == dummy.firstChild) {
+                    tbody = arr[0];
+                    var next;
+                    for (var node = dummy.firstChild.firstChild; node; node = next) {
+                        next = node.nextSibling;
+
+                        if (node.tagName != "TBODY")
+                            tbody.appendChild(node);
+                    }
+                } else
+                    tbody = dummy.firstChild;
+
+                var next;
+                firstChild = document.createElement("script");
+                table.insertBefore(firstChild, x);
+                for (var node = tbody.firstChild; node; node = next) {
+                    next = node.nextSibling;
+                    table.insertBefore(node, x);
+                }
+            }
+        } else if (pnode == "tr") {
+            var dummy = document.createElement("body");
+            dummy.innerHTML = "<table><tr>" + html + "</tr></table>";
+            runScripts(dummy);
+            var table = x.parentNode;
+
+            if (table) {
+                var arr = dummy.getElementsByTagName("tr");
+                firstChild = null;
+                var tr;
+                if (arr.length > 0 && table != null)
+                    tr = arr[0];
+                else
+                    tr = dummy.firstChild;
+
+                var next;
+                firstChild = document.createElement("script");
+                table.insertBefore(firstChild, x);
+                for (var node = tr.firstChild; node; node = next) {
+                    next = node.nextSibling;
+                    table.insertBefore(node, x);
+                }
+            }
+        } else {
+            firstChild = document.createElement("span");
+            firstChild.innerHTML = html;
+            runScripts(firstChild);
+            if (x.parentNode)
+                x.parentNode.insertBefore(firstChild, x);
         }
-      }
-    } else {
-      firstChild = document.createElement("span");
-      firstChild.innerHTML = html;
-      runScripts(firstChild);
-      if (x.parentNode)
-        x.parentNode.insertBefore(firstChild, x);
-    }
-  };
+    };
 
-  addNode(x);
-  populate(x);
+    addNode(x);
+    populate(x);
 }
 
 function input(x, s, recreate, type) {
-  if (type) x.type = type;
-  x.dead = false;
-  x.signal = ss(s);
-  x.sources = null;
-  x.recreate = recreate(x);
-  addNode(x);
-  populate(x);
+    if (type) x.type = type;
+    x.dead = false;
+    x.signal = ss(s);
+    x.sources = null;
+    x.recreate = recreate(x);
+    addNode(x);
+    populate(x);
 
-  return x;
+    return x;
 }
 
 function inp(s) {
-  var x = input(document.createElement("input"), s,
-        function(x) { return function(v) { if (x.value != v) x.value = v; }; });
-  x.value = s.data;
-  x.onkeyup = function() { sv(s, x.value) };
+    var x = input(document.createElement("input"), s,
+                  function(x) { return function(v) { if (x.value != v) x.value = v; }; });
+    x.value = s.data;
+    x.onkeyup = function() { sv(s, x.value) };
 
-  return x;
+    return x;
 }
 
 function sel(s, content) {
-  var dummy = document.createElement("span");
-  dummy.innerHTML = "<select>" + content + "</select>";
-  var x = input(dummy.firstChild, s, function(x) { return function(v) { if (x.value != v) x.value = v; }; });
-  x.value = s.data;
-  if (x.value != s.data)
-    sv(s, x.value);
-  x.onchange = function() { sv(s, x.value) };
+    var dummy = document.createElement("span");
+    dummy.innerHTML = "<select>" + content + "</select>";
+    var x = input(dummy.firstChild, s, function(x) { return function(v) { if (x.value != v) x.value = v; }; });
+    x.value = s.data;
+    if (x.value != s.data)
+        sv(s, x.value);
+    x.onchange = function() { sv(s, x.value) };
 
-  return x;
+    return x;
 }
 
 function chk(s) {
-  var x = input(document.createElement("input"), s,
-        function(x) { return function(v) { if (x.checked != v) x.checked = v; }; }, "checkbox");
-  x.defaultChecked = x.checked = s.data;
-  x.onclick = function() { sv(s, x.checked) };
+    var x = input(document.createElement("input"), s,
+                  function(x) { return function(v) { if (x.checked != v) x.checked = v; }; }, "checkbox");
+    x.defaultChecked = x.checked = s.data;
+    x.onclick = function() { sv(s, x.checked) };
 
-  return x;
+    return x;
 }
 
 function tbx(s) {
-  var x = input(document.createElement("textarea"), s,
-        function(x) { return function(v) { if (x.innerHTML != v) x.innerHTML = v; }; });
-  x.innerHTML = s.data;
-  x.onkeyup = function() { sv(s, x.value) };
+    var x = input(document.createElement("textarea"), s,
+                  function(x) { return function(v) { if (x.innerHTML != v) x.innerHTML = v; }; });
+    x.innerHTML = s.data;
+    x.onkeyup = function() { sv(s, x.value) };
 
-  return x;
+    return x;
 }
 
 function addOnChange(x, f) {
-  var old = x.onchange;
-  x.onchange = function() { old(); f (); };
+    var old = x.onchange;
+    x.onchange = function() { old(); f (); };
 }
 
 
 // Basic string operations
 
 function eh(x) {
-  if (x == null)
-    return "NULL";
-  else
-    return x.split("&").join("&amp;").split("<").join("&lt;").split(">").join("&gt;");
+    if (x == null)
+        return "NULL";
+    else
+        return x.split("&").join("&amp;").split("<").join("&lt;").split(">").join("&gt;");
 }
 
 function ts(x) { return x.toString() }
@@ -533,102 +533,102 @@
 function suf(s, i) { return s.substring(i); }
 function slen(s) { return s.length; }
 function sidx(s, ch) {
-  var r = s.indexOf(ch);
-  if (r == -1)
-    return null;
-  else
-    return r;
+    var r = s.indexOf(ch);
+    if (r == -1)
+        return null;
+    else
+        return r;
 }
 function sspn(s, chs) {
-  for (var i = 0; i < s.length; ++i)
-    if (chs.indexOf(s.charAt(i)) != -1)
-      return i;
+    for (var i = 0; i < s.length; ++i)
+        if (chs.indexOf(s.charAt(i)) != -1)
+            return i;
 
-  return null;
+    return null;
 }
 function schr(s, ch) {
-  var r = s.indexOf(ch);
-  if (r == -1)
-    return null;
-  else
-    return s.substring(r);
+    var r = s.indexOf(ch);
+    if (r == -1)
+        return null;
+    else
+        return s.substring(r);
 }
 function ssub(s, start, len) {
-  return s.substring(start, start+len);
+    return s.substring(start, start+len);
 }
 
 function pi(s) {
-  var r = parseInt(s);
-  if (r.toString() == s)
-    return r;
-  else
-    er("Can't parse int: " + s);
+    var r = parseInt(s);
+    if (r.toString() == s)
+        return r;
+    else
+        er("Can't parse int: " + s);
 }
 
 function pfl(s) {
-  var r = parseFloat(s);
-  if (r.toString() == s)
-    return r;
-  else
-    er("Can't parse float: " + s);
+    var r = parseFloat(s);
+    if (r.toString() == s)
+        return r;
+    else
+        er("Can't parse float: " + s);
 }
 
 function pio(s) {
-  var r = parseInt(s);
-  if (r.toString() == s)
-    return r;
-  else
-    return null;
+    var r = parseInt(s);
+    if (r.toString() == s)
+        return r;
+    else
+        return null;
 }
 
 function pflo(s) {
-  var r = parseFloat(s);
-  if (r.toString() == s)
-    return r;
-  else
-    return null;
+    var r = parseFloat(s);
+    if (r.toString() == s)
+        return r;
+    else
+        return null;
 }
 
 function uf(s) {
-  if (s.length == 0)
-    return "_";
-  s = s.replace(new RegExp ("\\.", "g"), ".2E");
-  return (s.charAt(0) == '_' ? "_" : "") + encodeURIComponent(s).replace(new RegExp ("%", "g"), ".");
+    if (s.length == 0)
+        return "_";
+    s = s.replace(new RegExp ("\\.", "g"), ".2E");
+    return (s.charAt(0) == '_' ? "_" : "") + encodeURIComponent(s).replace(new RegExp ("%", "g"), ".");
 }
 
 function uu(s) {
-  if (s.length > 0 && s.charAt(0) == '_') {
-    s = s.substring(1);
-  } else if (s.length >= 3 && (s.charAt(0) == '%' || s.charAt(0) == '.')
-             && s.charAt(1) == '5' && (s.charAt(2) == 'f' || s.charAt(2) == 'F'))
-      s = s.substring(3);
-  s = s.replace(new RegExp ("\\+", "g"), " ");
-  s = s.replace(new RegExp ("\\.", "g"), "%");
-  return decodeURIComponent(s);
+    if (s.length > 0 && s.charAt(0) == '_') {
+        s = s.substring(1);
+    } else if (s.length >= 3 && (s.charAt(0) == '%' || s.charAt(0) == '.')
+               && s.charAt(1) == '5' && (s.charAt(2) == 'f' || s.charAt(2) == 'F'))
+        s = s.substring(3);
+    s = s.replace(new RegExp ("\\+", "g"), " ");
+    s = s.replace(new RegExp ("\\.", "g"), "%");
+    return decodeURIComponent(s);
 }
 
 function atr(s) {
-  return s.replace(new RegExp ("\"", "g"), "&quot;").replace(new RegExp ("&", "g"), "&amp;")
-}
+    return s.replace(new RegExp ("\"", "g"), "&quot;").replace(new RegExp ("&", "g"), "&amp;")
+        }
 
 function ub(b) {
-  return b ? "1" : "0";
+    return b ? "1" : "0";
 }
 
 function uul(getToken, getData) {
-  var tok = getToken();
-  if (tok == "Nil") {
-    return null;
-  } else if (tok == "Cons") {
-    var d = getData();
-    var l = uul(getToken, getData);
-    return {_1:d, _2:l};
-  } else
-    whine("Can't unmarshal list (" + tok + ")");
+    var tok = getToken();
+    if (tok == "Nil") {
+        return null;
+    } else if (tok == "Cons") {
+        var d = getData();
+        var l = uul(getToken, getData);
+        return {_1:d, _2:l};
+    } else
+        whine("Can't unmarshal list (" + tok + ")");
 }
 
 function strcmp(str1, str2) {
-  return ((str1 == str2) ? 0 : ((str1 > str2) ? 1 : -1));
+    return ((str1 == str2) ? 0 : ((str1 > str2) ? 1 : -1));
 }
 
 
@@ -641,19 +641,19 @@
 
 function getXHR(uri)
 {
-  try {
-    return new XMLHttpRequest();
-  } catch (e) {
     try {
-     return new ActiveXObject("Msxml2.XMLHTTP");
+        return new XMLHttpRequest();
     } catch (e) {
-      try {
-        return new ActiveXObject("Microsoft.XMLHTTP");
-      } catch (e) {
-        whine("Your browser doesn't seem to support AJAX.");
-      }
+        try {
+            return new ActiveXObject("Msxml2.XMLHTTP");
+        } catch (e) {
+            try {
+                return new ActiveXObject("Microsoft.XMLHTTP");
+            } catch (e) {
+                whine("Your browser doesn't seem to support AJAX.");
+            }
+        }
     }
-  }
 }
 
 var sig = null;
@@ -661,230 +661,230 @@
 var unloading = false, inFlight = null;
 
 function unload() {
-  unloading = true;
+    unloading = true;
 
-  for (; inFlight; inFlight = inFlight.next) {
-    inFlight.data.abort();
-  }
+    for (; inFlight; inFlight = inFlight.next) {
+        inFlight.data.abort();
+    }
 }
 
 function requestUri(xhr, uri, needsSig) {
-  if (unloading)
-    return;
+    if (unloading)
+        return;
 
-  xhr.open("POST", uri, true);
-  xhr.setRequestHeader("Content-type", "text/plain");
-  xhr.setRequestHeader("Content-length", "0");
-  xhr.setRequestHeader("Connection", "close");
+    xhr.open("POST", uri, true);
+    xhr.setRequestHeader("Content-type", "text/plain");
+    xhr.setRequestHeader("Content-length", "0");
+    xhr.setRequestHeader("Connection", "close");
 
-  if (client_id != null) {
-    xhr.setRequestHeader("UrWeb-Client", client_id.toString());
-    xhr.setRequestHeader("UrWeb-Pass", client_pass.toString());
-  }
+    if (client_id != null) {
+        xhr.setRequestHeader("UrWeb-Client", client_id.toString());
+        xhr.setRequestHeader("UrWeb-Pass", client_pass.toString());
+    }
 
-  if (needsSig) {
-    if (sig == null)
-      whine("Missing cookie signature!");
+    if (needsSig) {
+        if (sig == null)
+            whine("Missing cookie signature!");
 
-    xhr.setRequestHeader("UrWeb-Sig", sig);
-  }
+        xhr.setRequestHeader("UrWeb-Sig", sig);
+    }
 
-  inFlight = cons(xhr, inFlight);
-  xhr.send(null);
+    inFlight = cons(xhr, inFlight);
+    xhr.send(null);
 }
 
 function xhrFinished(xhr) {
-  xhr.abort();
-  inFlight = remove(xhr, inFlight);
+    xhr.abort();
+    inFlight = remove(xhr, inFlight);
 }
 
 function unurlify(parse, s) {
-  return parse(s);
+    return parse(s);
 }
 
 function rc(prefix, uri, parse, k, needsSig) {
-  uri = cat(prefix, uri);
-  uri = flattenLocal(uri);
-  var xhr = getXHR();
+    uri = cat(prefix, uri);
+    uri = flattenLocal(uri);
+    var xhr = getXHR();
 
-  xhr.onreadystatechange = function() {
-    if (xhr.readyState == 4) {
-      var isok = false;
+    xhr.onreadystatechange = function() {
+        if (xhr.readyState == 4) {
+            var isok = false;
 
-      try {
-        if (xhr.status == 200)
-          isok = true;
-      } catch (e) { }
+            try {
+                if (xhr.status == 200)
+                    isok = true;
+            } catch (e) { }
 
-      if (isok) {
-        try {
-          k(parse(xhr.responseText));
-        } catch (v) {
-          doExn(v);
+            if (isok) {
+                try {
+                    k(parse(xhr.responseText));
+                } catch (v) {
+                    doExn(v);
+                }
+            } else {
+                conn();
+            }
+
+            xhrFinished(xhr);
         }
-      } else {
-        conn();
-      }
+    };
 
-      xhrFinished(xhr);
-    }
-  };
-
-  requestUri(xhr, uri, needsSig);
+    requestUri(xhr, uri, needsSig);
 }
 
 function path_join(s1, s2) {
-  if (s1.length > 0 && s1.charAt(s1.length-1) == '/')
-    return s1 + s2;
-  else
-    return s1 + "/" + s2;
+    if (s1.length > 0 && s1.charAt(s1.length-1) == '/')
+        return s1 + s2;
+    else
+        return s1 + "/" + s2;
 }
 
 var channels = [];
 
 function newQueue() {
-  return { front : null, back : null };
+    return { front : null, back : null };
 }
 function enqueue(q, v) {
-  if (q.front == null) {
-    q.front = cons(v, null);
-    q.back = q.front;
-  } else {
-    var node = cons(v, null);
-    q.back.next = node;
-    q.back = node;
-  }
+    if (q.front == null) {
+        q.front = cons(v, null);
+        q.back = q.front;
+    } else {
+        var node = cons(v, null);
+        q.back.next = node;
+        q.back = node;
+    }
 }
 function dequeue(q) {
-  if (q.front == null)
-    return null;
-  else {
-    var r = q.front.data;
-    q.front = q.front.next;
     if (q.front == null)
-      q.back = null;
-    return r;
-  }
+        return null;
+    else {
+        var r = q.front.data;
+        q.front = q.front.next;
+        if (q.front == null)
+            q.back = null;
+        return r;
+    }
 }
 
 function newChannel() {
-  return { msgs : newQueue(), listeners : newQueue() };
+    return { msgs : newQueue(), listeners : newQueue() };
 }
 
 function listener() {
-  var uri = path_join(url_prefix, ".msgs");
-  var xhr = getXHR();
-  var tid, orsc, onTimeout;
+    var uri = path_join(url_prefix, ".msgs");
+    var xhr = getXHR();
+    var tid, orsc, onTimeout;
 
-  var connect = function () {
-    xhr.onreadystatechange = orsc;
-    tid = window.setTimeout(onTimeout, timeout * 500);
-    requestUri(xhr, uri, false);
-  }
+    var connect = function () {
+        xhr.onreadystatechange = orsc;
+        tid = window.setTimeout(onTimeout, timeout * 500);
+        requestUri(xhr, uri, false);
+    }
 
-  orsc = function() {
-    if (xhr.readyState == 4) {
-      window.clearTimeout(tid);
+    orsc = function() {
+        if (xhr.readyState == 4) {
+            window.clearTimeout(tid);
 
-      var isok = false;
+            var isok = false;
 
-      try {
-        if (xhr.status == 200)
-          isok = true;
-      } catch (e) { }
+            try {
+                if (xhr.status == 200)
+                    isok = true;
+            } catch (e) { }
 
-      if (isok) {
-        var text = xhr.responseText
-        if (text == "")
-          return;
-        var lines = text.split("\n");
+            if (isok) {
+                var text = xhr.responseText
+                if (text == "")
+                    return;
+                var lines = text.split("\n");
 
-        if (lines.length < 2) {
-          discon();
-          return;
+                if (lines.length < 2) {
+                    discon();
+                    return;
+                }
+
+                for (var i = 0; i+1 < lines.length; i += 2) {
+                    var chn = lines[i];
+                    var msg = lines[i+1];
+
+                    if (chn < 0)
+                        whine("Out-of-bounds channel in message from remote server");
+
+                    var ch;
+
+                    if (chn >= channels.length || channels[chn] == null) {
+                        ch = newChannel();
+                        channels[chn] = ch;
+                    } else
+                        ch = channels[chn];
+
+                    var listener = dequeue(ch.listeners);
+                    if (listener == null) {
+                        enqueue(ch.msgs, msg);
+                    } else {
+                        try {
+                            listener(msg);
+                        } catch (v) {
+                            doExn(v);
+                        }
+                    }
+                }
+
+                xhrFinished(xhr);
+
+                connect();
+            }
+            else {
+                try {
+                    if (xhr.status != 0)
+                        servErr("Error querying remote server for messages: " + xhr.status);
+                } catch (e) { }
+            }
         }
+    };
 
-        for (var i = 0; i+1 < lines.length; i += 2) {
-          var chn = lines[i];
-          var msg = lines[i+1];
+    onTimeout = function() {
+        xhrFinished(xhr);
+        connect();
+    };
 
-          if (chn < 0)
-            whine("Out-of-bounds channel in message from remote server");
-
-          var ch;
-
-          if (chn >= channels.length || channels[chn] == null) {
-            ch = newChannel();
-            channels[chn] = ch;
-          } else
-            ch = channels[chn];
-
-          var listener = dequeue(ch.listeners);
-          if (listener == null) {
-            enqueue(ch.msgs, msg);
-          } else {
-            try {
-              listener(msg);
-            } catch (v) {
-              doExn(v);
-            }
-          }
-        }
-
-        xhrFinished(xhr);
-
-        connect();
-      }
-      else {
-        try {
-          if (xhr.status != 0)
-            servErr("Error querying remote server for messages: " + xhr.status);
-        } catch (e) { }
-      }
-    }
-  };
-
-  onTimeout = function() {
-    xhrFinished(xhr);
     connect();
-  };
-
-  connect();
 }
 
 function rv(chn, parse, k) {
-  if (chn == null)
-    return;
+    if (chn == null)
+        return;
 
-  if (chn < 0)
-    whine("Out-of-bounds channel receive");
+    if (chn < 0)
+        whine("Out-of-bounds channel receive");
 
-  var ch;
+    var ch;
 
-  if (chn >= channels.length || channels[chn] == null) {
-    ch = newChannel();
-    channels[chn] = ch;
-  } else
-    ch = channels[chn];
+    if (chn >= channels.length || channels[chn] == null) {
+        ch = newChannel();
+        channels[chn] = ch;
+    } else
+        ch = channels[chn];
 
-  var msg = dequeue(ch.msgs);
-  if (msg == null) {
-    enqueue(ch.listeners, function(msg) { k(parse(msg)); });
-  } else {
-    try {
-      k(parse(msg));
-    } catch (v) {
-      doExn(v);
+    var msg = dequeue(ch.msgs);
+    if (msg == null) {
+        enqueue(ch.listeners, function(msg) { k(parse(msg)); });
+    } else {
+        try {
+            k(parse(msg));
+        } catch (v) {
+            doExn(v);
+        }
     }
-  }
 }
 
 function sl(ms, k) {
-  window.setTimeout(function() { k(null); }, ms);
+    window.setTimeout(function() { k(null); }, ms);
 }
 
 function sp(e) {
-  execF(e, null);
+    execF(e, null);
 }
 
 
@@ -893,7 +893,7 @@
 var uw_event = null;
 
 function kc() {
-  return window.event ? uw_event.keyCode : uw_event.which;
+    return window.event ? uw_event.keyCode : uw_event.which;
 }
 
 
@@ -902,262 +902,265 @@
 var urfuncs = [];
 
 function lookup(env, n) {
-  while (env != null) {
-    if (n == 0)
-      return env.data;
-    else {
-      --n;
-      env = env.next;
+    while (env != null) {
+        if (n == 0)
+            return env.data;
+        else {
+            --n;
+            env = env.next;
+        }
     }
-  }
 
-  whine("Out-of-bounds Ur variable reference");
+    whine("Out-of-bounds Ur variable reference");
 }
 
 function execP(env, p, v) {
-  switch (p.c) {
-  case "w":
-    return env;
-  case "v":
-    return cons(v, env);
-  case "c":
-    if (v == p.v)
-      return env;
-    else
-      return false;
-  case "s":
-    if (v == null)
-      return false;
-    else
-      return execP(env, p.p, p.n ? v.v : v);
-  case "1":
-    if (v.n != p.n)
-      return false;
-    else
-      return execP(env, p.p, v.v);
-  case "r":
-    for (var fs = p.l; fs != null; fs = fs.next) {
-      env = execP(env, fs.data.p, v["_" + fs.data.n]);
-      if (env == false)
-        return false;
+    switch (p.c) {
+    case "w":
+        return env;
+    case "v":
+        return cons(v, env);
+    case "c":
+        if (v == p.v)
+            return env;
+        else
+            return false;
+    case "s":
+        if (v == null)
+            return false;
+        else
+            return execP(env, p.p, p.n ? v.v : v);
+    case "1":
+        if (v.n != p.n)
+            return false;
+        else
+            return execP(env, p.p, v.v);
+    case "r":
+        for (var fs = p.l; fs != null; fs = fs.next) {
+            env = execP(env, fs.data.p, v["_" + fs.data.n]);
+            if (env == false)
+                return false;
+        }
+        return env;
+    default:
+        whine("Unknown Ur pattern kind" + p.c);
     }
-    return env;
-  default:
-    whine("Unknown Ur pattern kind" + p.c);
-  }
 }
 
 function exec0(env, e) {
-  return exec1(env, null, e);
+    return exec1(env, null, e);
 }
 
 function exec1(env, stack, e) {
-  var stack, usedK = false;
+    var stack, usedK = false;
 
-  var saveEnv = function() {
-    if (stack.next != null && stack.next.data.c != "<")
-      stack = cons({c: "<", env: env}, stack.next);
-    else
-      stack = stack.next;
-  };
+    var saveEnv = function() {
+        if (stack.next != null && stack.next.data.c != "<")
+            stack = cons({c: "<", env: env}, stack.next);
+        else
+            stack = stack.next;
+    };
 
-  while (true) {
-    switch (e.c) {
-    case "c":
-      var v = e.v;
-      if (stack == null)
-        return v;
-      var fr = stack.data;
+    while (true) {
+        switch (e.c) {
+        case "c":
+            var v = e.v;
+            if (stack == null)
+                return v;
+            var fr = stack.data;
 
-      switch (fr.c) {
-      case "s":
-        e = {c: "c", v: {v: v}};
-        stack = stack.next;
-        break;
-      case "1":
-        e = {c: "c", v: {n: fr.n, v: v}};
-        stack = stack.next;
-        break;
-      case "f":
-        fr.args[fr.pos++] = v;
-        if (fr.a == null) {
-          var res;
-          stack = stack.next;
+            switch (fr.c) {
+            case "s":
+                e = {c: "c", v: {v: v}};
+                stack = stack.next;
+                break;
+            case "1":
+                e = {c: "c", v: {n: fr.n, v: v}};
+                stack = stack.next;
+                break;
+            case "f":
+                fr.args[fr.pos++] = v;
+                if (fr.a == null) {
+                    var res;
+                    stack = stack.next;
 
-          if (fr.f.apply)
-            res = fr.f.apply(null, fr.args);
-          else if (fr.args.length == 0)
-            res = fr.f();
-          else if (fr.args.length == 1)
-            res = fr.f(fr.args[0]);
-          else if (fr.args.length == 2)
-            res = fr.f(fr.args[0], fr.args[1]);
-          else if (fr.args.length == 3)
-            res = fr.f(fr.args[0], fr.args[1], fr.args[2]);
-          else if (fr.args.length == 4)
-            res = fr.f(fr.args[0], fr.args[1], fr.args[2], fr.args[3]);
-          else if (fr.args.length == 5)
-            res = fr.f(fr.args[0], fr.args[1], fr.args[2], fr.args[3], fr.args[4]);
-          else
-            whine("Native function has " + fr.args.length + " args, but there is no special case for that count.");
+                    if (fr.f.apply)
+                        res = fr.f.apply(null, fr.args);
+                    else if (fr.args.length == 0)
+                        res = fr.f();
+                    else if (fr.args.length == 1)
+                        res = fr.f(fr.args[0]);
+                    else if (fr.args.length == 2)
+                        res = fr.f(fr.args[0], fr.args[1]);
+                    else if (fr.args.length == 3)
+                        res = fr.f(fr.args[0], fr.args[1], fr.args[2]);
+                    else if (fr.args.length == 4)
+                        res = fr.f(fr.args[0], fr.args[1], fr.args[2], fr.args[3]);
+                    else if (fr.args.length == 5)
+                        res = fr.f(fr.args[0], fr.args[1], fr.args[2], fr.args[3], fr.args[4]);
+                    else
+                        whine("Native function has " + fr.args.length + " args, but there is no special case for that count.");
 
-          e = {c: "c", v: res};
-          if (usedK) return null;
-        } else {
-          e = fr.a.data;
-          fr.a = fr.a.next;
+                    e = {c: "c", v: res};
+                    if (usedK) return null;
+                } else {
+                    e = fr.a.data;
+                    fr.a = fr.a.next;
+                }
+                break;
+            case "a1":
+                e = fr.x;
+                stack = cons({c: "a2", f: v}, stack.next);
+                break;
+            case "a2":
+                if (fr.f == null)
+                    whine("Ur: applying null function");
+                else if (fr.f.body) {
+                    saveEnv();
+                    env = cons(v, fr.f.env);
+                    e = fr.f.body;
+                } else {
+                    e = {c: "c", v: fr.f(v)};
+                    stack = stack.next;
+                }
+                break;
+            case "<":
+                env = fr.env;
+                stack = stack.next;
+                break;
+            case "r":
+                fr.fs["_" + fr.n] = v;
+                if (fr.l == null) {
+                    e = {c: "c", v: fr.fs};
+                    stack = stack.next;
+                } else {
+                    fr.n = fr.l.data.n;
+                    e = fr.l.data.v;
+                    fr.l = fr.l.next;
+                }
+                break;
+            case ".":
+                e = {c: "c", v: v["_" + fr.f]};
+                stack = stack.next;
+                break;
+            case ";":
+                e = fr.e2;
+                stack = stack.next;
+                break;
+            case "=":
+                saveEnv();
+                env = cons(v, env);
+                e = fr.e2;
+                break;
+            case "m":
+                var ps;
+                for (ps = fr.p; ps != null; ps = ps.next) {
+                    var r = execP(env, ps.data.p, v);
+                    if (r != false) {
+                        saveEnv();
+                        env = r;
+                        e = ps.data.b;
+                        break;
+                    }
+                }
+                if (ps == null)
+                    whine("Match failure in Ur interpretation");
+                break;
+            default:
+                whine("Unknown Ur continuation kind " + fr.c);
+            }
+
+            break;
+        case "v":
+            e = {c: "c", v: lookup(env, e.n)};
+            break;
+        case "n":
+            var idx = e.n;
+            e = urfuncs[idx];
+            if (e.c == "t")
+                e = urfuncs[idx] = e.f();
+            break;
+        case "s":
+            stack = cons({c: "s"}, stack);
+            e = e.v;
+            break;
+        case "1":
+            stack = cons({c: "1", n: e.n}, stack);
+            e = e.v;
+            break;
+        case "f":
+            if (e.a == null)
+                e = {c: "c", v: (eval(e.f))()};
+            else {
+                var args = [];
+                stack = cons({c: "f", f: eval(e.f), args: args, pos: 0, a: e.a.next}, stack);
+                if (!e.a.data.c) alert("[2] fr.f = " + e.f + "; 0 = " + e.a.data);
+                e = e.a.data;
+            }
+            break;
+        case "l":
+            e = {c: "c", v: {env: env, body: e.b}};
+            break;
+        case "a":
+            stack = cons({c: "a1", x: e.x}, stack);
+            e = e.f;
+            break;
+        case "r":
+            if (e.l == null)
+                whine("Empty Ur record in interpretation");
+            var fs = {};
+            stack = cons({c: "r", n: e.l.data.n, fs: fs, l: e.l.next}, stack);
+            e = e.l.data.v;
+            break;
+        case ".":
+            stack = cons({c: ".", f: e.f}, stack);
+            e = e.r;
+            break;
+        case ";":
+            stack = cons({c: ";", e2: e.e2}, stack);
+            e = e.e1;
+            break;
+        case "=":
+            stack = cons({c: "=", e2: e.e2}, stack);
+            e = e.e1;
+            break;
+        case "m":
+            stack = cons({c: "m", p: e.p}, stack);
+            e = e.e;
+            break;
+        case "e":
+            e = {c: "c", v: cs({c: "wc", env: env, body: e.e})};
+            break;
+        case "wc":
+            env = e.env;
+            e = e.body;
+            break;
+        case "K":
+            { var savedStack = stack.next, savedEnv = env;
+                e = {c: "c", v: function(v) { return exec1(savedEnv, savedStack, {c: "c", v: v}); } };}
+            usedK = true;
+            break;      
+        default:
+            whine("Unknown Ur expression kind " + e.c);
         }
-        break;
-      case "a1":
-        e = fr.x;
-        stack = cons({c: "a2", f: v}, stack.next);
-        break;
-      case "a2":
-        if (fr.f == null)
-          whine("Ur: applying null function");
-        else if (fr.f.body) {
-          saveEnv();
-          env = cons(v, fr.f.env);
-          e = fr.f.body;
-        } else {
-          e = {c: "c", v: fr.f(v)};
-          stack = stack.next;
-        }
-        break;
-      case "<":
-        env = fr.env;
-        stack = stack.next;
-        break;
-      case "r":
-        fr.fs["_" + fr.n] = v;
-        if (fr.l == null) {
-          e = {c: "c", v: fr.fs};
-          stack = stack.next;
-        } else {
-          fr.n = fr.l.data.n;
-          e = fr.l.data.v;
-          fr.l = fr.l.next;
-        }
-        break;
-      case ".":
-        e = {c: "c", v: v["_" + fr.f]};
-        stack = stack.next;
-        break;
-      case ";":
-        e = fr.e2;
-        stack = stack.next;
-        break;
-      case "=":
-        saveEnv();
-        env = cons(v, env);
-        e = fr.e2;
-        break;
-      case "m":
-        var ps;
-        for (ps = fr.p; ps != null; ps = ps.next) {
-          var r = execP(env, ps.data.p, v);
-          if (r != false) {
-            saveEnv();
-            env = r;
-            e = ps.data.b;
-            break;
-          }
-        }
-        if (ps == null)
-          whine("Match failure in Ur interpretation");
-        break;
-      default:
-        whine("Unknown Ur continuation kind " + fr.c);
-      }
-
-      break;
-    case "v":
-      e = {c: "c", v: lookup(env, e.n)};
-      break;
-    case "n":
-      e = urfuncs[e.n];
-      break;
-    case "s":
-      stack = cons({c: "s"}, stack);
-      e = e.v;
-      break;
-    case "1":
-      stack = cons({c: "1", n: e.n}, stack);
-      e = e.v;
-      break;
-    case "f":
-      if (e.a == null)
-        e = {c: "c", v: (eval(e.f))()};
-      else {
-        var args = [];
-        stack = cons({c: "f", f: eval(e.f), args: args, pos: 0, a: e.a.next}, stack);
-        if (!e.a.data.c) alert("[2] fr.f = " + e.f + "; 0 = " + e.a.data);
-        e = e.a.data;
-      }
-      break;
-    case "l":
-      e = {c: "c", v: {env: env, body: e.b}};
-      break;
-    case "a":
-      stack = cons({c: "a1", x: e.x}, stack);
-      e = e.f;
-      break;
-    case "r":
-      if (e.l == null)
-        whine("Empty Ur record in interpretation");
-      var fs = {};
-      stack = cons({c: "r", n: e.l.data.n, fs: fs, l: e.l.next}, stack);
-      e = e.l.data.v;
-      break;
-    case ".":
-      stack = cons({c: ".", f: e.f}, stack);
-      e = e.r;
-      break;
-    case ";":
-      stack = cons({c: ";", e2: e.e2}, stack);
-      e = e.e1;
-      break;
-    case "=":
-      stack = cons({c: "=", e2: e.e2}, stack);
-      e = e.e1;
-      break;
-    case "m":
-      stack = cons({c: "m", p: e.p}, stack);
-      e = e.e;
-      break;
-    case "e":
-      e = {c: "c", v: cs({c: "wc", env: env, body: e.e})};
-      break;
-    case "wc":
-      env = e.env;
-      e = e.body;
-      break;
-    case "K":
-      { var savedStack = stack.next, savedEnv = env;
-      e = {c: "c", v: function(v) { return exec1(savedEnv, savedStack, {c: "c", v: v}); } };}
-      usedK = true;
-      break;      
-    default:
-      whine("Unknown Ur expression kind " + e.c);
     }
-  }
 }
 
 function execD(e) {
-  return exec0(null, e);
+    return exec0(null, e);
 }
 
 function exec(e) {
-  var r = exec0(null, e);
+    var r = exec0(null, e);
 
-  if (r != null && r.body != null)
-    return function(v) { return exec0(cons(v, r.env), r.body); };
-  else
-    return r;
+    if (r != null && r.body != null)
+        return function(v) { return exec0(cons(v, r.env), r.body); };
+    else
+        return r;
 }
 
 function execF(f, x) {
-  return exec0(cons(x, f.env), f.body);
+    return exec0(cons(x, f.env), f.body);
 }