wl.js (12115B)
1 // wl.js -- (c) 2009 Tomas Hlavaty 2 3 function wl() { 4 var Me = this; 5 6 function Cons(Car, Cdr) { 7 this.car = Car; 8 this.cdr = Cdr; 9 return this; 10 } 11 function isCons(X) {return X && X.constructor === Cons;} 12 function cons(Car, Cdr) {return new Cons(Car, Cdr);} 13 14 function Sym(Nm, Val, Prop) { 15 this.nm = Nm; 16 this.car = Prop; 17 this.cdr = Val; 18 return this; 19 } 20 function isSym(X) {return X && X.constructor === Sym;} 21 function sym(X) {return X.nm;} 22 23 function car(X) {return X.car;} 24 function cdr(X) {return X.cdr;} 25 26 var NIL = new Sym("NIL"); 27 NIL.car = NIL; 28 NIL.cdr = NIL; 29 30 var Qte = {}; 31 var Lp = {}; 32 var Rp = {}; 33 34 // reader 35 function init(L) { 36 Me.L = L; 37 Me.N = L.length; 38 Me.I = 0; 39 Me.D = 0; 40 } 41 function charIn(C, L) {return 0 <= L.indexOf(C);} 42 function peek() {return Me.I < Me.N && Me.L[Me.I];} 43 function xchar() {return Me.I < Me.N && Me.L[Me.I++];} 44 function skip() { 45 while(Me.I < Me.N && charIn(Me.L[Me.I], " \t\n")) 46 Me.I++; 47 } 48 function comment() { 49 while("#" == peek()) { 50 while(peek() && "\n" != peek()) 51 xchar(); 52 skip(); 53 } 54 } 55 function text() { 56 var L = []; 57 while(peek() != '"') { 58 var C = xchar(); 59 if(C == "\\") C = xchar(); 60 else if(C == "^") { 61 C = xchar(); 62 if(C == "I") C = "\t"; 63 else if(C == "J") C = "\n"; 64 else if(C == "M") C = "\r"; 65 else C = String.fromCharCode(C == "?" ? 127 : C & 0x1f); 66 } 67 L.push(C); 68 } 69 if(xchar() != '"') throw "Unbalanced double quote"; 70 if(0 < L.length) return L.join(""); //.replace(/\r\n/g, "\n"); 71 return ""; 72 } 73 function symbol() { 74 var C = xchar(); 75 if(charIn(C, "()# \t\n")) throw "Symbol expected, got " + C; 76 var N = charIn(C, "+-0123456789."); 77 var F = "." == C; 78 var L = [C]; 79 while(peek() && !charIn(peek(), "()# \t\n")) { 80 C = xchar(); 81 L.push(C); 82 if(N && !charIn(C, "0123456789")) { 83 if(!F && "." == C) F = true; 84 else N = false; 85 } 86 } 87 L = L.join(""); 88 if(1 == L.length && charIn(L, "+-.")) N = false; 89 return N ? (F ? parseFloat(L) : parseInt(L, 10)) : new Sym(L); 90 } 91 function token() { 92 skip(); 93 switch(peek()) { 94 case false: return undefined; 95 case "#": return comment(); 96 case '"': xchar(); return text(); 97 case "'": xchar(); return Qte; 98 case "(": xchar(); Me.D++; return Lp; 99 case ")": xchar(); Me.D--; return Rp; 100 default: return symbol(); 101 } 102 } 103 104 // parser 105 function parse(L) { 106 init(L); 107 var Os = [Lp]; 108 function build() { 109 var Z = NIL; 110 var Dot; 111 while(0 < Os.length) { 112 var X = Os.pop(); 113 if(Lp === X) return Dot ? cons(Dot, cons(Z, NIL)) : Z; 114 Dot = false; 115 if(isSym(X) && "." == sym(X)) { 116 if(NIL === cdr(Z)) { 117 Z = car(Z); 118 Dot = X; 119 } else throw "Bad dotted pair"; 120 } else Z = cons(X, Z); 121 } 122 throw "Missing mark"; 123 } 124 while(peek()) { 125 var X = token(); 126 if(X) { 127 if(Rp === X) Os.push(build()); 128 else Os.push(X); 129 } 130 } 131 Z = build(); 132 if(0 < Os.length) throw "Incomplete input, left with " + Os; 133 return Z; 134 } 135 136 // printer 137 function str(L) { 138 var A = []; 139 if(typeof L == "number") A.push(L); 140 else if(isSym(L)) A.push(sym(L)); 141 else if(isCons(L)) { 142 A.push("("); 143 while(isCons(L)) { 144 A.push(str(car(L))); 145 L = cdr(L); 146 if(NIL !== L) A.push(" "); 147 } 148 if(NIL !== L) { 149 A.push(". "); 150 A.push(str(L)); 151 } 152 A.push(")"); 153 } else { 154 var Y = L.split(""); 155 for(var I = 0; I < Y.length; I++) { 156 if(Y[I] == "\\") Y[I] = "\\\\"; 157 else if(Y[I] == '"') Y[I] = '\\"'; 158 } 159 var S = Y.join(""); 160 //var S = L.replace(/\"/g, "\\\"").replace(/\\/g, "\\\\"); 161 A.push('"' + S + '"'); 162 } 163 return A.join(""); 164 } 165 166 wl.prototype.parse = parse; 167 wl.prototype.str = str; 168 return this; 169 } 170 171 172 173 174 175 176 // function wl() { 177 // var Sd = {}; 178 // var Rd = new wlR; 179 180 // Sd["car"] = function car(E) {return E.car;}; 181 // Sd["cdr"] = function cdr(E) {return E.cdr;}; 182 // Sd["cons"] = function cons(E) {return Rd.cons(E.car, E.cdr);}; 183 // Sd["reverse"] = function reverse(E) { 184 // var Z = NIL; 185 // while(!isNil(E)) { 186 // Z = cons(E.car, Z); 187 // E = E.cdr; 188 // } 189 // return Z; 190 // }; 191 // Sd["member"] = function member(E) { 192 // var F = E.car; 193 // var R = E.cdr; 194 // while(!isNil(R)) { 195 // if(!isNil(eq(F, R.car))) return R; 196 // R = R.cdr; 197 // } 198 // return NIL; 199 // }; 200 // // Sd["caar"] = function caar(E) {return car(car(E));}; 201 // // Sd["cadr"] = function cadr(E) {return car(cdr(E));}; 202 // // Sd["cdar"] = function cdar(E) {return cdr(car(E));}; 203 // // Sd["cddr"] = function cddr(E) {return cdr(cdr(E));}; 204 // // Sd["caddr"] = function caddr(E) {return car(cdr(cdr(E)));}; 205 // // Sd["cdddr"] = function cdddr(E) {return cdr(cdr(cdr(E)));}; 206 // // Sd["cadddr"] = function cadddr(E) {return car(cdr(cdr(cdr(E))));}; 207 // // Sd["cddddr"] = function cddddr(E) {return cdr(cdr(cdr(cdr(E))));}; 208 209 // Sd["-"] = function(E) {var X = Os.pop(); Os.push(Os.pop() - X);}; 210 // Sd["*"] = function(E) {Os.push(Os.pop() * Os.pop());}; 211 // Sd["/"] = function(E) {var X = Os.pop(); Os.push(Os.pop() / X);}; 212 // Sd["%"] = function(E) {var X = Os.pop(); Os.push(Os.pop() % X);}; 213 214 // Sd["eq"] = function() {var Y = Os.pop(); var X = Os.pop(); Os.push(X == Y);}; 215 // Sd["lt"] = function() {var Y = Os.pop(); var X = Os.pop(); Os.push(X < Y);}; 216 217 // Sd["if"] = function() { 218 // var N = Os.pop(); 219 // var P = Os.pop(); 220 // var C = Os.pop(); 221 // Es.push([false, C === true ? P : N]); 222 // }; 223 224 // // Sd[".call"] = function() { 225 // // var N = Os.pop(); 226 // // var K = Os.pop(); 227 // // var D = Os.pop(); 228 // // var X = []; 229 // // for(var I = 0; I < N; I++) X.unshift(Os.pop()); 230 // // Os.push(D[K].apply(D, X)); 231 // // }; 232 // Sd[".math"] = function(E) {return Math;}; 233 // Sd[".date"] = function(E) {return new Date();}; // TODO split new and Date 234 // Sd[".window"] = function(E) {return window;}; 235 // // Sd[".callback"] = function() { // TODO event arg? 236 // // var X = Os.pop(); 237 // // Os.push(function() { 238 // // Ps.run(X, true); 239 // // while(0 < Es.length) 240 // // Ps.step(); 241 // // }); 242 // // }; 243 244 245 // Sd["list2array"] = function list2array(E) { 246 // var Z = []; 247 // while(!isNil(E)) { 248 // Z.push(E.car); 249 // E = E.cdr; 250 // } 251 // return Z; 252 // }; 253 // // Sd["array2list"] = function array2list(A) { 254 // // var Z = NIL; 255 // // for(var I = A.length - 1; 0 <= I; I--) 256 // // Z = cons(A[I], Z); 257 // // return Z; 258 // // }; 259 // // Sd["object2list"] = function object2list(O) { 260 // // var Z = NIL; 261 // // for(var I in O) 262 // // Z = cons(cons(I, O[I]), Z); 263 // // return Z; 264 // // }; 265 266 // function parse() { 267 // var Z; 268 // var A = arguments; 269 // if(A.length) 270 // for(var I = 0; I < A.length; I++) 271 // Z = Rd.parse(A[I]); 272 // else Z = Rd.parse(A); 273 // return Z; 274 // } 275 // wl.prototype.parse = parse; 276 // return this; 277 // } 278 279 // /// lisp 280 281 // // function format(N, S) { 282 // // var X = "" + N; 283 // // var L = X.length; 284 // // return X.slice(0, L - S) + "." + X.slice(L - S, L); 285 // // } 286 287 // // function real(N, S) { 288 // // return N / Math.pow(10, S); 289 // // } 290 291 // var Syms = {}; 292 293 // function mkSym(Nm, Val, Prop) { 294 // var X = new Sym(Nm, Val, Prop); 295 // Syms[Nm] = X; 296 // return X; 297 // } 298 299 // function xget(Sym) { 300 // return isSym(Sym) ? Sym._val : Sym; 301 // } 302 303 // function xset(Sym, Val) { 304 // if(!isSym(Sym)) throw "Sym expected"; 305 // Sym._val = Val; 306 // return Sym._val; 307 // } 308 309 // //var NIL = mkSym("NIL"); 310 // var T = mkSym("T"); 311 312 // //xset(NIL, NIL); 313 // xset(T, T); 314 // // TODO set props for NIL and T 315 316 // function intern(Sym) { 317 // //if(!(Sym in Syms)) Syms[Sym] = mkSym(Sym, NIL, NIL); 318 // return Syms[Sym] || (Syms[Sym] = mkSym(Sym, NIL, NIL)); 319 // } 320 321 322 // function isT(X) { 323 // return X === T; 324 // } 325 326 327 328 329 330 331 332 333 // function eq(X, Y) { 334 // if(X === Y) return T; 335 // //if(X == Y) return T; 336 // return NIL; 337 // } 338 339 // function xdelete(A, L) { 340 // var X = NIL; 341 // while(!isNil(L)) { 342 // if(isNil(eq(A, L.car))) X = cons(L.car, X); 343 // L = L.cdr; 344 // } 345 // return reverse(X); 346 // } 347 348 // function lsApply(Fn, Args) { 349 // } 350 351 // function jsFn(Fn) { 352 // var F = function() { 353 // return lsApply(Fn, list2array(arguments)); 354 // }; 355 // return F; 356 // } 357 358 // function jsApply() { 359 // var Fn = arguments.shift(); 360 // var Args = arguments.shift(); 361 // var Rest = arguments; 362 // return Fn.apply(list2array(Args).unshift.apply(list2array(Rest))); 363 // } 364 365 // var Xeval = {}; 366 367 // function xdef(Nm, Fn) { 368 // if(!(Nm in Syms)) intern(Nm); 369 // Xeval[Nm] = Fn; 370 // } 371 372 // xdef("quote", function(E) { 373 // return cdr(E); // or cdr? 374 // }); 375 // xdef("if", function(E) { 376 // if(!isNil(xeval(cadr(E)))) return xeval(caddr(E)); 377 // else return xeval(cadddr(E)); 378 // }); 379 // xdef("prog", function(E) { 380 // var L = cdr(E); 381 // var X = NIL; 382 // while(!isNil(L)) { 383 // X = xeval(L.car); 384 // L = L.cdr; 385 // } 386 // return X; 387 // }); 388 389 // function xeval(E) { 390 // if(isSym(E)) return xget(E); 391 // else if(!isCons(E)) return E; 392 // else if(car(E)._nm in Xeval) return Xeval[car(E)._nm](E); 393 // else return xapply(xeval(car(E)), map(xeval, cdr(E))); 394 // } 395 396 // function initLisp() { 397 // //xalert(xeval(NIL), xeval(T), xeval(12), xeval(12.3), xeval({x: "hi"}), xeval([1, 2])); 398 // //xalert(xeval(cons(intern("quote"), cons("whoa", NIL)))); 399 // //xalert(xeval(cons(intern("quote"), cons("whoa", cons("b", NIL))))); 400 // //xalert(intern("if")); 401 // //xalert(object2list(Syms)); 402 // //xalert(object2list(Xeval)); 403 // //xalert(xeval(cons(intern("if"), cons(T, cons("yes", cons("no", NIL)))))); 404 // //xalert(xeval(cons(intern("if"), cons(NIL, cons("yes", cons("no", NIL)))))); 405 // //xalert(xeval(cons(intern("quote"), intern("whoa")))); 406 // } 407 408 // // parser 409 410 // function isArray(A) { 411 // return A && A.constructor == Array; 412 // } 413 414 // function flatten(A) { 415 // var X = []; 416 // function rec(B) { 417 // var N = B.length; 418 // var I; 419 // for(I = 0; I < N; I++) { 420 // if(isArray(B[I])) rec(B[I]); 421 // else X.push(B[I]); 422 // } 423 // } 424 // rec(A); 425 // return X; 426 // } 427 428 // function lisp2string(Any) { 429 // return flatten(unparse(Any)).join(""); 430 // } 431 432 // function xalert() { 433 // var X = []; 434 // var N = arguments.length; 435 // var I; 436 // for(I = 0; I < N; I++) { 437 // X.push(flatten(unparse(arguments[I])).join("")); 438 // } 439 // alert(X.join(" ")); 440 // } 441 442 // function xmsg(A) { 443 // var B = parse(A); 444 // var X = flatten(unparse(B)).join(""); 445 // mk(w("test"), "pre", {}, {}, "'" + A + "' => " + X); 446 // }