comparison 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
comparison
equal deleted inserted replaced
1259:83b1853d1e58 1260:25ebd8c4fafb
1 // Detect browser quirks that we should be aware of. 1 // Detect browser quirks that we should be aware of.
2 2
3 function needsDynPrefix() { 3 function needsDynPrefix() {
4 var span = document.createElement("span"); 4 var span = document.createElement("span");
5 span.innerHTML = "<script>alert('test');</script>"; 5 span.innerHTML = "<script>alert('test');</script>";
6 var scripts = span.getElementsByTagName("script"); 6 var scripts = span.getElementsByTagName("script");
7 return scripts.length == 0; 7 return scripts.length == 0;
8 } 8 }
9 9
10 var dynPrefix = needsDynPrefix() ? "<span style=\"display:none\">A</span>" : ""; 10 var dynPrefix = needsDynPrefix() ? "<span style=\"display:none\">A</span>" : "";
11 11
12 // Function versions of operators 12 // Function versions of operators
38 38
39 39
40 // Lists 40 // Lists
41 41
42 function cons(v, ls) { 42 function cons(v, ls) {
43 return { next : ls, data : v }; 43 return { next : ls, data : v };
44 } 44 }
45 function rev(ls) { 45 function rev(ls) {
46 var acc = null; 46 var acc = null;
47 for (; ls; ls = ls.next) 47 for (; ls; ls = ls.next)
48 acc = cons(ls.data, acc); 48 acc = cons(ls.data, acc);
49 return acc; 49 return acc;
50 } 50 }
51 function concat(ls1, ls2) { 51 function concat(ls1, ls2) {
52 var acc = ls2; 52 var acc = ls2;
53 ls1 = rev(ls1); 53 ls1 = rev(ls1);
54 for (; ls1; ls1 = ls1.next) 54 for (; ls1; ls1 = ls1.next)
55 acc = cons(ls1.data, acc); 55 acc = cons(ls1.data, acc);
56 return acc; 56 return acc;
57 } 57 }
58 function member(x, ls) { 58 function member(x, ls) {
59 for (; ls; ls = ls.next) 59 for (; ls; ls = ls.next)
60 if (ls.data == x) 60 if (ls.data == x)
61 return true; 61 return true;
62 return false; 62 return false;
63 } 63 }
64 function remove(x, ls) { 64 function remove(x, ls) {
65 var acc = null; 65 var acc = null;
66 66
67 for (; ls; ls = ls.next) 67 for (; ls; ls = ls.next)
68 if (ls.data == x) 68 if (ls.data == x)
69 return concat(acc, ls.next); 69 return concat(acc, ls.next);
70 else
71 acc = cons(ls.data, acc);
72
73 return ls;
74 }
75 function union(ls1, ls2) {
76 var acc = ls2;
77
78 for (; ls1; ls1 = ls1.next)
79 if (!member(ls1.data, ls2))
80 acc = cons(ls1.data, acc);
81
82 return acc;
83 }
84 function length(ls) {
85 var acc = 0;
86
87 for (; ls; ls = ls.next)
88 ++acc;
89
90 return acc;
91 }
92
93
94 // Error handling
95
96 function whine(msg) {
97 alert(msg);
98 throw msg;
99 }
100
101 function pf(loc) {
102 throw ("Pattern match failure (" + loc + ")");
103 }
104
105 function runHandlers(kind, ls, arg) {
106 if (ls == null)
107 alert(kind + ": " + arg);
108 for (; ls; ls = ls.next)
109 try {
110 exec({c:"a", f:{c:"a", f:ls.data, x:{c:"c", v:arg}}, x:{c:"c", v:null}});
111 } catch (v) { }
112 }
113
114 var errorHandlers = null;
115
116 function onError(f) {
117 errorHandlers = cons(f, errorHandlers);
118 }
119
120 function er(s) {
121 runHandlers("Error", errorHandlers, s);
122 throw {uw_error: s};
123 }
124
125 var failHandlers = null;
126
127 function onFail(f) {
128 failHandlers = cons(f, failHandlers);
129 }
130
131 function doExn(v) {
132 if (v == null || v.uw_error == null) {
133 var s = (v == null ? "null" : v.message ? v.message : v.toString());
134 if (v != null && v.fileName && v.lineNumber)
135 s += " (" + v.fileName + ":" + v.lineNumber + ")";
136 runHandlers("Fail", failHandlers, s);
137 }
138 }
139
140 var disconnectHandlers = null;
141
142 function flift(f) {
143 return {env:cons(f,null), body:{c:"v", n:1}};
144 }
145
146 function onDisconnect(f) {
147 disconnectHandlers = cons(flift(f), disconnectHandlers);
148 }
149
150 function discon() {
151 runHandlers("Disconnect", disconnectHandlers, null);
152 }
153
154 var connectHandlers = null;
155
156 function onConnectFail(f) {
157 connectHandlers = cons(flift(f), connectHandlers);
158 }
159
160 function conn() {
161 runHandlers("Connect", connectHandlers, null);
162 }
163
164 var serverHandlers = null;
165
166 function onServerError(f) {
167 serverHandlers = cons(f, serverHandlers);
168 }
169
170 function servErr(s) {
171 window.setTimeout(function () { runHandlers("Server", serverHandlers, s); }, 0);
172 }
173
174
175 // Embedding closures in XML strings
176
177 function cs(f) {
178 return {closure: f};
179 }
180
181 function isWeird(v) {
182 return v.closure != null || v.cat1 != null;
183 }
184
185 function cat(s1, s2) {
186 if (isWeird(s1) || isWeird(s2))
187 return {cat1: s1, cat2: s2};
70 else 188 else
71 acc = cons(ls.data, acc); 189 return s1 + s2;
72
73 return ls;
74 }
75 function union(ls1, ls2) {
76 var acc = ls2;
77
78 for (; ls1; ls1 = ls1.next)
79 if (!member(ls1.data, ls2))
80 acc = cons(ls1.data, acc);
81
82 return acc;
83 }
84 function length(ls) {
85 var acc = 0;
86
87 for (; ls; ls = ls.next)
88 ++acc;
89
90 return acc;
91 }
92
93
94 // Error handling
95
96 function whine(msg) {
97 alert(msg);
98 throw msg;
99 }
100
101 function pf(loc) {
102 throw ("Pattern match failure (" + loc + ")");
103 }
104
105 function runHandlers(kind, ls, arg) {
106 if (ls == null)
107 alert(kind + ": " + arg);
108 for (; ls; ls = ls.next)
109 try {
110 exec({c:"a", f:{c:"a", f:ls.data, x:{c:"c", v:arg}}, x:{c:"c", v:null}});
111 } catch (v) { }
112 }
113
114 var errorHandlers = null;
115
116 function onError(f) {
117 errorHandlers = cons(f, errorHandlers);
118 }
119
120 function er(s) {
121 runHandlers("Error", errorHandlers, s);
122 throw {uw_error: s};
123 }
124
125 var failHandlers = null;
126
127 function onFail(f) {
128 failHandlers = cons(f, failHandlers);
129 }
130
131 function doExn(v) {
132 if (v == null || v.uw_error == null) {
133 var s = (v == null ? "null" : v.message ? v.message : v.toString());
134 if (v != null && v.fileName && v.lineNumber)
135 s += " (" + v.fileName + ":" + v.lineNumber + ")";
136 runHandlers("Fail", failHandlers, s);
137 }
138 }
139
140 var disconnectHandlers = null;
141
142 function flift(f) {
143 return {env:cons(f,null), body:{c:"v", n:1}};
144 }
145
146 function onDisconnect(f) {
147 disconnectHandlers = cons(flift(f), disconnectHandlers);
148 }
149
150 function discon() {
151 runHandlers("Disconnect", disconnectHandlers, null);
152 }
153
154 var connectHandlers = null;
155
156 function onConnectFail(f) {
157 connectHandlers = cons(flift(f), connectHandlers);
158 }
159
160 function conn() {
161 runHandlers("Connect", connectHandlers, null);
162 }
163
164 var serverHandlers = null;
165
166 function onServerError(f) {
167 serverHandlers = cons(f, serverHandlers);
168 }
169
170 function servErr(s) {
171 window.setTimeout(function () { runHandlers("Server", serverHandlers, s); }, 0);
172 }
173
174
175 // Embedding closures in XML strings
176
177 function cs(f) {
178 return {closure: f};
179 }
180
181 function isWeird(v) {
182 return v.closure != null || v.cat1 != null;
183 }
184
185 function cat(s1, s2) {
186 if (isWeird(s1) || isWeird(s2))
187 return {cat1: s1, cat2: s2};
188 else
189 return s1 + s2;
190 } 190 }
191 191
192 var closures = []; 192 var closures = [];
193 var freeClosures = null; 193 var freeClosures = null;
194 194
195 function newClosure(f) { 195 function newClosure(f) {
196 var n; 196 var n;
197 if (freeClosures == null) { 197 if (freeClosures == null) {
198 n = closures.length; 198 n = closures.length;
199 } else { 199 } else {
200 n = freeClosures.data; 200 n = freeClosures.data;
201 freeClosures = freeClosures.next; 201 freeClosures = freeClosures.next;
202 } 202 }
203 closures[n] = f; 203 closures[n] = f;
204 return n; 204 return n;
205 } 205 }
206 206
207 function freeClosure(n) { 207 function freeClosure(n) {
208 closures[n] = null; 208 closures[n] = null;
209 freeClosures = cons(n, freeClosures); 209 freeClosures = cons(n, freeClosures);
210 } 210 }
211 211
212 function cr(n) { 212 function cr(n) {
213 return closures[n]; 213 return closures[n];
214 } 214 }
215 215
216 function flatten(cls, tr) { 216 function flatten(cls, tr) {
217 if (tr.cat1 != null) 217 if (tr.cat1 != null)
218 return flatten(cls, tr.cat1) + flatten(cls, tr.cat2); 218 return flatten(cls, tr.cat1) + flatten(cls, tr.cat2);
219 else if (tr.closure != null) { 219 else if (tr.closure != null) {
220 var cl = newClosure(tr.closure); 220 var cl = newClosure(tr.closure);
221 cls.v = cons(cl, cls.v); 221 cls.v = cons(cl, cls.v);
222 return "cr(" + cl + ")"; 222 return "cr(" + cl + ")";
223 } else 223 } else
224 return tr; 224 return tr;
225 } 225 }
226 226
227 function flattenLocal(s) { 227 function flattenLocal(s) {
228 var cls = {v : null}; 228 var cls = {v : null};
229 var r = flatten(cls, s); 229 var r = flatten(cls, s);
230 for (cl = cls.v; cl != null; cl = cl.next) 230 for (cl = cls.v; cl != null; cl = cl.next)
231 freeClosure(cl.data); 231 freeClosure(cl.data);
232 return r; 232 return r;
233 } 233 }
234 234
235 235
236 236
237 // Dynamic tree management 237 // Dynamic tree management
238 238
239 function populate(node) { 239 function populate(node) {
240 var s = node.signal; 240 var s = node.signal;
241 var oldSources = node.sources; 241 var oldSources = node.sources;
242 try { 242 try {
243 var sr = execF(s, null); 243 var sr = execF(s, null);
244 var newSources = sr._sources; 244 var newSources = sr._sources;
245 245
246 for (var sp = oldSources; sp; sp = sp.next) 246 for (var sp = oldSources; sp; sp = sp.next)
247 if (!member(sp.data, newSources)) 247 if (!member(sp.data, newSources))
248 sp.data.dyns = remove(node, sp.data.dyns); 248 sp.data.dyns = remove(node, sp.data.dyns);
249 249
250 for (var sp = newSources; sp; sp = sp.next) 250 for (var sp = newSources; sp; sp = sp.next)
251 if (!member(sp.data, oldSources)) 251 if (!member(sp.data, oldSources))
252 sp.data.dyns = cons(node, sp.data.dyns); 252 sp.data.dyns = cons(node, sp.data.dyns);
253 253
254 node.sources = newSources; 254 node.sources = newSources;
255 node.recreate(sr._data); 255 node.recreate(sr._data);
256 } catch (v) { 256 } catch (v) {
257 doExn(v); 257 doExn(v);
258 } 258 }
259 } 259 }
260 260
261 function sc(v) { 261 function sc(v) {
262 return {data : v, dyns : null}; 262 return {data : v, dyns : null};
263 } 263 }
264 function sv(s, v) { 264 function sv(s, v) {
265 s.data = v; 265 s.data = v;
266 266
267 for (var ls = s.dyns; ls; ls = ls.next) 267 for (var ls = s.dyns; ls; ls = ls.next)
268 if (!ls.dead) 268 if (!ls.dead)
269 populate(ls.data); 269 populate(ls.data);
270 } 270 }
271 function sg(s) { 271 function sg(s) {
272 return s.data; 272 return s.data;
273 } 273 }
274 274
275 function ss(s) { 275 function ss(s) {
276 return {env:cons(s, null), body:{c:"r", l: 276 return {env:cons(s, null), body:{c:"r", l:
277 cons({n:"sources", v:{c:"c", v:cons(s, null)}}, 277 cons({n:"sources", v:{c:"c", v:cons(s, null)}},
278 cons({n:"data", v:{c:"f", f:sg, a:cons({c:"v", n:1}, null)}}, null))}}; 278 cons({n:"data", v:{c:"f", f:sg, a:cons({c:"v", n:1}, null)}}, null))}};
279 } 279 }
280 function sr(v) { 280 function sr(v) {
281 return {env:null, body:{c:"c", v:{_sources : null, _data : v}}}; 281 return {env:null, body:{c:"c", v:{_sources : null, _data : v}}};
282 } 282 }
283 function sb(x,y) { 283 function sb(x,y) {
284 return {env:cons(y,cons(x,null)), 284 return {env:cons(y,cons(x,null)),
285 body:{c:"=", 285 body:{c:"=",
286 e1:{c:"a", f:{c:"v", n:2}, x:{c:"c", v:null}}, 286 e1:{c:"a", f:{c:"v", n:2}, x:{c:"c", v:null}},
287 e2:{c:"=", 287 e2:{c:"=",
288 e1:{c:"a", 288 e1:{c:"a",
289 f:{c:"a", f:{c:"v", n:2}, x:{c:".", r:{c:"v", n:0}, f:"data"}}, 289 f:{c:"a", f:{c:"v", n:2}, x:{c:".", r:{c:"v", n:0}, f:"data"}},
290 x:{c:"c", v:null}}, 290 x:{c:"c", v:null}},
291 e2:{c:"r", l:cons( 291 e2:{c:"r", l:cons(
292 {n:"sources", v:{c:"f", f:union, a:cons({c:".", r:{c:"v", n:1}, f:"sources"}, 292 {n:"sources", v:{c:"f", f:union, a:cons({c:".", r:{c:"v", n:1}, f:"sources"},
293 cons({c:".", r:{c:"v", n:0}, f:"sources"}, null))}}, 293 cons({c:".", r:{c:"v", n:0}, f:"sources"}, null))}},
294 cons({n:"data", v:{c:".", r:{c:"v", n:0}, f:"data"}}, null))}}}}; 294 cons({n:"data", v:{c:".", r:{c:"v", n:0}, f:"data"}}, null))}}}};
295 } 295 }
296 function scur(s) { 296 function scur(s) {
297 return execF(s, null)._data; 297 return execF(s, null)._data;
298 } 298 }
299 299
300 function lastParent() { 300 function lastParent() {
301 var pos = document.body; 301 var pos = document.body;
302 302
303 while (pos.lastChild && pos.lastChild.nodeType == 1) 303 while (pos.lastChild && pos.lastChild.nodeType == 1)
304 pos = pos.lastChild; 304 pos = pos.lastChild;
305 305
306 pos = pos.parentNode; 306 pos = pos.parentNode;
307 307
308 return pos; 308 return pos;
309 } 309 }
310 310
311 function parent() { 311 function parent() {
312 return thisScript ? thisScript.parentNode : lastParent(); 312 return thisScript ? thisScript.parentNode : lastParent();
313 } 313 }
314 314
315 function addNode(node) { 315 function addNode(node) {
316 if (thisScript) 316 if (thisScript)
317 thisScript.parentNode.replaceChild(node, thisScript); 317 thisScript.parentNode.replaceChild(node, thisScript);
318 else 318 else
319 lastParent().appendChild(node); 319 lastParent().appendChild(node);
320 } 320 }
321 321
322 var thisScript = null; 322 var thisScript = null;
323 323
324 function runScripts(node) { 324 function runScripts(node) {
325 if (node.getElementsByTagName) { 325 if (node.getElementsByTagName) {
326 var savedScript = thisScript; 326 var savedScript = thisScript;
327 327
328 var scripts = node.getElementsByTagName("script"), scriptsCopy = []; 328 var scripts = node.getElementsByTagName("script"), scriptsCopy = [];
329 var len = scripts.length; 329 var len = scripts.length;
330 for (var i = 0; i < len; ++i) 330 for (var i = 0; i < len; ++i)
331 scriptsCopy[i] = scripts[i]; 331 scriptsCopy[i] = scripts[i];
332 for (var i = 0; i < len; ++i) { 332 for (var i = 0; i < len; ++i) {
333 thisScript = scriptsCopy[i]; 333 thisScript = scriptsCopy[i];
334 334
335 try { 335 try {
336 eval(thisScript.text); 336 eval(thisScript.text);
337 } catch (v) { 337 } catch (v) {
338 doExn(v); 338 doExn(v);
339 } 339 }
340 if (thisScript.parentNode) 340 if (thisScript.parentNode)
341 thisScript.parentNode.removeChild(thisScript); 341 thisScript.parentNode.removeChild(thisScript);
342 } 342 }
343 343
344 thisScript = savedScript; 344 thisScript = savedScript;
345 } 345 }
346 } 346 }
347 347
348 348
349 // Dynamic tree entry points 349 // Dynamic tree entry points
350 350
351 function killScript(scr) { 351 function killScript(scr) {
352 scr.dead = true; 352 scr.dead = true;
353 for (var ls = scr.sources; ls; ls = ls.next) 353 for (var ls = scr.sources; ls; ls = ls.next)
354 ls.data.dyns = remove(scr, ls.data.dyns); 354 ls.data.dyns = remove(scr, ls.data.dyns);
355 for (var ls = scr.closures; ls; ls = ls.next) 355 for (var ls = scr.closures; ls; ls = ls.next)
356 freeClosure(ls.data); 356 freeClosure(ls.data);
357 } 357 }
358 358
359 function dyn(pnode, s) { 359 function dyn(pnode, s) {
360 var x = document.createElement("script"); 360 var x = document.createElement("script");
361 x.dead = false; 361 x.dead = false;
362 x.signal = s; 362 x.signal = s;
363 x.sources = null; 363 x.sources = null;
364 x.closures = null; 364 x.closures = null;
365 365
366 var firstChild = null; 366 var firstChild = null;
367 367
368 x.recreate = function(v) { 368 x.recreate = function(v) {
369 for (var ls = x.closures; ls; ls = ls.next) 369 for (var ls = x.closures; ls; ls = ls.next)
370 freeClosure(ls.data); 370 freeClosure(ls.data);
371
372 var next;
373 for (var child = firstChild; child && child != x; child = next) {
374 next = child.nextSibling;
375
376 killScript(child);
377 if (child.getElementsByTagName) {
378 var arr = child.getElementsByTagName("script");
379 for (var i = 0; i < arr.length; ++i)
380 killScript(arr[i]);
381 }
382
383 if (child.parentNode)
384 child.parentNode.removeChild(child);
385 }
386
387 var cls = {v : null};
388 var html = flatten(cls, v);
389 if (pnode != "table" && pnode != "tr")
390 html = dynPrefix + html;
391 x.closures = cls.v;
392
393 if (pnode == "table") {
394 var dummy = document.createElement("body");
395 dummy.innerHTML = "<table>" + html + "</table>";
396 runScripts(dummy);
397 var table = x.parentNode;
398
399 if (table) {
400 firstChild = null;
401 var tbody;
402
403 var arr = dummy.getElementsByTagName("tbody");
404
405 var tbody;
406 if (arr.length > 0 && arr[0].parentNode == dummy.firstChild) {
407 tbody = arr[0];
408 var next;
409 for (var node = dummy.firstChild.firstChild; node; node = next) {
410 next = node.nextSibling;
411
412 if (node.tagName != "TBODY")
413 tbody.appendChild(node);
414 }
415 } else
416 tbody = dummy.firstChild;
417 371
418 var next; 372 var next;
419 firstChild = document.createElement("script"); 373 for (var child = firstChild; child && child != x; child = next) {
420 table.insertBefore(firstChild, x); 374 next = child.nextSibling;
421 for (var node = tbody.firstChild; node; node = next) { 375
422 next = node.nextSibling; 376 killScript(child);
423 table.insertBefore(node, x); 377 if (child.getElementsByTagName) {
378 var arr = child.getElementsByTagName("script");
379 for (var i = 0; i < arr.length; ++i)
380 killScript(arr[i]);
381 }
382
383 if (child.parentNode)
384 child.parentNode.removeChild(child);
424 } 385 }
425 } 386
426 } else if (pnode == "tr") { 387 var cls = {v : null};
427 var dummy = document.createElement("body"); 388 var html = flatten(cls, v);
428 dummy.innerHTML = "<table><tr>" + html + "</tr></table>"; 389 if (pnode != "table" && pnode != "tr")
429 runScripts(dummy); 390 html = dynPrefix + html;
430 var table = x.parentNode; 391 x.closures = cls.v;
431 392
432 if (table) { 393 if (pnode == "table") {
433 var arr = dummy.getElementsByTagName("tr"); 394 var dummy = document.createElement("body");
434 firstChild = null; 395 dummy.innerHTML = "<table>" + html + "</table>";
435 var tr; 396 runScripts(dummy);
436 if (arr.length > 0 && table != null) 397 var table = x.parentNode;
437 tr = arr[0]; 398
438 else 399 if (table) {
439 tr = dummy.firstChild; 400 firstChild = null;
440 401 var tbody;
441 var next; 402
442 firstChild = document.createElement("script"); 403 var arr = dummy.getElementsByTagName("tbody");
443 table.insertBefore(firstChild, x); 404
444 for (var node = tr.firstChild; node; node = next) { 405 var tbody;
445 next = node.nextSibling; 406 if (arr.length > 0 && arr[0].parentNode == dummy.firstChild) {
446 table.insertBefore(node, x); 407 tbody = arr[0];
408 var next;
409 for (var node = dummy.firstChild.firstChild; node; node = next) {
410 next = node.nextSibling;
411
412 if (node.tagName != "TBODY")
413 tbody.appendChild(node);
414 }
415 } else
416 tbody = dummy.firstChild;
417
418 var next;
419 firstChild = document.createElement("script");
420 table.insertBefore(firstChild, x);
421 for (var node = tbody.firstChild; node; node = next) {
422 next = node.nextSibling;
423 table.insertBefore(node, x);
424 }
425 }
426 } else if (pnode == "tr") {
427 var dummy = document.createElement("body");
428 dummy.innerHTML = "<table><tr>" + html + "</tr></table>";
429 runScripts(dummy);
430 var table = x.parentNode;
431
432 if (table) {
433 var arr = dummy.getElementsByTagName("tr");
434 firstChild = null;
435 var tr;
436 if (arr.length > 0 && table != null)
437 tr = arr[0];
438 else
439 tr = dummy.firstChild;
440
441 var next;
442 firstChild = document.createElement("script");
443 table.insertBefore(firstChild, x);
444 for (var node = tr.firstChild; node; node = next) {
445 next = node.nextSibling;
446 table.insertBefore(node, x);
447 }
448 }
449 } else {
450 firstChild = document.createElement("span");
451 firstChild.innerHTML = html;
452 runScripts(firstChild);
453 if (x.parentNode)
454 x.parentNode.insertBefore(firstChild, x);
447 } 455 }
448 } 456 };
449 } else { 457
450 firstChild = document.createElement("span"); 458 addNode(x);
451 firstChild.innerHTML = html; 459 populate(x);
452 runScripts(firstChild);
453 if (x.parentNode)
454 x.parentNode.insertBefore(firstChild, x);
455 }
456 };
457
458 addNode(x);
459 populate(x);
460 } 460 }
461 461
462 function input(x, s, recreate, type) { 462 function input(x, s, recreate, type) {
463 if (type) x.type = type; 463 if (type) x.type = type;
464 x.dead = false; 464 x.dead = false;
465 x.signal = ss(s); 465 x.signal = ss(s);
466 x.sources = null; 466 x.sources = null;
467 x.recreate = recreate(x); 467 x.recreate = recreate(x);
468 addNode(x); 468 addNode(x);
469 populate(x); 469 populate(x);
470 470
471 return x; 471 return x;
472 } 472 }
473 473
474 function inp(s) { 474 function inp(s) {
475 var x = input(document.createElement("input"), s, 475 var x = input(document.createElement("input"), s,
476 function(x) { return function(v) { if (x.value != v) x.value = v; }; }); 476 function(x) { return function(v) { if (x.value != v) x.value = v; }; });
477 x.value = s.data; 477 x.value = s.data;
478 x.onkeyup = function() { sv(s, x.value) }; 478 x.onkeyup = function() { sv(s, x.value) };
479 479
480 return x; 480 return x;
481 } 481 }
482 482
483 function sel(s, content) { 483 function sel(s, content) {
484 var dummy = document.createElement("span"); 484 var dummy = document.createElement("span");
485 dummy.innerHTML = "<select>" + content + "</select>"; 485 dummy.innerHTML = "<select>" + content + "</select>";
486 var x = input(dummy.firstChild, s, function(x) { return function(v) { if (x.value != v) x.value = v; }; }); 486 var x = input(dummy.firstChild, s, function(x) { return function(v) { if (x.value != v) x.value = v; }; });
487 x.value = s.data; 487 x.value = s.data;
488 if (x.value != s.data) 488 if (x.value != s.data)
489 sv(s, x.value); 489 sv(s, x.value);
490 x.onchange = function() { sv(s, x.value) }; 490 x.onchange = function() { sv(s, x.value) };
491 491
492 return x; 492 return x;
493 } 493 }
494 494
495 function chk(s) { 495 function chk(s) {
496 var x = input(document.createElement("input"), s, 496 var x = input(document.createElement("input"), s,
497 function(x) { return function(v) { if (x.checked != v) x.checked = v; }; }, "checkbox"); 497 function(x) { return function(v) { if (x.checked != v) x.checked = v; }; }, "checkbox");
498 x.defaultChecked = x.checked = s.data; 498 x.defaultChecked = x.checked = s.data;
499 x.onclick = function() { sv(s, x.checked) }; 499 x.onclick = function() { sv(s, x.checked) };
500 500
501 return x; 501 return x;
502 } 502 }
503 503
504 function tbx(s) { 504 function tbx(s) {
505 var x = input(document.createElement("textarea"), s, 505 var x = input(document.createElement("textarea"), s,
506 function(x) { return function(v) { if (x.innerHTML != v) x.innerHTML = v; }; }); 506 function(x) { return function(v) { if (x.innerHTML != v) x.innerHTML = v; }; });
507 x.innerHTML = s.data; 507 x.innerHTML = s.data;
508 x.onkeyup = function() { sv(s, x.value) }; 508 x.onkeyup = function() { sv(s, x.value) };
509 509
510 return x; 510 return x;
511 } 511 }
512 512
513 function addOnChange(x, f) { 513 function addOnChange(x, f) {
514 var old = x.onchange; 514 var old = x.onchange;
515 x.onchange = function() { old(); f (); }; 515 x.onchange = function() { old(); f (); };
516 } 516 }
517 517
518 518
519 // Basic string operations 519 // Basic string operations
520 520
521 function eh(x) { 521 function eh(x) {
522 if (x == null) 522 if (x == null)
523 return "NULL"; 523 return "NULL";
524 else 524 else
525 return x.split("&").join("&amp;").split("<").join("&lt;").split(">").join("&gt;"); 525 return x.split("&").join("&amp;").split("<").join("&lt;").split(">").join("&gt;");
526 } 526 }
527 527
528 function ts(x) { return x.toString() } 528 function ts(x) { return x.toString() }
529 function bs(b) { return (b ? "True" : "False") } 529 function bs(b) { return (b ? "True" : "False") }
530 530
531 function id(x) { return x; } 531 function id(x) { return x; }
532 function sub(s, i) { return s.charAt(i); } 532 function sub(s, i) { return s.charAt(i); }
533 function suf(s, i) { return s.substring(i); } 533 function suf(s, i) { return s.substring(i); }
534 function slen(s) { return s.length; } 534 function slen(s) { return s.length; }
535 function sidx(s, ch) { 535 function sidx(s, ch) {
536 var r = s.indexOf(ch); 536 var r = s.indexOf(ch);
537 if (r == -1) 537 if (r == -1)
538 return null;
539 else
540 return r;
541 }
542 function sspn(s, chs) {
543 for (var i = 0; i < s.length; ++i)
544 if (chs.indexOf(s.charAt(i)) != -1)
545 return i;
546
538 return null; 547 return null;
539 else
540 return r;
541 }
542 function sspn(s, chs) {
543 for (var i = 0; i < s.length; ++i)
544 if (chs.indexOf(s.charAt(i)) != -1)
545 return i;
546
547 return null;
548 } 548 }
549 function schr(s, ch) { 549 function schr(s, ch) {
550 var r = s.indexOf(ch); 550 var r = s.indexOf(ch);
551 if (r == -1) 551 if (r == -1)
552 return null; 552 return null;
553 else 553 else
554 return s.substring(r); 554 return s.substring(r);
555 } 555 }
556 function ssub(s, start, len) { 556 function ssub(s, start, len) {
557 return s.substring(start, start+len); 557 return s.substring(start, start+len);
558 } 558 }
559 559
560 function pi(s) { 560 function pi(s) {
561 var r = parseInt(s); 561 var r = parseInt(s);
562 if (r.toString() == s) 562 if (r.toString() == s)
563 return r; 563 return r;
564 else 564 else
565 er("Can't parse int: " + s); 565 er("Can't parse int: " + s);
566 } 566 }
567 567
568 function pfl(s) { 568 function pfl(s) {
569 var r = parseFloat(s); 569 var r = parseFloat(s);
570 if (r.toString() == s) 570 if (r.toString() == s)
571 return r; 571 return r;
572 else 572 else
573 er("Can't parse float: " + s); 573 er("Can't parse float: " + s);
574 } 574 }
575 575
576 function pio(s) { 576 function pio(s) {
577 var r = parseInt(s); 577 var r = parseInt(s);
578 if (r.toString() == s) 578 if (r.toString() == s)
579 return r; 579 return r;
580 else 580 else
581 return null; 581 return null;
582 } 582 }
583 583
584 function pflo(s) { 584 function pflo(s) {
585 var r = parseFloat(s); 585 var r = parseFloat(s);
586 if (r.toString() == s) 586 if (r.toString() == s)
587 return r; 587 return r;
588 else 588 else
589 return null; 589 return null;
590 } 590 }
591 591
592 function uf(s) { 592 function uf(s) {
593 if (s.length == 0) 593 if (s.length == 0)
594 return "_"; 594 return "_";
595 s = s.replace(new RegExp ("\\.", "g"), ".2E"); 595 s = s.replace(new RegExp ("\\.", "g"), ".2E");
596 return (s.charAt(0) == '_' ? "_" : "") + encodeURIComponent(s).replace(new RegExp ("%", "g"), "."); 596 return (s.charAt(0) == '_' ? "_" : "") + encodeURIComponent(s).replace(new RegExp ("%", "g"), ".");
597 } 597 }
598 598
599 function uu(s) { 599 function uu(s) {
600 if (s.length > 0 && s.charAt(0) == '_') { 600 if (s.length > 0 && s.charAt(0) == '_') {
601 s = s.substring(1); 601 s = s.substring(1);
602 } else if (s.length >= 3 && (s.charAt(0) == '%' || s.charAt(0) == '.') 602 } else if (s.length >= 3 && (s.charAt(0) == '%' || s.charAt(0) == '.')
603 && s.charAt(1) == '5' && (s.charAt(2) == 'f' || s.charAt(2) == 'F')) 603 && s.charAt(1) == '5' && (s.charAt(2) == 'f' || s.charAt(2) == 'F'))
604 s = s.substring(3); 604 s = s.substring(3);
605 s = s.replace(new RegExp ("\\+", "g"), " "); 605 s = s.replace(new RegExp ("\\+", "g"), " ");
606 s = s.replace(new RegExp ("\\.", "g"), "%"); 606 s = s.replace(new RegExp ("\\.", "g"), "%");
607 return decodeURIComponent(s); 607 return decodeURIComponent(s);
608 } 608 }
609 609
610 function atr(s) { 610 function atr(s) {
611 return s.replace(new RegExp ("\"", "g"), "&quot;").replace(new RegExp ("&", "g"), "&amp;") 611 return s.replace(new RegExp ("\"", "g"), "&quot;").replace(new RegExp ("&", "g"), "&amp;")
612 } 612 }
613 613
614 function ub(b) { 614 function ub(b) {
615 return b ? "1" : "0"; 615 return b ? "1" : "0";
616 } 616 }
617 617
618 function uul(getToken, getData) { 618 function uul(getToken, getData) {
619 var tok = getToken(); 619 var tok = getToken();
620 if (tok == "Nil") { 620 if (tok == "Nil") {
621 return null; 621 return null;
622 } else if (tok == "Cons") { 622 } else if (tok == "Cons") {
623 var d = getData(); 623 var d = getData();
624 var l = uul(getToken, getData); 624 var l = uul(getToken, getData);
625 return {_1:d, _2:l}; 625 return {_1:d, _2:l};
626 } else 626 } else
627 whine("Can't unmarshal list (" + tok + ")"); 627 whine("Can't unmarshal list (" + tok + ")");
628 } 628 }
629 629
630 function strcmp(str1, str2) { 630 function strcmp(str1, str2) {
631 return ((str1 == str2) ? 0 : ((str1 > str2) ? 1 : -1)); 631 return ((str1 == str2) ? 0 : ((str1 > str2) ? 1 : -1));
632 } 632 }
633 633
634 634
635 // Remote calls 635 // Remote calls
636 636
639 var url_prefix = "/"; 639 var url_prefix = "/";
640 var timeout = 60; 640 var timeout = 60;
641 641
642 function getXHR(uri) 642 function getXHR(uri)
643 { 643 {
644 try {
645 return new XMLHttpRequest();
646 } catch (e) {
647 try { 644 try {
648 return new ActiveXObject("Msxml2.XMLHTTP"); 645 return new XMLHttpRequest();
649 } catch (e) { 646 } catch (e) {
650 try { 647 try {
651 return new ActiveXObject("Microsoft.XMLHTTP"); 648 return new ActiveXObject("Msxml2.XMLHTTP");
652 } catch (e) { 649 } catch (e) {
653 whine("Your browser doesn't seem to support AJAX."); 650 try {
654 } 651 return new ActiveXObject("Microsoft.XMLHTTP");
655 } 652 } catch (e) {
656 } 653 whine("Your browser doesn't seem to support AJAX.");
654 }
655 }
656 }
657 } 657 }
658 658
659 var sig = null; 659 var sig = null;
660 660
661 var unloading = false, inFlight = null; 661 var unloading = false, inFlight = null;
662 662
663 function unload() { 663 function unload() {
664 unloading = true; 664 unloading = true;
665 665
666 for (; inFlight; inFlight = inFlight.next) { 666 for (; inFlight; inFlight = inFlight.next) {
667 inFlight.data.abort(); 667 inFlight.data.abort();
668 } 668 }
669 } 669 }
670 670
671 function requestUri(xhr, uri, needsSig) { 671 function requestUri(xhr, uri, needsSig) {
672 if (unloading) 672 if (unloading)
673 return; 673 return;
674 674
675 xhr.open("POST", uri, true); 675 xhr.open("POST", uri, true);
676 xhr.setRequestHeader("Content-type", "text/plain"); 676 xhr.setRequestHeader("Content-type", "text/plain");
677 xhr.setRequestHeader("Content-length", "0"); 677 xhr.setRequestHeader("Content-length", "0");
678 xhr.setRequestHeader("Connection", "close"); 678 xhr.setRequestHeader("Connection", "close");
679 679
680 if (client_id != null) { 680 if (client_id != null) {
681 xhr.setRequestHeader("UrWeb-Client", client_id.toString()); 681 xhr.setRequestHeader("UrWeb-Client", client_id.toString());
682 xhr.setRequestHeader("UrWeb-Pass", client_pass.toString()); 682 xhr.setRequestHeader("UrWeb-Pass", client_pass.toString());
683 } 683 }
684 684
685 if (needsSig) { 685 if (needsSig) {
686 if (sig == null) 686 if (sig == null)
687 whine("Missing cookie signature!"); 687 whine("Missing cookie signature!");
688 688
689 xhr.setRequestHeader("UrWeb-Sig", sig); 689 xhr.setRequestHeader("UrWeb-Sig", sig);
690 } 690 }
691 691
692 inFlight = cons(xhr, inFlight); 692 inFlight = cons(xhr, inFlight);
693 xhr.send(null); 693 xhr.send(null);
694 } 694 }
695 695
696 function xhrFinished(xhr) { 696 function xhrFinished(xhr) {
697 xhr.abort(); 697 xhr.abort();
698 inFlight = remove(xhr, inFlight); 698 inFlight = remove(xhr, inFlight);
699 } 699 }
700 700
701 function unurlify(parse, s) { 701 function unurlify(parse, s) {
702 return parse(s); 702 return parse(s);
703 } 703 }
704 704
705 function rc(prefix, uri, parse, k, needsSig) { 705 function rc(prefix, uri, parse, k, needsSig) {
706 uri = cat(prefix, uri); 706 uri = cat(prefix, uri);
707 uri = flattenLocal(uri); 707 uri = flattenLocal(uri);
708 var xhr = getXHR(); 708 var xhr = getXHR();
709 709
710 xhr.onreadystatechange = function() { 710 xhr.onreadystatechange = function() {
711 if (xhr.readyState == 4) { 711 if (xhr.readyState == 4) {
712 var isok = false; 712 var isok = false;
713 713
714 try { 714 try {
715 if (xhr.status == 200) 715 if (xhr.status == 200)
716 isok = true; 716 isok = true;
717 } catch (e) { } 717 } catch (e) { }
718 718
719 if (isok) { 719 if (isok) {
720 try {
721 k(parse(xhr.responseText));
722 } catch (v) {
723 doExn(v);
724 }
725 } else {
726 conn();
727 }
728
729 xhrFinished(xhr);
730 }
731 };
732
733 requestUri(xhr, uri, needsSig);
734 }
735
736 function path_join(s1, s2) {
737 if (s1.length > 0 && s1.charAt(s1.length-1) == '/')
738 return s1 + s2;
739 else
740 return s1 + "/" + s2;
741 }
742
743 var channels = [];
744
745 function newQueue() {
746 return { front : null, back : null };
747 }
748 function enqueue(q, v) {
749 if (q.front == null) {
750 q.front = cons(v, null);
751 q.back = q.front;
752 } else {
753 var node = cons(v, null);
754 q.back.next = node;
755 q.back = node;
756 }
757 }
758 function dequeue(q) {
759 if (q.front == null)
760 return null;
761 else {
762 var r = q.front.data;
763 q.front = q.front.next;
764 if (q.front == null)
765 q.back = null;
766 return r;
767 }
768 }
769
770 function newChannel() {
771 return { msgs : newQueue(), listeners : newQueue() };
772 }
773
774 function listener() {
775 var uri = path_join(url_prefix, ".msgs");
776 var xhr = getXHR();
777 var tid, orsc, onTimeout;
778
779 var connect = function () {
780 xhr.onreadystatechange = orsc;
781 tid = window.setTimeout(onTimeout, timeout * 500);
782 requestUri(xhr, uri, false);
783 }
784
785 orsc = function() {
786 if (xhr.readyState == 4) {
787 window.clearTimeout(tid);
788
789 var isok = false;
790
791 try {
792 if (xhr.status == 200)
793 isok = true;
794 } catch (e) { }
795
796 if (isok) {
797 var text = xhr.responseText
798 if (text == "")
799 return;
800 var lines = text.split("\n");
801
802 if (lines.length < 2) {
803 discon();
804 return;
805 }
806
807 for (var i = 0; i+1 < lines.length; i += 2) {
808 var chn = lines[i];
809 var msg = lines[i+1];
810
811 if (chn < 0)
812 whine("Out-of-bounds channel in message from remote server");
813
814 var ch;
815
816 if (chn >= channels.length || channels[chn] == null) {
817 ch = newChannel();
818 channels[chn] = ch;
819 } else
820 ch = channels[chn];
821
822 var listener = dequeue(ch.listeners);
823 if (listener == null) {
824 enqueue(ch.msgs, msg);
825 } else {
826 try {
827 listener(msg);
828 } catch (v) {
829 doExn(v);
830 }
831 }
832 }
833
834 xhrFinished(xhr);
835
836 connect();
837 }
838 else {
839 try {
840 if (xhr.status != 0)
841 servErr("Error querying remote server for messages: " + xhr.status);
842 } catch (e) { }
843 }
844 }
845 };
846
847 onTimeout = function() {
848 xhrFinished(xhr);
849 connect();
850 };
851
852 connect();
853 }
854
855 function rv(chn, parse, k) {
856 if (chn == null)
857 return;
858
859 if (chn < 0)
860 whine("Out-of-bounds channel receive");
861
862 var ch;
863
864 if (chn >= channels.length || channels[chn] == null) {
865 ch = newChannel();
866 channels[chn] = ch;
867 } else
868 ch = channels[chn];
869
870 var msg = dequeue(ch.msgs);
871 if (msg == null) {
872 enqueue(ch.listeners, function(msg) { k(parse(msg)); });
873 } else {
720 try { 874 try {
721 k(parse(xhr.responseText)); 875 k(parse(msg));
722 } catch (v) { 876 } catch (v) {
723 doExn(v); 877 doExn(v);
724 } 878 }
725 } else { 879 }
726 conn(); 880 }
727 } 881
728 882 function sl(ms, k) {
729 xhrFinished(xhr); 883 window.setTimeout(function() { k(null); }, ms);
730 } 884 }
731 }; 885
732 886 function sp(e) {
733 requestUri(xhr, uri, needsSig); 887 execF(e, null);
734 } 888 }
735 889
736 function path_join(s1, s2) { 890
737 if (s1.length > 0 && s1.charAt(s1.length-1) == '/') 891 // Key events
738 return s1 + s2; 892
739 else 893 var uw_event = null;
740 return s1 + "/" + s2; 894
741 } 895 function kc() {
742 896 return window.event ? uw_event.keyCode : uw_event.which;
743 var channels = []; 897 }
744 898
745 function newQueue() { 899
746 return { front : null, back : null }; 900 // The Ur interpreter
747 } 901
748 function enqueue(q, v) { 902 var urfuncs = [];
749 if (q.front == null) { 903
750 q.front = cons(v, null); 904 function lookup(env, n) {
751 q.back = q.front; 905 while (env != null) {
752 } else { 906 if (n == 0)
753 var node = cons(v, null); 907 return env.data;
754 q.back.next = node; 908 else {
755 q.back = node; 909 --n;
756 } 910 env = env.next;
757 }
758 function dequeue(q) {
759 if (q.front == null)
760 return null;
761 else {
762 var r = q.front.data;
763 q.front = q.front.next;
764 if (q.front == null)
765 q.back = null;
766 return r;
767 }
768 }
769
770 function newChannel() {
771 return { msgs : newQueue(), listeners : newQueue() };
772 }
773
774 function listener() {
775 var uri = path_join(url_prefix, ".msgs");
776 var xhr = getXHR();
777 var tid, orsc, onTimeout;
778
779 var connect = function () {
780 xhr.onreadystatechange = orsc;
781 tid = window.setTimeout(onTimeout, timeout * 500);
782 requestUri(xhr, uri, false);
783 }
784
785 orsc = function() {
786 if (xhr.readyState == 4) {
787 window.clearTimeout(tid);
788
789 var isok = false;
790
791 try {
792 if (xhr.status == 200)
793 isok = true;
794 } catch (e) { }
795
796 if (isok) {
797 var text = xhr.responseText
798 if (text == "")
799 return;
800 var lines = text.split("\n");
801
802 if (lines.length < 2) {
803 discon();
804 return;
805 } 911 }
806 912 }
807 for (var i = 0; i+1 < lines.length; i += 2) { 913
808 var chn = lines[i]; 914 whine("Out-of-bounds Ur variable reference");
809 var msg = lines[i+1]; 915 }
810 916
811 if (chn < 0) 917 function execP(env, p, v) {
812 whine("Out-of-bounds channel in message from remote server"); 918 switch (p.c) {
813 919 case "w":
814 var ch; 920 return env;
815 921 case "v":
816 if (chn >= channels.length || channels[chn] == null) { 922 return cons(v, env);
817 ch = newChannel(); 923 case "c":
818 channels[chn] = ch; 924 if (v == p.v)
819 } else 925 return env;
820 ch = channels[chn]; 926 else
821 927 return false;
822 var listener = dequeue(ch.listeners); 928 case "s":
823 if (listener == null) { 929 if (v == null)
824 enqueue(ch.msgs, msg); 930 return false;
825 } else { 931 else
826 try { 932 return execP(env, p.p, p.n ? v.v : v);
827 listener(msg); 933 case "1":
828 } catch (v) { 934 if (v.n != p.n)
829 doExn(v); 935 return false;
936 else
937 return execP(env, p.p, v.v);
938 case "r":
939 for (var fs = p.l; fs != null; fs = fs.next) {
940 env = execP(env, fs.data.p, v["_" + fs.data.n]);
941 if (env == false)
942 return false;
943 }
944 return env;
945 default:
946 whine("Unknown Ur pattern kind" + p.c);
947 }
948 }
949
950 function exec0(env, e) {
951 return exec1(env, null, e);
952 }
953
954 function exec1(env, stack, e) {
955 var stack, usedK = false;
956
957 var saveEnv = function() {
958 if (stack.next != null && stack.next.data.c != "<")
959 stack = cons({c: "<", env: env}, stack.next);
960 else
961 stack = stack.next;
962 };
963
964 while (true) {
965 switch (e.c) {
966 case "c":
967 var v = e.v;
968 if (stack == null)
969 return v;
970 var fr = stack.data;
971
972 switch (fr.c) {
973 case "s":
974 e = {c: "c", v: {v: v}};
975 stack = stack.next;
976 break;
977 case "1":
978 e = {c: "c", v: {n: fr.n, v: v}};
979 stack = stack.next;
980 break;
981 case "f":
982 fr.args[fr.pos++] = v;
983 if (fr.a == null) {
984 var res;
985 stack = stack.next;
986
987 if (fr.f.apply)
988 res = fr.f.apply(null, fr.args);
989 else if (fr.args.length == 0)
990 res = fr.f();
991 else if (fr.args.length == 1)
992 res = fr.f(fr.args[0]);
993 else if (fr.args.length == 2)
994 res = fr.f(fr.args[0], fr.args[1]);
995 else if (fr.args.length == 3)
996 res = fr.f(fr.args[0], fr.args[1], fr.args[2]);
997 else if (fr.args.length == 4)
998 res = fr.f(fr.args[0], fr.args[1], fr.args[2], fr.args[3]);
999 else if (fr.args.length == 5)
1000 res = fr.f(fr.args[0], fr.args[1], fr.args[2], fr.args[3], fr.args[4]);
1001 else
1002 whine("Native function has " + fr.args.length + " args, but there is no special case for that count.");
1003
1004 e = {c: "c", v: res};
1005 if (usedK) return null;
1006 } else {
1007 e = fr.a.data;
1008 fr.a = fr.a.next;
1009 }
1010 break;
1011 case "a1":
1012 e = fr.x;
1013 stack = cons({c: "a2", f: v}, stack.next);
1014 break;
1015 case "a2":
1016 if (fr.f == null)
1017 whine("Ur: applying null function");
1018 else if (fr.f.body) {
1019 saveEnv();
1020 env = cons(v, fr.f.env);
1021 e = fr.f.body;
1022 } else {
1023 e = {c: "c", v: fr.f(v)};
1024 stack = stack.next;
1025 }
1026 break;
1027 case "<":
1028 env = fr.env;
1029 stack = stack.next;
1030 break;
1031 case "r":
1032 fr.fs["_" + fr.n] = v;
1033 if (fr.l == null) {
1034 e = {c: "c", v: fr.fs};
1035 stack = stack.next;
1036 } else {
1037 fr.n = fr.l.data.n;
1038 e = fr.l.data.v;
1039 fr.l = fr.l.next;
1040 }
1041 break;
1042 case ".":
1043 e = {c: "c", v: v["_" + fr.f]};
1044 stack = stack.next;
1045 break;
1046 case ";":
1047 e = fr.e2;
1048 stack = stack.next;
1049 break;
1050 case "=":
1051 saveEnv();
1052 env = cons(v, env);
1053 e = fr.e2;
1054 break;
1055 case "m":
1056 var ps;
1057 for (ps = fr.p; ps != null; ps = ps.next) {
1058 var r = execP(env, ps.data.p, v);
1059 if (r != false) {
1060 saveEnv();
1061 env = r;
1062 e = ps.data.b;
1063 break;
1064 }
1065 }
1066 if (ps == null)
1067 whine("Match failure in Ur interpretation");
1068 break;
1069 default:
1070 whine("Unknown Ur continuation kind " + fr.c);
830 } 1071 }
831 } 1072
1073 break;
1074 case "v":
1075 e = {c: "c", v: lookup(env, e.n)};
1076 break;
1077 case "n":
1078 var idx = e.n;
1079 e = urfuncs[idx];
1080 if (e.c == "t")
1081 e = urfuncs[idx] = e.f();
1082 break;
1083 case "s":
1084 stack = cons({c: "s"}, stack);
1085 e = e.v;
1086 break;
1087 case "1":
1088 stack = cons({c: "1", n: e.n}, stack);
1089 e = e.v;
1090 break;
1091 case "f":
1092 if (e.a == null)
1093 e = {c: "c", v: (eval(e.f))()};
1094 else {
1095 var args = [];
1096 stack = cons({c: "f", f: eval(e.f), args: args, pos: 0, a: e.a.next}, stack);
1097 if (!e.a.data.c) alert("[2] fr.f = " + e.f + "; 0 = " + e.a.data);
1098 e = e.a.data;
1099 }
1100 break;
1101 case "l":
1102 e = {c: "c", v: {env: env, body: e.b}};
1103 break;
1104 case "a":
1105 stack = cons({c: "a1", x: e.x}, stack);
1106 e = e.f;
1107 break;
1108 case "r":
1109 if (e.l == null)
1110 whine("Empty Ur record in interpretation");
1111 var fs = {};
1112 stack = cons({c: "r", n: e.l.data.n, fs: fs, l: e.l.next}, stack);
1113 e = e.l.data.v;
1114 break;
1115 case ".":
1116 stack = cons({c: ".", f: e.f}, stack);
1117 e = e.r;
1118 break;
1119 case ";":
1120 stack = cons({c: ";", e2: e.e2}, stack);
1121 e = e.e1;
1122 break;
1123 case "=":
1124 stack = cons({c: "=", e2: e.e2}, stack);
1125 e = e.e1;
1126 break;
1127 case "m":
1128 stack = cons({c: "m", p: e.p}, stack);
1129 e = e.e;
1130 break;
1131 case "e":
1132 e = {c: "c", v: cs({c: "wc", env: env, body: e.e})};
1133 break;
1134 case "wc":
1135 env = e.env;
1136 e = e.body;
1137 break;
1138 case "K":
1139 { var savedStack = stack.next, savedEnv = env;
1140 e = {c: "c", v: function(v) { return exec1(savedEnv, savedStack, {c: "c", v: v}); } };}
1141 usedK = true;
1142 break;
1143 default:
1144 whine("Unknown Ur expression kind " + e.c);
832 } 1145 }
833 1146 }
834 xhrFinished(xhr); 1147 }
835 1148
836 connect(); 1149 function execD(e) {
837 } 1150 return exec0(null, e);
838 else { 1151 }
839 try { 1152
840 if (xhr.status != 0) 1153 function exec(e) {
841 servErr("Error querying remote server for messages: " + xhr.status); 1154 var r = exec0(null, e);
842 } catch (e) { } 1155
843 } 1156 if (r != null && r.body != null)
844 } 1157 return function(v) { return exec0(cons(v, r.env), r.body); };
845 };
846
847 onTimeout = function() {
848 xhrFinished(xhr);
849 connect();
850 };
851
852 connect();
853 }
854
855 function rv(chn, parse, k) {
856 if (chn == null)
857 return;
858
859 if (chn < 0)
860 whine("Out-of-bounds channel receive");
861
862 var ch;
863
864 if (chn >= channels.length || channels[chn] == null) {
865 ch = newChannel();
866 channels[chn] = ch;
867 } else
868 ch = channels[chn];
869
870 var msg = dequeue(ch.msgs);
871 if (msg == null) {
872 enqueue(ch.listeners, function(msg) { k(parse(msg)); });
873 } else {
874 try {
875 k(parse(msg));
876 } catch (v) {
877 doExn(v);
878 }
879 }
880 }
881
882 function sl(ms, k) {
883 window.setTimeout(function() { k(null); }, ms);
884 }
885
886 function sp(e) {
887 execF(e, null);
888 }
889
890
891 // Key events
892
893 var uw_event = null;
894
895 function kc() {
896 return window.event ? uw_event.keyCode : uw_event.which;
897 }
898
899
900 // The Ur interpreter
901
902 var urfuncs = [];
903
904 function lookup(env, n) {
905 while (env != null) {
906 if (n == 0)
907 return env.data;
908 else {
909 --n;
910 env = env.next;
911 }
912 }
913
914 whine("Out-of-bounds Ur variable reference");
915 }
916
917 function execP(env, p, v) {
918 switch (p.c) {
919 case "w":
920 return env;
921 case "v":
922 return cons(v, env);
923 case "c":
924 if (v == p.v)
925 return env;
926 else 1158 else
927 return false; 1159 return r;
928 case "s":
929 if (v == null)
930 return false;
931 else
932 return execP(env, p.p, p.n ? v.v : v);
933 case "1":
934 if (v.n != p.n)
935 return false;
936 else
937 return execP(env, p.p, v.v);
938 case "r":
939 for (var fs = p.l; fs != null; fs = fs.next) {
940 env = execP(env, fs.data.p, v["_" + fs.data.n]);
941 if (env == false)
942 return false;
943 }
944 return env;
945 default:
946 whine("Unknown Ur pattern kind" + p.c);
947 }
948 }
949
950 function exec0(env, e) {
951 return exec1(env, null, e);
952 }
953
954 function exec1(env, stack, e) {
955 var stack, usedK = false;
956
957 var saveEnv = function() {
958 if (stack.next != null && stack.next.data.c != "<")
959 stack = cons({c: "<", env: env}, stack.next);
960 else
961 stack = stack.next;
962 };
963
964 while (true) {
965 switch (e.c) {
966 case "c":
967 var v = e.v;
968 if (stack == null)
969 return v;
970 var fr = stack.data;
971
972 switch (fr.c) {
973 case "s":
974 e = {c: "c", v: {v: v}};
975 stack = stack.next;
976 break;
977 case "1":
978 e = {c: "c", v: {n: fr.n, v: v}};
979 stack = stack.next;
980 break;
981 case "f":
982 fr.args[fr.pos++] = v;
983 if (fr.a == null) {
984 var res;
985 stack = stack.next;
986
987 if (fr.f.apply)
988 res = fr.f.apply(null, fr.args);
989 else if (fr.args.length == 0)
990 res = fr.f();
991 else if (fr.args.length == 1)
992 res = fr.f(fr.args[0]);
993 else if (fr.args.length == 2)
994 res = fr.f(fr.args[0], fr.args[1]);
995 else if (fr.args.length == 3)
996 res = fr.f(fr.args[0], fr.args[1], fr.args[2]);
997 else if (fr.args.length == 4)
998 res = fr.f(fr.args[0], fr.args[1], fr.args[2], fr.args[3]);
999 else if (fr.args.length == 5)
1000 res = fr.f(fr.args[0], fr.args[1], fr.args[2], fr.args[3], fr.args[4]);
1001 else
1002 whine("Native function has " + fr.args.length + " args, but there is no special case for that count.");
1003
1004 e = {c: "c", v: res};
1005 if (usedK) return null;
1006 } else {
1007 e = fr.a.data;
1008 fr.a = fr.a.next;
1009 }
1010 break;
1011 case "a1":
1012 e = fr.x;
1013 stack = cons({c: "a2", f: v}, stack.next);
1014 break;
1015 case "a2":
1016 if (fr.f == null)
1017 whine("Ur: applying null function");
1018 else if (fr.f.body) {
1019 saveEnv();
1020 env = cons(v, fr.f.env);
1021 e = fr.f.body;
1022 } else {
1023 e = {c: "c", v: fr.f(v)};
1024 stack = stack.next;
1025 }
1026 break;
1027 case "<":
1028 env = fr.env;
1029 stack = stack.next;
1030 break;
1031 case "r":
1032 fr.fs["_" + fr.n] = v;
1033 if (fr.l == null) {
1034 e = {c: "c", v: fr.fs};
1035 stack = stack.next;
1036 } else {
1037 fr.n = fr.l.data.n;
1038 e = fr.l.data.v;
1039 fr.l = fr.l.next;
1040 }
1041 break;
1042 case ".":
1043 e = {c: "c", v: v["_" + fr.f]};
1044 stack = stack.next;
1045 break;
1046 case ";":
1047 e = fr.e2;
1048 stack = stack.next;
1049 break;
1050 case "=":
1051 saveEnv();
1052 env = cons(v, env);
1053 e = fr.e2;
1054 break;
1055 case "m":
1056 var ps;
1057 for (ps = fr.p; ps != null; ps = ps.next) {
1058 var r = execP(env, ps.data.p, v);
1059 if (r != false) {
1060 saveEnv();
1061 env = r;
1062 e = ps.data.b;
1063 break;
1064 }
1065 }
1066 if (ps == null)
1067 whine("Match failure in Ur interpretation");
1068 break;
1069 default:
1070 whine("Unknown Ur continuation kind " + fr.c);
1071 }
1072
1073 break;
1074 case "v":
1075 e = {c: "c", v: lookup(env, e.n)};
1076 break;
1077 case "n":
1078 e = urfuncs[e.n];
1079 break;
1080 case "s":
1081 stack = cons({c: "s"}, stack);
1082 e = e.v;
1083 break;
1084 case "1":
1085 stack = cons({c: "1", n: e.n}, stack);
1086 e = e.v;
1087 break;
1088 case "f":
1089 if (e.a == null)
1090 e = {c: "c", v: (eval(e.f))()};
1091 else {
1092 var args = [];
1093 stack = cons({c: "f", f: eval(e.f), args: args, pos: 0, a: e.a.next}, stack);
1094 if (!e.a.data.c) alert("[2] fr.f = " + e.f + "; 0 = " + e.a.data);
1095 e = e.a.data;
1096 }
1097 break;
1098 case "l":
1099 e = {c: "c", v: {env: env, body: e.b}};
1100 break;
1101 case "a":
1102 stack = cons({c: "a1", x: e.x}, stack);
1103 e = e.f;
1104 break;
1105 case "r":
1106 if (e.l == null)
1107 whine("Empty Ur record in interpretation");
1108 var fs = {};
1109 stack = cons({c: "r", n: e.l.data.n, fs: fs, l: e.l.next}, stack);
1110 e = e.l.data.v;
1111 break;
1112 case ".":
1113 stack = cons({c: ".", f: e.f}, stack);
1114 e = e.r;
1115 break;
1116 case ";":
1117 stack = cons({c: ";", e2: e.e2}, stack);
1118 e = e.e1;
1119 break;
1120 case "=":
1121 stack = cons({c: "=", e2: e.e2}, stack);
1122 e = e.e1;
1123 break;
1124 case "m":
1125 stack = cons({c: "m", p: e.p}, stack);
1126 e = e.e;
1127 break;
1128 case "e":
1129 e = {c: "c", v: cs({c: "wc", env: env, body: e.e})};
1130 break;
1131 case "wc":
1132 env = e.env;
1133 e = e.body;
1134 break;
1135 case "K":
1136 { var savedStack = stack.next, savedEnv = env;
1137 e = {c: "c", v: function(v) { return exec1(savedEnv, savedStack, {c: "c", v: v}); } };}
1138 usedK = true;
1139 break;
1140 default:
1141 whine("Unknown Ur expression kind " + e.c);
1142 }
1143 }
1144 }
1145
1146 function execD(e) {
1147 return exec0(null, e);
1148 }
1149
1150 function exec(e) {
1151 var r = exec0(null, e);
1152
1153 if (r != null && r.body != null)
1154 return function(v) { return exec0(cons(v, r.env), r.body); };
1155 else
1156 return r;
1157 } 1160 }
1158 1161
1159 function execF(f, x) { 1162 function execF(f, x) {
1160 return exec0(cons(x, f.env), f.body); 1163 return exec0(cons(x, f.env), f.body);
1161 } 1164 }
1162 1165
1163 1166
1164 // App-specific code 1167 // App-specific code
1165 1168