├── README.markdown └── arc3.1 ├── ac.scm ├── app.arc ├── arc.arc ├── array.arc ├── as.scm ├── between0.arc ├── blog.arc ├── brackets.scm ├── code.arc ├── copyright ├── extend-readtable0.arc ├── extend0.arc ├── how-to-run-news ├── html.arc ├── js.arc ├── libs.arc ├── news.arc ├── person.arc ├── pprint.arc ├── prompt.arc ├── scheme0.arc ├── skipwhite1.arc ├── srv.arc ├── static ├── arc.png ├── grayarrow.gif ├── graydown.gif ├── jquery.js ├── lis.py.txt ├── robots.txt ├── s.gif ├── sweet-example.html ├── sweet-example.js ├── sweet.coffee ├── sweet.html ├── sweet.js └── underscore.js ├── strings.arc └── table-rw3.arc /README.markdown: -------------------------------------------------------------------------------- 1 | # SweetScript 2 | 3 | *SweetScript has died and been reborn as [LavaScript](https://github.com/evanrmurphy/lava-script). Seek your lispy-javascript goodness there!* 4 | 5 | A lispy language that compiles into JavaScript, strongly influenced by Arc. 6 | 7 | ## Install and Run 8 | 9 | SweetScript runs on a modified version of [arc3.1](http://arclanguage.org/item?id=10254). After installing [racket](http://racket-lang.org/download/) (previously called mzscheme): 10 | 11 | git clone git@github.com:evanrmurphy/SweetScript.git 12 | cd SweetScript/arc3.1 13 | racket -f as.scm 14 | 15 | You should find yourself at the `arc>` prompt. Enter `(sweet)` to use SweetScript: 16 | 17 | arc> (sweet) 18 | Welcome to SweetScript! Type (sour) to leave. 19 | sweet> (def hello () 20 | (alert "hello world!")) 21 | hello=(function(){return alert('hello world!');}); 22 | sweet> (sour) 23 | Bye! 24 | nil 25 | arc> 26 | -------------------------------------------------------------------------------- /arc3.1/app.arc: -------------------------------------------------------------------------------- 1 | ; Application Server. Layer inserted 2 Sep 06. 2 | 3 | ; ideas: 4 | ; def a general notion of apps of which prompt is one, news another 5 | ; give each user a place to store data? A home dir? 6 | 7 | ; A user is simply a string: "pg". Use /whoami to test user cookie. 8 | 9 | (= hpwfile* "arc/hpw" 10 | oidfile* "arc/openids" 11 | adminfile* "arc/admins" 12 | cookfile* "arc/cooks") 13 | 14 | (def asv ((o port 8080)) 15 | (load-userinfo) 16 | (serve port)) 17 | 18 | (def load-userinfo () 19 | (= hpasswords* (safe-load-table hpwfile*) 20 | openids* (safe-load-table oidfile*) 21 | admins* (map string (errsafe (readfile adminfile*))) 22 | cookie->user* (safe-load-table cookfile*)) 23 | (maptable (fn (k v) (= (user->cookie* v) k)) 24 | cookie->user*)) 25 | 26 | ; idea: a bidirectional table, so don't need two vars (and sets) 27 | 28 | (= cookie->user* (table) user->cookie* (table) logins* (table)) 29 | 30 | (def get-user (req) 31 | (let u (aand (alref req!cooks "user") (cookie->user* (sym it))) 32 | (when u (= (logins* u) req!ip)) 33 | u)) 34 | 35 | (mac when-umatch (user req . body) 36 | `(if (is ,user (get-user ,req)) 37 | (do ,@body) 38 | (mismatch-message))) 39 | 40 | (def mismatch-message () 41 | (prn "Dead link: users don't match.")) 42 | 43 | (mac when-umatch/r (user req . body) 44 | `(if (is ,user (get-user ,req)) 45 | (do ,@body) 46 | "mismatch")) 47 | 48 | (defop mismatch req (mismatch-message)) 49 | 50 | (mac uform (user req after . body) 51 | `(aform (fn (,req) 52 | (when-umatch ,user ,req 53 | ,after)) 54 | ,@body)) 55 | 56 | (mac urform (user req after . body) 57 | `(arform (fn (,req) 58 | (when-umatch/r ,user ,req 59 | ,after)) 60 | ,@body)) 61 | 62 | ; Like onlink, but checks that user submitting the request is the 63 | ; same it was generated for. For extra protection could log the 64 | ; username and ip addr of every genlink, and check if they match. 65 | 66 | (mac ulink (user text . body) 67 | (w/uniq req 68 | `(linkf ,text (,req) 69 | (when-umatch ,user ,req ,@body)))) 70 | 71 | 72 | (defop admin req (admin-gate (get-user req))) 73 | 74 | (def admin-gate (u) 75 | (if (admin u) 76 | (admin-page u) 77 | (login-page 'login nil 78 | (fn (u ip) (admin-gate u))))) 79 | 80 | (def admin (u) (and u (mem u admins*))) 81 | 82 | (def user-exists (u) (and u (hpasswords* u) u)) 83 | 84 | (def admin-page (user . msg) 85 | (whitepage 86 | (prbold "Admin: ") 87 | (hspace 20) 88 | (pr user " | ") 89 | (w/link (do (logout-user user) 90 | (whitepage (pr "Bye " user "."))) 91 | (pr "logout")) 92 | (when msg (hspace 10) (map pr msg)) 93 | (br2) 94 | (aform (fn (req) 95 | (when-umatch user req 96 | (with (u (arg req "u") p (arg req "p")) 97 | (if (or (no u) (no p) (is u "") (is p "")) 98 | (pr "Bad data.") 99 | (user-exists u) 100 | (admin-page user "User already exists: " u) 101 | (do (create-acct u p) 102 | (admin-page user)))))) 103 | (pwfields "create (server) account")))) 104 | 105 | (def cook-user (user) 106 | (let id (new-user-cookie) 107 | (= (cookie->user* id) user 108 | (user->cookie* user) id) 109 | (save-table cookie->user* cookfile*) 110 | id)) 111 | 112 | ; Unique-ids are only unique per server invocation. 113 | 114 | (def new-user-cookie () 115 | (let id (unique-id) 116 | (if (cookie->user* id) (new-user-cookie) id))) 117 | 118 | (def logout-user (user) 119 | (wipe (logins* user)) 120 | (wipe (cookie->user* (user->cookie* user)) (user->cookie* user)) 121 | (save-table cookie->user* cookfile*)) 122 | 123 | (def create-acct (user pw) 124 | (set (dc-usernames* (downcase user))) 125 | (set-pw user pw)) 126 | 127 | (def disable-acct (user) 128 | (set-pw user (rand-string 20)) 129 | (logout-user user)) 130 | 131 | (def set-pw (user pw) 132 | (= (hpasswords* user) (and pw (shash pw))) 133 | (save-table hpasswords* hpwfile*)) 134 | 135 | (def hello-page (user ip) 136 | (whitepage (prs "hello" user "at" ip))) 137 | 138 | (defop login req (login-page 'login)) 139 | 140 | ; switch is one of: register, login, both 141 | 142 | ; afterward is either a function on the newly created username and 143 | ; ip address, in which case it is called to generate the next page 144 | ; after a successful login, or a pair of (function url), which means 145 | ; call the function, then redirect to the url. 146 | 147 | ; classic example of something that should just "return" a val 148 | ; via a continuation rather than going to a new page. 149 | 150 | (def login-page (switch (o msg nil) (o afterward hello-page)) 151 | (whitepage 152 | (pagemessage msg) 153 | (when (in switch 'login 'both) 154 | (login-form "Login" switch login-handler afterward) 155 | (hook 'login-form afterward) 156 | (br2)) 157 | (when (in switch 'register 'both) 158 | (login-form "Create Account" switch create-handler afterward)))) 159 | 160 | (def login-form (label switch handler afterward) 161 | (prbold label) 162 | (br2) 163 | (fnform (fn (req) (handler req switch afterward)) 164 | (fn () (pwfields (downcase label))) 165 | (acons afterward))) 166 | 167 | (def login-handler (req switch afterward) 168 | (logout-user (get-user req)) 169 | (aif (good-login (arg req "u") (arg req "p") req!ip) 170 | (login it req!ip (user->cookie* it) afterward) 171 | (failed-login switch "Bad login." afterward))) 172 | 173 | (def create-handler (req switch afterward) 174 | (logout-user (get-user req)) 175 | (with (user (arg req "u") pw (arg req "p")) 176 | (aif (bad-newacct user pw) 177 | (failed-login switch it afterward) 178 | (do (create-acct user pw) 179 | (login user req!ip (cook-user user) afterward))))) 180 | 181 | (def login (user ip cookie afterward) 182 | (= (logins* user) ip) 183 | (prcookie cookie) 184 | (if (acons afterward) 185 | (let (f url) afterward 186 | (f user ip) 187 | url) 188 | (do (prn) 189 | (afterward user ip)))) 190 | 191 | (def failed-login (switch msg afterward) 192 | (if (acons afterward) 193 | (flink (fn ignore (login-page switch msg afterward))) 194 | (do (prn) 195 | (login-page switch msg afterward)))) 196 | 197 | (def prcookie (cook) 198 | (prn "Set-Cookie: user=" cook "; expires=Sun, 17-Jan-2038 19:14:07 GMT")) 199 | 200 | (def pwfields ((o label "login")) 201 | (inputs u username 20 nil 202 | p password 20 nil) 203 | (br) 204 | (submit label)) 205 | 206 | (= good-logins* (queue) bad-logins* (queue)) 207 | 208 | (def good-login (user pw ip) 209 | (let record (list (seconds) ip user) 210 | (if (and user pw (aand (shash pw) (is it (hpasswords* user)))) 211 | (do (unless (user->cookie* user) (cook-user user)) 212 | (enq-limit record good-logins*) 213 | user) 214 | (do (enq-limit record bad-logins*) 215 | nil)))) 216 | 217 | ; Create a file in case people have quote chars in their pws. I can't 218 | ; believe there's no way to just send the chars. 219 | 220 | (def shash (str) 221 | (let fname (+ "/tmp/shash" (rand-string 10)) 222 | (w/outfile f fname (disp str f)) 223 | (let res (tostring (system (+ "openssl dgst -sha1 <" fname))) 224 | (do1 (cut res 0 (- (len res) 1)) 225 | (rmfile fname))))) 226 | 227 | (= dc-usernames* (table)) 228 | 229 | (def username-taken (user) 230 | (when (empty dc-usernames*) 231 | (each (k v) hpasswords* 232 | (set (dc-usernames* (downcase k))))) 233 | (dc-usernames* (downcase user))) 234 | 235 | (def bad-newacct (user pw) 236 | (if (no (goodname user 2 15)) 237 | "Usernames can only contain letters, digits, dashes and 238 | underscores, and should be between 2 and 15 characters long. 239 | Please choose another." 240 | (username-taken user) 241 | "That username is taken. Please choose another." 242 | (or (no pw) (< (len pw) 4)) 243 | "Passwords should be a least 4 characters long. Please 244 | choose another." 245 | nil)) 246 | 247 | (def goodname (str (o min 1) (o max nil)) 248 | (and (isa str 'string) 249 | (>= (len str) min) 250 | (~find (fn (c) (no (or (alphadig c) (in c #\- #\_)))) 251 | str) 252 | (isnt (str 0) #\-) 253 | (or (no max) (<= (len str) max)) 254 | str)) 255 | 256 | (defop logout req 257 | (aif (get-user req) 258 | (do (logout-user it) 259 | (pr "Logged out.")) 260 | (pr "You were not logged in."))) 261 | 262 | (defop whoami req 263 | (aif (get-user req) 264 | (prs it 'at req!ip) 265 | (do (pr "You are not logged in. ") 266 | (w/link (login-page 'both) (pr "Log in")) 267 | (pr ".")))) 268 | 269 | 270 | (= formwid* 60 bigformwid* 80 numwid* 16 formatdoc-url* nil) 271 | 272 | ; Eventually figure out a way to separate type name from format of 273 | ; input field, instead of having e.g. toks and bigtoks 274 | 275 | (def varfield (typ id val) 276 | (if (in typ 'string 'string1 'url) 277 | (gentag input type 'text name id value val size formwid*) 278 | (in typ 'num 'int 'posint 'sym) 279 | (gentag input type 'text name id value val size numwid*) 280 | (in typ 'users 'toks) 281 | (gentag input type 'text name id value (tostring (apply prs val)) 282 | size formwid*) 283 | (is typ 'sexpr) 284 | (gentag input type 'text name id 285 | value (tostring (map [do (write _) (sp)] val)) 286 | size formwid*) 287 | (in typ 'syms 'text 'doc 'mdtext 'mdtext2 'lines 'bigtoks) 288 | (let text (if (in typ 'syms 'bigtoks) 289 | (tostring (apply prs val)) 290 | (is typ 'lines) 291 | (tostring (apply pr (intersperse #\newline val))) 292 | (in typ 'mdtext 'mdtext2) 293 | (unmarkdown val) 294 | (no val) 295 | "" 296 | val) 297 | (tag (textarea cols (if (is typ 'doc) bigformwid* formwid*) 298 | rows (needrows text formwid* 4) 299 | wrap 'virtual 300 | style (if (is typ 'doc) "font-size:8.5pt") 301 | name id) 302 | (prn) ; needed or 1 initial newline gets chopped off 303 | (pr text)) 304 | (when (and formatdoc-url* (in typ 'mdtext 'mdtext2)) 305 | (pr " ") 306 | (tag (font size -2) 307 | (link "help" formatdoc-url* (gray 175))))) 308 | (caris typ 'choice) 309 | (menu id (cddr typ) val) 310 | (is typ 'yesno) 311 | (menu id '("yes" "no") (if val "yes" "no")) 312 | (is typ 'hexcol) 313 | (gentag input type 'text name id value val) 314 | (is typ 'time) 315 | (gentag input type 'text name id value (if val (english-time val) "")) 316 | (is typ 'date) 317 | (gentag input type 'text name id value (if val (english-date val) "")) 318 | (err "unknown varfield type" typ))) 319 | 320 | (def text-rows (text wid (o pad 3)) 321 | (+ (trunc (/ (len text) (* wid .8))) pad)) 322 | 323 | (def needrows (text cols (o pad 0)) 324 | (+ pad (max (+ 1 (count #\newline text)) 325 | (roundup (/ (len text) (- cols 5)))))) 326 | 327 | (def varline (typ id val (o liveurls)) 328 | (if (in typ 'users 'syms 'toks 'bigtoks) (apply prs val) 329 | (is typ 'lines) (map prn val) 330 | (is typ 'yesno) (pr (if val 'yes 'no)) 331 | (caris typ 'choice) (varline (cadr typ) nil val) 332 | (is typ 'url) (if (and liveurls (valid-url val)) 333 | (link val val) 334 | (pr val)) 335 | (text-type typ) (pr (or val "")) 336 | (pr val))) 337 | 338 | (def text-type (typ) (in typ 'string 'string1 'url 'text 'mdtext 'mdtext2)) 339 | 340 | ; Newlines in forms come back as /r/n. Only want the /ns. Currently 341 | ; remove the /rs in individual cases below. Could do it in aform or 342 | ; even in the parsing of http requests, in the server. 343 | 344 | ; Need the calls to striptags so that news users can't get html 345 | ; into a title or comment by editing it. If want a form that 346 | ; can take html, just create another typ for it. 347 | 348 | (def readvar (typ str (o fail nil)) 349 | (case (carif typ) 350 | string (striptags str) 351 | string1 (if (blank str) fail (striptags str)) 352 | url (if (blank str) "" (valid-url str) (clean-url str) fail) 353 | num (let n (saferead str) (if (number n) n fail)) 354 | int (let n (saferead str) 355 | (if (number n) (round n) fail)) 356 | posint (let n (saferead str) 357 | (if (and (number n) (> n 0)) (round n) fail)) 358 | text (striptags str) 359 | doc (striptags str) 360 | mdtext (md-from-form str) 361 | mdtext2 (md-from-form str t) ; for md with no links 362 | sym (or (sym:car:tokens str) fail) 363 | syms (map sym (tokens str)) 364 | sexpr (errsafe (readall str)) 365 | users (rem [no (goodname _)] (tokens str)) 366 | toks (tokens str) 367 | bigtoks (tokens str) 368 | lines (lines str) 369 | choice (readvar (cadr typ) str) 370 | yesno (is str "yes") 371 | hexcol (if (hex>color str) str fail) 372 | time (or (errsafe (parse-time str)) fail) 373 | date (or (errsafe (parse-date str)) fail) 374 | (err "unknown readvar type" typ))) 375 | 376 | ; dates should be tagged date, and just redefine < 377 | 378 | (def varcompare (typ) 379 | (if (in typ 'syms 'sexpr 'users 'toks 'bigtoks 'lines 'hexcol) 380 | (fn (x y) (> (len x) (len y))) 381 | (is typ 'date) 382 | (fn (x y) 383 | (or (no y) (and x (date< x y)))) 384 | (fn (x y) 385 | (or (empty y) (and (~empty x) (< x y)))))) 386 | 387 | 388 | ; (= fail* (uniq)) 389 | 390 | (def fail* ()) ; coudn't possibly come back from a form 391 | 392 | ; Takes a list of fields of the form (type label value view modify) and 393 | ; a fn f and generates a form such that when submitted (f label newval) 394 | ; will be called for each valid value. Finally done is called. 395 | 396 | (def vars-form (user fields f done (o button "update") (o lasts)) 397 | (taform lasts 398 | (if (all [no (_ 4)] fields) 399 | (fn (req)) 400 | (fn (req) 401 | (when-umatch user req 402 | (each (k v) req!args 403 | (let name (sym k) 404 | (awhen (find [is (cadr _) name] fields) 405 | ; added sho to fix bug 406 | (let (typ id val sho mod) it 407 | (when (and mod v) 408 | (let newval (readvar typ v fail*) 409 | (unless (is newval fail*) 410 | (f name newval)))))))) 411 | (done)))) 412 | (tab 413 | (showvars fields)) 414 | (unless (all [no (_ 4)] fields) ; no modifiable fields 415 | (br) 416 | (submit button)))) 417 | 418 | (def showvars (fields (o liveurls)) 419 | (each (typ id val view mod question) fields 420 | (when view 421 | (when question 422 | (tr (td (prn question)))) 423 | (tr (unless question (tag (td valign 'top) (pr id ":"))) 424 | (td (if mod 425 | (varfield typ id val) 426 | (varline typ id val liveurls)))) 427 | (prn)))) 428 | 429 | ; http://daringfireball.net/projects/markdown/syntax 430 | 431 | (def md-from-form (str (o nolinks)) 432 | (markdown (trim (rem #\return (esc-tags str)) 'end) 60 nolinks)) 433 | 434 | (def markdown (s (o maxurl) (o nolinks)) 435 | (let ital nil 436 | (tostring 437 | (forlen i s 438 | (iflet (newi spaces) (indented-code s i (if (is i 0) 2 0)) 439 | (do (pr "

")
440 |                  (let cb (code-block s (- newi spaces 1))
441 |                    (pr cb)
442 |                    (= i (+ (- newi spaces 1) (len cb))))
443 |                  (pr "
")) 444 | (iflet newi (parabreak s i (if (is i 0) 1 0)) 445 | (do (unless (is i 0) (pr "

")) 446 | (= i (- newi 1))) 447 | (and (is (s i) #\*) 448 | (or ital 449 | (atend i s) 450 | (and (~whitec (s (+ i 1))) 451 | (pos #\* s (+ i 1))))) 452 | (do (pr (if ital "" "")) 453 | (= ital (no ital))) 454 | (and (no nolinks) 455 | (or (litmatch "http://" s i) 456 | (litmatch "https://" s i))) 457 | (withs (n (urlend s i) 458 | url (clean-url (cut s i n))) 459 | (tag (a href url rel 'nofollow) 460 | (pr (if (no maxurl) url (ellipsize url maxurl)))) 461 | (= i (- n 1))) 462 | (writec (s i)))))))) 463 | 464 | (def indented-code (s i (o newlines 0) (o spaces 0)) 465 | (let c (s i) 466 | (if (nonwhite c) 467 | (if (and (> newlines 1) (> spaces 1)) 468 | (list i spaces) 469 | nil) 470 | (atend i s) 471 | nil 472 | (is c #\newline) 473 | (indented-code s (+ i 1) (+ newlines 1) 0) 474 | (indented-code s (+ i 1) newlines (+ spaces 1))))) 475 | 476 | ; If i is start a paragraph break, returns index of start of next para. 477 | 478 | (def parabreak (s i (o newlines 0)) 479 | (let c (s i) 480 | (if (or (nonwhite c) (atend i s)) 481 | (if (> newlines 1) i nil) 482 | (parabreak s (+ i 1) (+ newlines (if (is c #\newline) 1 0)))))) 483 | 484 | ; Returns the indices of the next paragraph break in s, if any. 485 | 486 | (def next-parabreak (s i) 487 | (unless (atend i s) 488 | (aif (parabreak s i) 489 | (list i it) 490 | (next-parabreak s (+ i 1))))) 491 | 492 | (def paras (s (o i 0)) 493 | (if (atend i s) 494 | nil 495 | (iflet (endthis startnext) (next-parabreak s i) 496 | (cons (cut s i endthis) 497 | (paras s startnext)) 498 | (list (trim (cut s i) 'end))))) 499 | 500 | 501 | ; Returns the index of the first char not part of the url beginning 502 | ; at i, or len of string if url goes all the way to the end. 503 | 504 | ; Note that > immediately after a url (http://foo.com>) will cause 505 | ; an odd result, because the > gets escaped to something beginning 506 | ; with &, which is treated as part of the url. Perhaps the answer 507 | ; is just to esc-tags after markdown instead of before. 508 | 509 | ; Treats a delimiter as part of a url if it is (a) an open delimiter 510 | ; not followed by whitespace or eos, or (b) a close delimiter 511 | ; balancing a previous open delimiter. 512 | 513 | (def urlend (s i (o indelim)) 514 | (let c (s i) 515 | (if (atend i s) 516 | (if ((orf punc whitec opendelim) c) 517 | i 518 | (closedelim c) 519 | (if indelim (+ i 1) i) 520 | (+ i 1)) 521 | (if (or (whitec c) 522 | (and (punc c) (whitec (s (+ i 1)))) 523 | (and ((orf whitec punc) (s (+ i 1))) 524 | (or (opendelim c) 525 | (and (closedelim c) (no indelim))))) 526 | i 527 | (urlend s (+ i 1) (or (opendelim c) 528 | (and indelim (no (closedelim c))))))))) 529 | 530 | (def opendelim (c) (in c #\< #\( #\[ #\{)) 531 | 532 | (def closedelim (c) (in c #\> #\) #\] #\})) 533 | 534 | 535 | (def code-block (s i) 536 | (tostring 537 | (until (let left (- (len s) i 1) 538 | (or (is left 0) 539 | (and (> left 2) 540 | (is (s (+ i 1)) #\newline) 541 | (nonwhite (s (+ i 2)))))) 542 | (writec (s (++ i)))))) 543 | 544 | (def unmarkdown (s) 545 | (tostring 546 | (forlen i s 547 | (if (litmatch "

" s i) 548 | (do (++ i 2) 549 | (unless (is i 2) (pr "\n\n"))) 550 | (litmatch "" s i) 551 | (do (++ i 2) (pr #\*)) 552 | (litmatch "" s i) 553 | (do (++ i 3) (pr #\*)) 554 | (litmatch "" s endurl) 559 | (+ it 3) 560 | endurl))) 561 | (writec (s i)))) 562 | (litmatch "

" s i)
563 |            (awhen (findsubseq "
" s (+ i 12)) 564 | (pr (cut s (+ i 11) it)) 565 | (= i (+ it 12))) 566 | (writec (s i)))))) 567 | 568 | 569 | (def english-time (min) 570 | (let n (mod min 720) 571 | (string (let h (trunc (/ n 60)) (if (is h 0) "12" h)) 572 | ":" 573 | (let m (mod n 60) 574 | (if (is m 0) "00" 575 | (< m 10) (string "0" m) 576 | m)) 577 | (if (is min 0) " midnight" 578 | (is min 720) " noon" 579 | (>= min 720) " pm" 580 | " am")))) 581 | 582 | (def parse-time (s) 583 | (let (nums (o label "")) (halve s letter) 584 | (with ((h (o m 0)) (map int (tokens nums ~digit)) 585 | cleanlabel (downcase (rem ~alphadig label))) 586 | (+ (* (if (is h 12) 587 | (if (in cleanlabel "am" "midnight") 588 | 0 589 | 12) 590 | (is cleanlabel "am") 591 | h 592 | (+ h 12)) 593 | 60) 594 | m)))) 595 | 596 | 597 | (= months* '("January" "February" "March" "April" "May" "June" "July" 598 | "August" "September" "October" "November" "December")) 599 | 600 | (def english-date ((y m d)) 601 | (string d " " (months* (- m 1)) " " y)) 602 | 603 | (= month-names* (obj "january" 1 "jan" 1 604 | "february" 2 "feb" 2 605 | "march" 3 "mar" 3 606 | "april" 4 "apr" 4 607 | "may" 5 608 | "june" 6 "jun" 6 609 | "july" 7 "jul" 7 610 | "august" 8 "aug" 8 611 | "september" 9 "sept" 9 "sep" 9 612 | "october" 10 "oct" 10 613 | "november" 11 "nov" 11 614 | "december" 12 "dec" 12)) 615 | 616 | (def monthnum (s) (month-names* (downcase s))) 617 | 618 | ; Doesn't work for BC dates. 619 | 620 | (def parse-date (s) 621 | (let nums (date-nums s) 622 | (if (valid-date nums) 623 | nums 624 | (err (string "Invalid date: " s))))) 625 | 626 | (def date-nums (s) 627 | (with ((ynow mnow dnow) (date) 628 | toks (tokens s ~alphadig)) 629 | (if (all [all digit _] toks) 630 | (let nums (map int toks) 631 | (case (len nums) 632 | 1 (list ynow mnow (car nums)) 633 | 2 (iflet d (find [> _ 12] nums) 634 | (list ynow (find [isnt _ d] nums) d) 635 | (cons ynow nums)) 636 | (if (> (car nums) 31) 637 | (firstn 3 nums) 638 | (rev (firstn 3 nums))))) 639 | ([all digit _] (car toks)) 640 | (withs ((ds ms ys) toks 641 | d (int ds)) 642 | (aif (monthnum ms) 643 | (list (or (errsafe (int ys)) ynow) 644 | it 645 | d) 646 | nil)) 647 | (monthnum (car toks)) 648 | (let (ms ds ys) toks 649 | (aif (errsafe (int ds)) 650 | (list (or (errsafe (int ys)) ynow) 651 | (monthnum (car toks)) 652 | it) 653 | nil)) 654 | nil))) 655 | 656 | ; To be correct needs to know days per month, and about leap years 657 | 658 | (def valid-date ((y m d)) 659 | (and y m d 660 | (< 0 m 13) 661 | (< 0 d 32))) 662 | 663 | (mac defopl (name parm . body) 664 | `(defop ,name ,parm 665 | (if (get-user ,parm) 666 | (do ,@body) 667 | (login-page 'both 668 | "You need to be logged in to do that." 669 | (list (fn (u ip)) 670 | (string ',name (reassemble-args ,parm))))))) 671 | 672 | -------------------------------------------------------------------------------- /arc3.1/arc.arc: -------------------------------------------------------------------------------- 1 | ; Main Arc lib. Ported to Scheme version Jul 06. 2 | 3 | ; don't like names of conswhen and consif 4 | 5 | ; need better way of generating strings; too many calls to string 6 | ; maybe strings with escape char for evaluation 7 | ; make foo~bar equiv of foo:~bar (in expand-ssyntax) 8 | ; add sigs of ops defined in ac.scm 9 | ; get hold of error types within arc 10 | ; does macex have to be defined in scheme instead of using def below? 11 | ; write disp, read, write in arc 12 | ; could I get all of macros up into arc.arc? 13 | ; warn when shadow a global name 14 | ; some simple regexp/parsing plan 15 | 16 | ; compromises in this implementation: 17 | ; no objs in code 18 | ; (mac testlit args (listtab args)) breaks when called 19 | ; separate string type 20 | ; (= (cdr (cdr str)) "foo") couldn't work because no way to get str tail 21 | ; not sure this is a mistake; strings may be subtly different from 22 | ; lists of chars 23 | 24 | 25 | (assign do (annotate 'mac 26 | (fn args `((fn () ,@args))))) 27 | 28 | (assign safeset (annotate 'mac 29 | (fn (var val) 30 | `(do (if (bound ',var) 31 | (do (disp "*** redefining " (stderr)) 32 | (disp ',var (stderr)) 33 | (disp #\newline (stderr)))) 34 | (assign ,var ,val))))) 35 | 36 | (assign def (annotate 'mac 37 | (fn (name parms . body) 38 | `(do (sref sig ',parms ',name) 39 | (safeset ,name (fn ,parms ,@body)))))) 40 | 41 | (def caar (xs) (car (car xs))) 42 | (def cadr (xs) (car (cdr xs))) 43 | (def cddr (xs) (cdr (cdr xs))) 44 | 45 | (def no (x) (is x nil)) 46 | 47 | (def acons (x) (is (type x) 'cons)) 48 | 49 | (def atom (x) (no (acons x))) 50 | 51 | ; Can return to this def once Rtm gets ac to make all rest args 52 | ; nil-terminated lists. 53 | 54 | ; (def list args args) 55 | 56 | (def copylist (xs) 57 | (if (no xs) 58 | nil 59 | (cons (car xs) (copylist (cdr xs))))) 60 | 61 | (def list args (copylist args)) 62 | 63 | (def idfn (x) x) 64 | 65 | ; Maybe later make this internal. Useful to let xs be a fn? 66 | 67 | (def map1 (f xs) 68 | (if (no xs) 69 | nil 70 | (cons (f (car xs)) (map1 f (cdr xs))))) 71 | 72 | (def pair (xs (o f list)) 73 | (if (no xs) 74 | nil 75 | (no (cdr xs)) 76 | (list (list (car xs))) 77 | (cons (f (car xs) (cadr xs)) 78 | (pair (cddr xs) f)))) 79 | 80 | (assign mac (annotate 'mac 81 | (fn (name parms . body) 82 | `(do (sref sig ',parms ',name) 83 | (safeset ,name (annotate 'mac (fn ,parms ,@body))))))) 84 | 85 | (mac and args 86 | (if args 87 | (if (cdr args) 88 | `(if ,(car args) (and ,@(cdr args))) 89 | (car args)) 90 | 't)) 91 | 92 | (def assoc (key al) 93 | (if (atom al) 94 | nil 95 | (and (acons (car al)) (is (caar al) key)) 96 | (car al) 97 | (assoc key (cdr al)))) 98 | 99 | (def alref (al key) (cadr (assoc key al))) 100 | 101 | (mac with (parms . body) 102 | `((fn ,(map1 car (pair parms)) 103 | ,@body) 104 | ,@(map1 cadr (pair parms)))) 105 | 106 | (mac let (var val . body) 107 | `(with (,var ,val) ,@body)) 108 | 109 | (mac withs (parms . body) 110 | (if (no parms) 111 | `(do ,@body) 112 | `(let ,(car parms) ,(cadr parms) 113 | (withs ,(cddr parms) ,@body)))) 114 | 115 | ; Rtm prefers to overload + to do this 116 | 117 | (def join args 118 | (if (no args) 119 | nil 120 | (let a (car args) 121 | (if (no a) 122 | (apply join (cdr args)) 123 | (cons (car a) (apply join (cdr a) (cdr args))))))) 124 | 125 | ; Need rfn for use in macro expansions. 126 | 127 | (mac rfn (name parms . body) 128 | `(let ,name nil 129 | (assign ,name (fn ,parms ,@body)))) 130 | 131 | (mac afn (parms . body) 132 | `(let self nil 133 | (assign self (fn ,parms ,@body)))) 134 | 135 | ; Ac expands x:y:z into (compose x y z), ~x into (complement x) 136 | 137 | ; Only used when the call to compose doesn't occur in functional position. 138 | ; Composes in functional position are transformed away by ac. 139 | 140 | (mac compose args 141 | (let g (uniq) 142 | `(fn ,g 143 | ,((afn (fs) 144 | (if (cdr fs) 145 | (list (car fs) (self (cdr fs))) 146 | `(apply ,(if (car fs) (car fs) 'idfn) ,g))) 147 | args)))) 148 | 149 | ; Ditto: complement in functional position optimized by ac. 150 | 151 | (mac complement (f) 152 | (let g (uniq) 153 | `(fn ,g (no (apply ,f ,g))))) 154 | 155 | (def rev (xs) 156 | ((afn (xs acc) 157 | (if (no xs) 158 | acc 159 | (self (cdr xs) (cons (car xs) acc)))) 160 | xs nil)) 161 | 162 | (def isnt (x y) (no (is x y))) 163 | 164 | (mac w/uniq (names . body) 165 | (if (acons names) 166 | `(with ,(apply + nil (map1 (fn (n) (list n '(uniq))) 167 | names)) 168 | ,@body) 169 | `(let ,names (uniq) ,@body))) 170 | 171 | (mac or args 172 | (and args 173 | (w/uniq g 174 | `(let ,g ,(car args) 175 | (if ,g ,g (or ,@(cdr args))))))) 176 | 177 | (def alist (x) (or (no x) (is (type x) 'cons))) 178 | 179 | (mac in (x . choices) 180 | (w/uniq g 181 | `(let ,g ,x 182 | (or ,@(map1 (fn (c) `(is ,g ,c)) choices))))) 183 | 184 | ; Could take n args, but have never once needed that. 185 | 186 | (def iso (x y) 187 | (or (is x y) 188 | (and (acons x) 189 | (acons y) 190 | (iso (car x) (car y)) 191 | (iso (cdr x) (cdr y))))) 192 | 193 | (mac when (test . body) 194 | `(if ,test (do ,@body))) 195 | 196 | (mac unless (test . body) 197 | `(if (no ,test) (do ,@body))) 198 | 199 | (mac while (test . body) 200 | (w/uniq (gf gp) 201 | `((rfn ,gf (,gp) 202 | (when ,gp ,@body (,gf ,test))) 203 | ,test))) 204 | 205 | (def empty (seq) 206 | (or (no seq) 207 | (and (or (is (type seq) 'string) (is (type seq) 'table)) 208 | (is (len seq) 0)))) 209 | 210 | (def reclist (f xs) 211 | (and xs (or (f xs) (reclist f (cdr xs))))) 212 | 213 | (def recstring (test s (o start 0)) 214 | ((afn (i) 215 | (and (< i (len s)) 216 | (or (test i) 217 | (self (+ i 1))))) 218 | start)) 219 | 220 | (def testify (x) 221 | (if (isa x 'fn) x [is _ x])) 222 | 223 | ; Like keep, seems like some shouldn't testify. But find should, 224 | ; and all probably should. 225 | 226 | (def some (test seq) 227 | (let f (testify test) 228 | (if (alist seq) 229 | (reclist f:car seq) 230 | (recstring f:seq seq)))) 231 | 232 | (def all (test seq) 233 | (~some (complement (testify test)) seq)) 234 | 235 | (def mem (test seq) 236 | (let f (testify test) 237 | (reclist [if (f:car _) _] seq))) 238 | 239 | (def find (test seq) 240 | (let f (testify test) 241 | (if (alist seq) 242 | (reclist [if (f:car _) (car _)] seq) 243 | (recstring [if (f:seq _) (seq _)] seq)))) 244 | 245 | (def isa (x y) (is (type x) y)) 246 | 247 | ; Possible to write map without map1, but makes News 3x slower. 248 | 249 | ;(def map (f . seqs) 250 | ; (if (some1 no seqs) 251 | ; nil 252 | ; (no (cdr seqs)) 253 | ; (let s1 (car seqs) 254 | ; (cons (f (car s1)) 255 | ; (map f (cdr s1)))) 256 | ; (cons (apply f (map car seqs)) 257 | ; (apply map f (map cdr seqs))))) 258 | 259 | 260 | (def map (f . seqs) 261 | (if (some [isa _ 'string] seqs) 262 | (withs (n (apply min (map len seqs)) 263 | new (newstring n)) 264 | ((afn (i) 265 | (if (is i n) 266 | new 267 | (do (sref new (apply f (map [_ i] seqs)) i) 268 | (self (+ i 1))))) 269 | 0)) 270 | (no (cdr seqs)) 271 | (map1 f (car seqs)) 272 | ((afn (seqs) 273 | (if (some no seqs) 274 | nil 275 | (cons (apply f (map1 car seqs)) 276 | (self (map1 cdr seqs))))) 277 | seqs))) 278 | 279 | (def mappend (f . args) 280 | (apply + nil (apply map f args))) 281 | 282 | (def firstn (n xs) 283 | (if (no n) xs 284 | (and (> n 0) xs) (cons (car xs) (firstn (- n 1) (cdr xs))) 285 | nil)) 286 | 287 | (def nthcdr (n xs) 288 | (if (no n) xs 289 | (> n 0) (nthcdr (- n 1) (cdr xs)) 290 | xs)) 291 | 292 | ; Generalization of pair: (tuples x) = (pair x) 293 | 294 | (def tuples (xs (o n 2)) 295 | (if (no xs) 296 | nil 297 | (cons (firstn n xs) 298 | (tuples (nthcdr n xs) n)))) 299 | 300 | ; If ok to do with =, why not with def? But see if use it. 301 | 302 | (mac defs args 303 | `(do ,@(map [cons 'def _] (tuples args 3)))) 304 | 305 | (def caris (x val) 306 | (and (acons x) (is (car x) val))) 307 | 308 | (def warn (msg . args) 309 | (disp (+ "Warning: " msg ". ")) 310 | (map [do (write _) (disp " ")] args) 311 | (disp #\newline)) 312 | 313 | (mac atomic body 314 | `(atomic-invoke (fn () ,@body))) 315 | 316 | (mac atlet args 317 | `(atomic (let ,@args))) 318 | 319 | (mac atwith args 320 | `(atomic (with ,@args))) 321 | 322 | (mac atwiths args 323 | `(atomic (withs ,@args))) 324 | 325 | 326 | ; setforms returns (vars get set) for a place based on car of an expr 327 | ; vars is a list of gensyms alternating with expressions whose vals they 328 | ; should be bound to, suitable for use as first arg to withs 329 | ; get is an expression returning the current value in the place 330 | ; set is an expression representing a function of one argument 331 | ; that stores a new value in the place 332 | 333 | ; A bit gross that it works based on the *name* in the car, but maybe 334 | ; wrong to worry. Macros live in expression land. 335 | 336 | ; seems meaningful to e.g. (push 1 (pop x)) if (car x) is a cons. 337 | ; can't in cl though. could I define a setter for push or pop? 338 | 339 | (assign setter (table)) 340 | 341 | (mac defset (name parms . body) 342 | (w/uniq gexpr 343 | `(sref setter 344 | (fn (,gexpr) 345 | (let ,parms (cdr ,gexpr) 346 | ,@body)) 347 | ',name))) 348 | 349 | (defset car (x) 350 | (w/uniq g 351 | (list (list g x) 352 | `(car ,g) 353 | `(fn (val) (scar ,g val))))) 354 | 355 | (defset cdr (x) 356 | (w/uniq g 357 | (list (list g x) 358 | `(cdr ,g) 359 | `(fn (val) (scdr ,g val))))) 360 | 361 | (defset caar (x) 362 | (w/uniq g 363 | (list (list g x) 364 | `(caar ,g) 365 | `(fn (val) (scar (car ,g) val))))) 366 | 367 | (defset cadr (x) 368 | (w/uniq g 369 | (list (list g x) 370 | `(cadr ,g) 371 | `(fn (val) (scar (cdr ,g) val))))) 372 | 373 | (defset cddr (x) 374 | (w/uniq g 375 | (list (list g x) 376 | `(cddr ,g) 377 | `(fn (val) (scdr (cdr ,g) val))))) 378 | 379 | ; Note: if expr0 macroexpands into any expression whose car doesn't 380 | ; have a setter, setforms assumes it's a data structure in functional 381 | ; position. Such bugs will be seen only when the code is executed, when 382 | ; sref complains it can't set a reference to a function. 383 | 384 | (def setforms (expr0) 385 | (let expr (macex expr0) 386 | (if (isa expr 'sym) 387 | (if (ssyntax expr) 388 | (setforms (ssexpand expr)) 389 | (w/uniq (g h) 390 | (list (list g expr) 391 | g 392 | `(fn (,h) (assign ,expr ,h))))) 393 | ; make it also work for uncompressed calls to compose 394 | (and (acons expr) (metafn (car expr))) 395 | (setforms (expand-metafn-call (ssexpand (car expr)) (cdr expr))) 396 | (and (acons expr) (acons (car expr)) (is (caar expr) 'get)) 397 | (setforms (list (cadr expr) (cadr (car expr)))) 398 | (let f (setter (car expr)) 399 | (if f 400 | (f expr) 401 | ; assumed to be data structure in fn position 402 | (do (when (caris (car expr) 'fn) 403 | (warn "Inverting what looks like a function call" 404 | expr0 expr)) 405 | (w/uniq (g h) 406 | (let argsyms (map [uniq] (cdr expr)) 407 | (list (+ (list g (car expr)) 408 | (mappend list argsyms (cdr expr))) 409 | `(,g ,@argsyms) 410 | `(fn (,h) (sref ,g ,h ,(car argsyms)))))))))))) 411 | 412 | (def metafn (x) 413 | (or (ssyntax x) 414 | (and (acons x) (in (car x) 'compose 'complement)))) 415 | 416 | (def expand-metafn-call (f args) 417 | (if (is (car f) 'compose) 418 | ((afn (fs) 419 | (if (caris (car fs) 'compose) ; nested compose 420 | (self (join (cdr (car fs)) (cdr fs))) 421 | (cdr fs) 422 | (list (car fs) (self (cdr fs))) 423 | (cons (car fs) args))) 424 | (cdr f)) 425 | (is (car f) 'no) 426 | (err "Can't invert " (cons f args)) 427 | (cons f args))) 428 | 429 | (def expand= (place val) 430 | (if (and (isa place 'sym) (~ssyntax place)) 431 | `(assign ,place ,val) 432 | (let (vars prev setter) (setforms place) 433 | (w/uniq g 434 | `(atwith ,(+ vars (list g val)) 435 | (,setter ,g)))))) 436 | 437 | (def expand=list (terms) 438 | `(do ,@(map (fn ((p v)) (expand= p v)) ; [apply expand= _] 439 | (pair terms)))) 440 | 441 | (mac = args 442 | (expand=list args)) 443 | 444 | (mac loop (start test update . body) 445 | (w/uniq (gfn gparm) 446 | `(do ,start 447 | ((rfn ,gfn (,gparm) 448 | (if ,gparm 449 | (do ,@body ,update (,gfn ,test)))) 450 | ,test)))) 451 | 452 | (mac for (v init max . body) 453 | (w/uniq (gi gm) 454 | `(with (,v nil ,gi ,init ,gm (+ ,max 1)) 455 | (loop (assign ,v ,gi) (< ,v ,gm) (assign ,v (+ ,v 1)) 456 | ,@body)))) 457 | 458 | (mac down (v init min . body) 459 | (w/uniq (gi gm) 460 | `(with (,v nil ,gi ,init ,gm (- ,min 1)) 461 | (loop (assign ,v ,gi) (> ,v ,gm) (assign ,v (- ,v 1)) 462 | ,@body)))) 463 | 464 | (mac repeat (n . body) 465 | `(for ,(uniq) 1 ,n ,@body)) 466 | 467 | ; could bind index instead of gensym 468 | 469 | (mac each (var expr . body) 470 | (w/uniq (gseq gf gv) 471 | `(let ,gseq ,expr 472 | (if (alist ,gseq) 473 | ((rfn ,gf (,gv) 474 | (when (acons ,gv) 475 | (let ,var (car ,gv) ,@body) 476 | (,gf (cdr ,gv)))) 477 | ,gseq) 478 | (isa ,gseq 'table) 479 | (maptable (fn ,var ,@body) 480 | ,gseq) 481 | (for ,gv 0 (- (len ,gseq) 1) 482 | (let ,var (,gseq ,gv) ,@body)))))) 483 | 484 | ; (nthcdr x y) = (cut y x). 485 | 486 | (def cut (seq start (o end)) 487 | (let end (if (no end) (len seq) 488 | (< end 0) (+ (len seq) end) 489 | end) 490 | (if (isa seq 'string) 491 | (let s2 (newstring (- end start)) 492 | (for i 0 (- end start 1) 493 | (= (s2 i) (seq (+ start i)))) 494 | s2) 495 | (firstn (- end start) (nthcdr start seq))))) 496 | 497 | (mac whilet (var test . body) 498 | (w/uniq (gf gp) 499 | `((rfn ,gf (,gp) 500 | (let ,var ,gp 501 | (when ,var ,@body (,gf ,test)))) 502 | ,test))) 503 | 504 | (def last (xs) 505 | (if (cdr xs) 506 | (last (cdr xs)) 507 | (car xs))) 508 | 509 | (def rem (test seq) 510 | (let f (testify test) 511 | (if (alist seq) 512 | ((afn (s) 513 | (if (no s) nil 514 | (f (car s)) (self (cdr s)) 515 | (cons (car s) (self (cdr s))))) 516 | seq) 517 | (coerce (rem test (coerce seq 'cons)) 'string)))) 518 | 519 | ; Seems like keep doesn't need to testify-- would be better to 520 | ; be able to use tables as fns. But rem does need to, because 521 | ; often want to rem a table from a list. So maybe the right answer 522 | ; is to make keep the more primitive, not rem. 523 | 524 | (def keep (test seq) 525 | (rem (complement (testify test)) seq)) 526 | 527 | ;(def trues (f seq) 528 | ; (rem nil (map f seq))) 529 | 530 | (def trues (f xs) 531 | (and xs 532 | (let fx (f (car xs)) 533 | (if fx 534 | (cons fx (trues f (cdr xs))) 535 | (trues f (cdr xs)))))) 536 | 537 | (mac do1 args 538 | (w/uniq g 539 | `(let ,g ,(car args) 540 | ,@(cdr args) 541 | ,g))) 542 | 543 | ; Would like to write a faster case based on table generated by a macro, 544 | ; but can't insert objects into expansions in Mzscheme. 545 | 546 | (mac caselet (var expr . args) 547 | (let ex (afn (args) 548 | (if (no (cdr args)) 549 | (car args) 550 | `(if (is ,var ',(car args)) 551 | ,(cadr args) 552 | ,(self (cddr args))))) 553 | `(let ,var ,expr ,(ex args)))) 554 | 555 | (mac case (expr . args) 556 | `(caselet ,(uniq) ,expr ,@args)) 557 | 558 | (mac push (x place) 559 | (w/uniq gx 560 | (let (binds val setter) (setforms place) 561 | `(let ,gx ,x 562 | (atwiths ,binds 563 | (,setter (cons ,gx ,val))))))) 564 | 565 | (mac swap (place1 place2) 566 | (w/uniq (g1 g2) 567 | (with ((binds1 val1 setter1) (setforms place1) 568 | (binds2 val2 setter2) (setforms place2)) 569 | `(atwiths ,(+ binds1 (list g1 val1) binds2 (list g2 val2)) 570 | (,setter1 ,g2) 571 | (,setter2 ,g1))))) 572 | 573 | (mac rotate places 574 | (with (vars (map [uniq] places) 575 | forms (map setforms places)) 576 | `(atwiths ,(mappend (fn (g (binds val setter)) 577 | (+ binds (list g val))) 578 | vars 579 | forms) 580 | ,@(map (fn (g (binds val setter)) 581 | (list setter g)) 582 | (+ (cdr vars) (list (car vars))) 583 | forms)))) 584 | 585 | (mac pop (place) 586 | (w/uniq g 587 | (let (binds val setter) (setforms place) 588 | `(atwiths ,(+ binds (list g val)) 589 | (do1 (car ,g) 590 | (,setter (cdr ,g))))))) 591 | 592 | (def adjoin (x xs (o test iso)) 593 | (if (some [test x _] xs) 594 | xs 595 | (cons x xs))) 596 | 597 | (mac pushnew (x place . args) 598 | (w/uniq gx 599 | (let (binds val setter) (setforms place) 600 | `(atwiths ,(+ (list gx x) binds) 601 | (,setter (adjoin ,gx ,val ,@args)))))) 602 | 603 | (mac pull (test place) 604 | (w/uniq g 605 | (let (binds val setter) (setforms place) 606 | `(atwiths ,(+ (list g test) binds) 607 | (,setter (rem ,g ,val)))))) 608 | 609 | (mac togglemem (x place . args) 610 | (w/uniq gx 611 | (let (binds val setter) (setforms place) 612 | `(atwiths ,(+ (list gx x) binds) 613 | (,setter (if (mem ,gx ,val) 614 | (rem ,gx ,val) 615 | (adjoin ,gx ,val ,@args))))))) 616 | 617 | (mac ++ (place (o i 1)) 618 | (if (isa place 'sym) 619 | `(= ,place (+ ,place ,i)) 620 | (w/uniq gi 621 | (let (binds val setter) (setforms place) 622 | `(atwiths ,(+ binds (list gi i)) 623 | (,setter (+ ,val ,gi))))))) 624 | 625 | (mac -- (place (o i 1)) 626 | (if (isa place 'sym) 627 | `(= ,place (- ,place ,i)) 628 | (w/uniq gi 629 | (let (binds val setter) (setforms place) 630 | `(atwiths ,(+ binds (list gi i)) 631 | (,setter (- ,val ,gi))))))) 632 | 633 | ; E.g. (++ x) equiv to (zap + x 1) 634 | 635 | (mac zap (op place . args) 636 | (with (gop (uniq) 637 | gargs (map [uniq] args) 638 | mix (afn seqs 639 | (if (some no seqs) 640 | nil 641 | (+ (map car seqs) 642 | (apply self (map cdr seqs)))))) 643 | (let (binds val setter) (setforms place) 644 | `(atwiths ,(+ binds (list gop op) (mix gargs args)) 645 | (,setter (,gop ,val ,@gargs)))))) 646 | 647 | ; Can't simply mod pr to print strings represented as lists of chars, 648 | ; because empty string will get printed as nil. Would need to rep strings 649 | ; as lists of chars annotated with 'string, and modify car and cdr to get 650 | ; the rep of these. That would also require hacking the reader. 651 | 652 | (def pr args 653 | (map1 disp args) 654 | (car args)) 655 | 656 | (def prt args 657 | (map1 [if _ (disp _)] args) 658 | (car args)) 659 | 660 | (def prn args 661 | (do1 (apply pr args) 662 | (writec #\newline))) 663 | 664 | (mac wipe args 665 | `(do ,@(map (fn (a) `(= ,a nil)) args))) 666 | 667 | (mac set args 668 | `(do ,@(map (fn (a) `(= ,a t)) args))) 669 | 670 | ; Destructuring means ambiguity: are pat vars bound in else? (no) 671 | 672 | (mac iflet (var expr then . rest) 673 | (w/uniq gv 674 | `(let ,gv ,expr 675 | (if ,gv (let ,var ,gv ,then) ,@rest)))) 676 | 677 | (mac whenlet (var expr . body) 678 | `(iflet ,var ,expr (do ,@body))) 679 | 680 | (mac aif (expr . body) 681 | `(let it ,expr 682 | (if it 683 | ,@(if (cddr body) 684 | `(,(car body) (aif ,@(cdr body))) 685 | body)))) 686 | 687 | (mac awhen (expr . body) 688 | `(let it ,expr (if it (do ,@body)))) 689 | 690 | (mac aand args 691 | (if (no args) 692 | 't 693 | (no (cdr args)) 694 | (car args) 695 | `(let it ,(car args) (and it (aand ,@(cdr args)))))) 696 | 697 | (mac accum (accfn . body) 698 | (w/uniq gacc 699 | `(withs (,gacc nil ,accfn [push _ ,gacc]) 700 | ,@body 701 | (rev ,gacc)))) 702 | 703 | ; Repeatedly evaluates its body till it returns nil, then returns vals. 704 | 705 | (mac drain (expr (o eof nil)) 706 | (w/uniq (gacc gdone gres) 707 | `(with (,gacc nil ,gdone nil) 708 | (while (no ,gdone) 709 | (let ,gres ,expr 710 | (if (is ,gres ,eof) 711 | (= ,gdone t) 712 | (push ,gres ,gacc)))) 713 | (rev ,gacc)))) 714 | 715 | ; For the common C idiom while x = snarfdata != stopval. 716 | ; Rename this if use it often. 717 | 718 | (mac whiler (var expr endval . body) 719 | (w/uniq gf 720 | `(withs (,var nil ,gf (testify ,endval)) 721 | (while (no (,gf (= ,var ,expr))) 722 | ,@body)))) 723 | 724 | ;(def macex (e) 725 | ; (if (atom e) 726 | ; e 727 | ; (let op (and (atom (car e)) (eval (car e))) 728 | ; (if (isa op 'mac) 729 | ; (apply (rep op) (cdr e)) 730 | ; e)))) 731 | 732 | (def consif (x y) (if x (cons x y) y)) 733 | 734 | (def string args 735 | (apply + "" (map [coerce _ 'string] args))) 736 | 737 | (def flat x 738 | ((afn (x acc) 739 | (if (no x) acc 740 | (atom x) (cons x acc) 741 | (self (car x) (self (cdr x) acc)))) 742 | x nil)) 743 | 744 | (mac check (x test (o alt)) 745 | (w/uniq gx 746 | `(let ,gx ,x 747 | (if (,test ,gx) ,gx ,alt)))) 748 | 749 | (def pos (test seq (o start 0)) 750 | (let f (testify test) 751 | (if (alist seq) 752 | ((afn (seq n) 753 | (if (no seq) 754 | nil 755 | (f (car seq)) 756 | n 757 | (self (cdr seq) (+ n 1)))) 758 | (nthcdr start seq) 759 | start) 760 | (recstring [if (f (seq _)) _] seq start)))) 761 | 762 | (def even (n) (is (mod n 2) 0)) 763 | 764 | (def odd (n) (no (even n))) 765 | 766 | (mac after (x . ys) 767 | `(protect (fn () ,x) (fn () ,@ys))) 768 | 769 | (let expander 770 | (fn (f var name body) 771 | `(let ,var (,f ,name) 772 | (after (do ,@body) (close ,var)))) 773 | 774 | (mac w/infile (var name . body) 775 | (expander 'infile var name body)) 776 | 777 | (mac w/outfile (var name . body) 778 | (expander 'outfile var name body)) 779 | 780 | (mac w/instring (var str . body) 781 | (expander 'instring var str body)) 782 | 783 | (mac w/socket (var port . body) 784 | (expander 'open-socket var port body)) 785 | ) 786 | 787 | (mac w/outstring (var . body) 788 | `(let ,var (outstring) ,@body)) 789 | 790 | ; what happens to a file opened for append if arc is killed in 791 | ; the middle of a write? 792 | 793 | (mac w/appendfile (var name . body) 794 | `(let ,var (outfile ,name 'append) 795 | (after (do ,@body) (close ,var)))) 796 | 797 | ; rename this simply "to"? - prob not; rarely use 798 | 799 | (mac w/stdout (str . body) 800 | `(call-w/stdout ,str (fn () ,@body))) 801 | 802 | (mac w/stdin (str . body) 803 | `(call-w/stdin ,str (fn () ,@body))) 804 | 805 | (mac tostring body 806 | (w/uniq gv 807 | `(w/outstring ,gv 808 | (w/stdout ,gv ,@body) 809 | (inside ,gv)))) 810 | 811 | (mac fromstring (str . body) 812 | (w/uniq gv 813 | `(w/instring ,gv ,str 814 | (w/stdin ,gv ,@body)))) 815 | 816 | (def readstring1 (s (o eof nil)) (w/instring i s (read i eof))) 817 | 818 | (def read ((o x (stdin)) (o eof nil)) 819 | (if (isa x 'string) (readstring1 x eof) (sread x eof))) 820 | 821 | ; inconsistency between names of readfile[1] and writefile 822 | 823 | (def readfile (name) (w/infile s name (drain (read s)))) 824 | 825 | (def readfile1 (name) (w/infile s name (read s))) 826 | 827 | (def readall (src (o eof nil)) 828 | ((afn (i) 829 | (let x (read i eof) 830 | (if (is x eof) 831 | nil 832 | (cons x (self i))))) 833 | (if (isa src 'string) (instring src) src))) 834 | 835 | (def allchars (str) 836 | (tostring (whiler c (readc str nil) no 837 | (writec c)))) 838 | 839 | (def filechars (name) 840 | (w/infile s name (allchars s))) 841 | 842 | (def writefile (val file) 843 | (let tmpfile (+ file ".tmp") 844 | (w/outfile o tmpfile (write val o)) 845 | (mvfile tmpfile file)) 846 | val) 847 | 848 | (def sym (x) (coerce x 'sym)) 849 | 850 | (def int (x (o b 10)) (coerce x 'int b)) 851 | 852 | (mac rand-choice exprs 853 | `(case (rand ,(len exprs)) 854 | ,@(let key -1 855 | (mappend [list (++ key) _] 856 | exprs)))) 857 | 858 | (mac n-of (n expr) 859 | (w/uniq ga 860 | `(let ,ga nil 861 | (repeat ,n (push ,expr ,ga)) 862 | (rev ,ga)))) 863 | 864 | ; rejects bytes >= 248 lest digits be overrepresented 865 | 866 | (def rand-string (n) 867 | (let c "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" 868 | (with (nc 62 s (newstring n) i 0) 869 | (w/infile str "/dev/urandom" 870 | (while (< i n) 871 | (let x (readb str) 872 | (unless (> x 247) 873 | (= (s i) (c (mod x nc))) 874 | (++ i))))) 875 | s))) 876 | 877 | (mac forlen (var s . body) 878 | `(for ,var 0 (- (len ,s) 1) ,@body)) 879 | 880 | (mac on (var s . body) 881 | (if (is var 'index) 882 | (err "Can't use index as first arg to on.") 883 | (w/uniq gs 884 | `(let ,gs ,s 885 | (forlen index ,gs 886 | (let ,var (,gs index) 887 | ,@body)))))) 888 | 889 | (def best (f seq) 890 | (if (no seq) 891 | nil 892 | (let wins (car seq) 893 | (each elt (cdr seq) 894 | (if (f elt wins) (= wins elt))) 895 | wins))) 896 | 897 | (def max args (best > args)) 898 | (def min args (best < args)) 899 | 900 | ; (mac max2 (x y) 901 | ; (w/uniq (a b) 902 | ; `(with (,a ,x ,b ,y) (if (> ,a ,b) ,a ,b)))) 903 | 904 | (def most (f seq) 905 | (unless (no seq) 906 | (withs (wins (car seq) topscore (f wins)) 907 | (each elt (cdr seq) 908 | (let score (f elt) 909 | (if (> score topscore) (= wins elt topscore score)))) 910 | wins))) 911 | 912 | ; Insert so that list remains sorted. Don't really want to expose 913 | ; these but seem to have to because can't include a fn obj in a 914 | ; macroexpansion. 915 | 916 | (def insert-sorted (test elt seq) 917 | (if (no seq) 918 | (list elt) 919 | (test elt (car seq)) 920 | (cons elt seq) 921 | (cons (car seq) (insert-sorted test elt (cdr seq))))) 922 | 923 | (mac insort (test elt seq) 924 | `(zap [insert-sorted ,test ,elt _] ,seq)) 925 | 926 | (def reinsert-sorted (test elt seq) 927 | (if (no seq) 928 | (list elt) 929 | (is elt (car seq)) 930 | (reinsert-sorted test elt (cdr seq)) 931 | (test elt (car seq)) 932 | (cons elt (rem elt seq)) 933 | (cons (car seq) (reinsert-sorted test elt (cdr seq))))) 934 | 935 | (mac insortnew (test elt seq) 936 | `(zap [reinsert-sorted ,test ,elt _] ,seq)) 937 | 938 | ; Could make this look at the sig of f and return a fn that took the 939 | ; right no of args and didn't have to call apply (or list if 1 arg). 940 | 941 | (def memo (f) 942 | (with (cache (table) nilcache (table)) 943 | (fn args 944 | (or (cache args) 945 | (and (no (nilcache args)) 946 | (aif (apply f args) 947 | (= (cache args) it) 948 | (do (set (nilcache args)) 949 | nil))))))) 950 | 951 | 952 | (mac defmemo (name parms . body) 953 | `(safeset ,name (memo (fn ,parms ,@body)))) 954 | 955 | (def <= args 956 | (or (no args) 957 | (no (cdr args)) 958 | (and (no (> (car args) (cadr args))) 959 | (apply <= (cdr args))))) 960 | 961 | (def >= args 962 | (or (no args) 963 | (no (cdr args)) 964 | (and (no (< (car args) (cadr args))) 965 | (apply >= (cdr args))))) 966 | 967 | (def whitec (c) 968 | (in c #\space #\newline #\tab #\return)) 969 | 970 | (def nonwhite (c) (no (whitec c))) 971 | 972 | (def letter (c) (or (<= #\a c #\z) (<= #\A c #\Z))) 973 | 974 | (def digit (c) (<= #\0 c #\9)) 975 | 976 | (def alphadig (c) (or (letter c) (digit c))) 977 | 978 | (def punc (c) 979 | (in c #\. #\, #\; #\: #\! #\?)) 980 | 981 | (def readline ((o str (stdin))) 982 | (awhen (readc str) 983 | (tostring 984 | (writec it) 985 | (whiler c (readc str) [in _ nil #\newline] 986 | (writec c))))) 987 | 988 | ; Don't currently use this but suspect some code could. 989 | 990 | (mac summing (sumfn . body) 991 | (w/uniq (gc gt) 992 | `(let ,gc 0 993 | (let ,sumfn (fn (,gt) (if ,gt (++ ,gc))) 994 | ,@body) 995 | ,gc))) 996 | 997 | (def sum (f xs) 998 | (let n 0 999 | (each x xs (++ n (f x))) 1000 | n)) 1001 | 1002 | (def treewise (f base tree) 1003 | (if (atom tree) 1004 | (base tree) 1005 | (f (treewise f base (car tree)) 1006 | (treewise f base (cdr tree))))) 1007 | 1008 | (def carif (x) (if (atom x) x (car x))) 1009 | 1010 | ; Could prob be generalized beyond printing. 1011 | 1012 | (def prall (elts (o init "") (o sep ", ")) 1013 | (when elts 1014 | (pr init (car elts)) 1015 | (map [pr sep _] (cdr elts)) 1016 | elts)) 1017 | 1018 | (def prs args 1019 | (prall args "" #\space)) 1020 | 1021 | (def tree-subst (old new tree) 1022 | (if (is tree old) 1023 | new 1024 | (atom tree) 1025 | tree 1026 | (cons (tree-subst old new (car tree)) 1027 | (tree-subst old new (cdr tree))))) 1028 | 1029 | (def ontree (f tree) 1030 | (f tree) 1031 | (unless (atom tree) 1032 | (ontree f (car tree)) 1033 | (ontree f (cdr tree)))) 1034 | 1035 | (def dotted (x) 1036 | (if (atom x) 1037 | nil 1038 | (and (cdr x) (or (atom (cdr x)) 1039 | (dotted (cdr x)))))) 1040 | 1041 | (def fill-table (table data) 1042 | (each (k v) (pair data) (= (table k) v)) 1043 | table) 1044 | 1045 | (def keys (h) 1046 | (accum a (each (k v) h (a k)))) 1047 | 1048 | (def vals (h) 1049 | (accum a (each (k v) h (a v)))) 1050 | 1051 | ; These two should really be done by coerce. Wrap coerce? 1052 | 1053 | (def tablist (h) 1054 | (accum a (maptable (fn args (a args)) h))) 1055 | 1056 | (def listtab (al) 1057 | (let h (table) 1058 | (map (fn ((k v)) (= (h k) v)) 1059 | al) 1060 | h)) 1061 | 1062 | (mac obj args 1063 | `(listtab (list ,@(map (fn ((k v)) 1064 | `(list ',k ,v)) 1065 | (pair args))))) 1066 | 1067 | (def load-table (file (o eof)) 1068 | (w/infile i file (read-table i eof))) 1069 | 1070 | (def read-table ((o i (stdin)) (o eof)) 1071 | (let e (read i eof) 1072 | (if (alist e) (listtab e) e))) 1073 | 1074 | (def load-tables (file) 1075 | (w/infile i file 1076 | (w/uniq eof 1077 | (drain (read-table i eof) eof)))) 1078 | 1079 | (def save-table (h file) 1080 | (writefile (tablist h) file)) 1081 | 1082 | (def write-table (h (o o (stdout))) 1083 | (write (tablist h) o)) 1084 | 1085 | (def copy (x . args) 1086 | (let x2 (case (type x) 1087 | sym x 1088 | cons (copylist x) ; (apply (fn args args) x) 1089 | string (let new (newstring (len x)) 1090 | (forlen i x 1091 | (= (new i) (x i))) 1092 | new) 1093 | table (let new (table) 1094 | (each (k v) x 1095 | (= (new k) v)) 1096 | new) 1097 | (err "Can't copy " x)) 1098 | (map (fn ((k v)) (= (x2 k) v)) 1099 | (pair args)) 1100 | x2)) 1101 | 1102 | (def abs (n) 1103 | (if (< n 0) (- n) n)) 1104 | 1105 | ; The problem with returning a list instead of multiple values is that 1106 | ; you can't act as if the fn didn't return multiple vals in cases where 1107 | ; you only want the first. Not a big problem. 1108 | 1109 | (def round (n) 1110 | (withs (base (trunc n) rem (abs (- n base))) 1111 | (if (> rem 1/2) ((if (> n 0) + -) base 1) 1112 | (< rem 1/2) base 1113 | (odd base) ((if (> n 0) + -) base 1) 1114 | base))) 1115 | 1116 | (def roundup (n) 1117 | (withs (base (trunc n) rem (abs (- n base))) 1118 | (if (>= rem 1/2) 1119 | ((if (> n 0) + -) base 1) 1120 | base))) 1121 | 1122 | (def nearest (n quantum) 1123 | (* (roundup (/ n quantum)) quantum)) 1124 | 1125 | (def avg (ns) (/ (apply + ns) (len ns))) 1126 | 1127 | (def med (ns (o test >)) 1128 | ((sort test ns) (round (/ (len ns) 2)))) 1129 | 1130 | ; Use mergesort on assumption that mostly sorting mostly sorted lists 1131 | ; benchmark: (let td (n-of 10000 (rand 100)) (time (sort < td)) 1) 1132 | 1133 | (def sort (test seq) 1134 | (if (alist seq) 1135 | (mergesort test (copy seq)) 1136 | (coerce (mergesort test (coerce seq 'cons)) (type seq)))) 1137 | 1138 | ; Destructive stable merge-sort, adapted from slib and improved 1139 | ; by Eli Barzilay for MzLib; re-written in Arc. 1140 | 1141 | (def mergesort (less? lst) 1142 | (with (n (len lst)) 1143 | (if (<= n 1) lst 1144 | ; ; check if the list is already sorted 1145 | ; ; (which can be a common case, eg, directory lists). 1146 | ; (let loop ([last (car lst)] [next (cdr lst)]) 1147 | ; (or (null? next) 1148 | ; (and (not (less? (car next) last)) 1149 | ; (loop (car next) (cdr next))))) 1150 | ; lst 1151 | ((afn (n) 1152 | (if (> n 2) 1153 | ; needs to evaluate L->R 1154 | (withs (j (/ (if (even n) n (- n 1)) 2) ; faster than round 1155 | a (self j) 1156 | b (self (- n j))) 1157 | (merge less? a b)) 1158 | ; the following case just inlines the length 2 case, 1159 | ; it can be removed (and use the above case for n>1) 1160 | ; and the code still works, except a little slower 1161 | (is n 2) 1162 | (with (x (car lst) y (cadr lst) p lst) 1163 | (= lst (cddr lst)) 1164 | (when (less? y x) (scar p y) (scar (cdr p) x)) 1165 | (scdr (cdr p) nil) 1166 | p) 1167 | (is n 1) 1168 | (with (p lst) 1169 | (= lst (cdr lst)) 1170 | (scdr p nil) 1171 | p) 1172 | nil)) 1173 | n)))) 1174 | 1175 | ; Also by Eli. 1176 | 1177 | (def merge (less? x y) 1178 | (if (no x) y 1179 | (no y) x 1180 | (let lup nil 1181 | (assign lup 1182 | (fn (r x y r-x?) ; r-x? for optimization -- is r connected to x? 1183 | (if (less? (car y) (car x)) 1184 | (do (if r-x? (scdr r y)) 1185 | (if (cdr y) (lup y x (cdr y) nil) (scdr y x))) 1186 | ; (car x) <= (car y) 1187 | (do (if (no r-x?) (scdr r x)) 1188 | (if (cdr x) (lup x (cdr x) y t) (scdr x y)))))) 1189 | (if (less? (car y) (car x)) 1190 | (do (if (cdr y) (lup y x (cdr y) nil) (scdr y x)) 1191 | y) 1192 | ; (car x) <= (car y) 1193 | (do (if (cdr x) (lup x (cdr x) y t) (scdr x y)) 1194 | x))))) 1195 | 1196 | (def bestn (n f seq) 1197 | (firstn n (sort f seq))) 1198 | 1199 | (def split (seq pos) 1200 | (list (cut seq 0 pos) (cut seq pos))) 1201 | 1202 | (mac time (expr) 1203 | (w/uniq (t1 t2) 1204 | `(let ,t1 (msec) 1205 | (do1 ,expr 1206 | (let ,t2 (msec) 1207 | (prn "time: " (- ,t2 ,t1) " msec.")))))) 1208 | 1209 | (mac jtime (expr) 1210 | `(do1 'ok (time ,expr))) 1211 | 1212 | (mac time10 (expr) 1213 | `(time (repeat 10 ,expr))) 1214 | 1215 | (def union (f xs ys) 1216 | (+ xs (rem (fn (y) (some [f _ y] xs)) 1217 | ys))) 1218 | 1219 | (= templates* (table)) 1220 | 1221 | (mac deftem (tem . fields) 1222 | (withs (name (carif tem) includes (if (acons tem) (cdr tem))) 1223 | `(= (templates* ',name) 1224 | (+ (mappend templates* ',(rev includes)) 1225 | (list ,@(map (fn ((k v)) `(list ',k (fn () ,v))) 1226 | (pair fields))))))) 1227 | 1228 | (mac addtem (name . fields) 1229 | `(= (templates* ',name) 1230 | (union (fn (x y) (is (car x) (car y))) 1231 | (list ,@(map (fn ((k v)) `(list ',k (fn () ,v))) 1232 | (pair fields))) 1233 | (templates* ',name)))) 1234 | 1235 | (def inst (tem . args) 1236 | (let x (table) 1237 | (each (k v) (if (acons tem) tem (templates* tem)) 1238 | (unless (no v) (= (x k) (v)))) 1239 | (each (k v) (pair args) 1240 | (= (x k) v)) 1241 | x)) 1242 | 1243 | ; To write something to be read by temread, (write (tablist x)) 1244 | 1245 | (def temread (tem (o str (stdin))) 1246 | (templatize tem (read str))) 1247 | 1248 | ; Converts alist to inst; ugly; maybe should make this part of coerce. 1249 | ; Note: discards fields not defined by the template. 1250 | 1251 | (def templatize (tem raw) 1252 | (with (x (inst tem) fields (if (acons tem) tem (templates* tem))) 1253 | (each (k v) raw 1254 | (when (assoc k fields) 1255 | (= (x k) v))) 1256 | x)) 1257 | 1258 | (def temload (tem file) 1259 | (w/infile i file (temread tem i))) 1260 | 1261 | (def temloadall (tem file) 1262 | (map (fn (pairs) (templatize tem pairs)) 1263 | (w/infile in file (readall in)))) 1264 | 1265 | 1266 | (def number (n) (in (type n) 'int 'num)) 1267 | 1268 | (def since (t1) (- (seconds) t1)) 1269 | 1270 | (def minutes-since (t1) (/ (since t1) 60)) 1271 | (def hours-since (t1) (/ (since t1) 3600)) 1272 | (def days-since (t1) (/ (since t1) 86400)) 1273 | 1274 | ; could use a version for fns of 1 arg at least 1275 | 1276 | (def cache (timef valf) 1277 | (with (cached nil gentime nil) 1278 | (fn () 1279 | (unless (and cached (< (since gentime) (timef))) 1280 | (= cached (valf) 1281 | gentime (seconds))) 1282 | cached))) 1283 | 1284 | (mac defcache (name lasts . body) 1285 | `(safeset ,name (cache (fn () ,lasts) 1286 | (fn () ,@body)))) 1287 | 1288 | (mac errsafe (expr) 1289 | `(on-err (fn (c) nil) 1290 | (fn () ,expr))) 1291 | 1292 | (def saferead (arg) (errsafe:read arg)) 1293 | 1294 | (def safe-load-table (filename) 1295 | (or (errsafe:load-table filename) 1296 | (table))) 1297 | 1298 | (def ensure-dir (path) 1299 | (unless (dir-exists path) 1300 | (system (string "mkdir -p " path)))) 1301 | 1302 | (def date ((o s (seconds))) 1303 | (rev (nthcdr 3 (timedate s)))) 1304 | 1305 | (def datestring ((o s (seconds))) 1306 | (let (y m d) (date s) 1307 | (string y "-" (if (< m 10) "0") m "-" (if (< d 10) "0") d))) 1308 | 1309 | (def count (test x) 1310 | (with (n 0 testf (testify test)) 1311 | (each elt x 1312 | (if (testf elt) (++ n))) 1313 | n)) 1314 | 1315 | (def ellipsize (str (o limit 80)) 1316 | (if (<= (len str) limit) 1317 | str 1318 | (+ (cut str 0 limit) "..."))) 1319 | 1320 | (def rand-elt (seq) 1321 | (seq (rand (len seq)))) 1322 | 1323 | (mac until (test . body) 1324 | `(while (no ,test) ,@body)) 1325 | 1326 | (def before (x y seq (o i 0)) 1327 | (with (xp (pos x seq i) yp (pos y seq i)) 1328 | (and xp (or (no yp) (< xp yp))))) 1329 | 1330 | (def orf fns 1331 | (fn args 1332 | ((afn (fs) 1333 | (and fs (or (apply (car fs) args) (self (cdr fs))))) 1334 | fns))) 1335 | 1336 | (def andf fns 1337 | (fn args 1338 | ((afn (fs) 1339 | (if (no fs) t 1340 | (no (cdr fs)) (apply (car fs) args) 1341 | (and (apply (car fs) args) (self (cdr fs))))) 1342 | fns))) 1343 | 1344 | (def atend (i s) 1345 | (> i (- (len s) 2))) 1346 | 1347 | (def multiple (x y) 1348 | (is 0 (mod x y))) 1349 | 1350 | (mac nor args `(no (or ,@args))) 1351 | 1352 | ; Consider making the default sort fn take compare's two args (when do 1353 | ; you ever have to sort mere lists of numbers?) and rename current sort 1354 | ; as prim-sort or something. 1355 | 1356 | ; Could simply modify e.g. > so that (> len) returned the same thing 1357 | ; as (compare > len). 1358 | 1359 | (def compare (comparer scorer) 1360 | (fn (x y) (comparer (scorer x) (scorer y)))) 1361 | 1362 | ; Cleaner thus, but may only ever need in 2 arg case. 1363 | 1364 | ;(def compare (comparer scorer) 1365 | ; (fn args (apply comparer map scorer args))) 1366 | 1367 | ; (def only (f g . args) (aif (apply g args) (f it))) 1368 | 1369 | (def only (f) 1370 | (fn args (if (car args) (apply f args)))) 1371 | 1372 | (mac conswhen (f x y) 1373 | (w/uniq (gf gx) 1374 | `(with (,gf ,f ,gx ,x) 1375 | (if (,gf ,gx) (cons ,gx ,y) ,y)))) 1376 | 1377 | ; Could combine with firstn if put f arg last, default to (fn (x) t). 1378 | 1379 | (def retrieve (n f xs) 1380 | (if (no n) (keep f xs) 1381 | (or (<= n 0) (no xs)) nil 1382 | (f (car xs)) (cons (car xs) (retrieve (- n 1) f (cdr xs))) 1383 | (retrieve n f (cdr xs)))) 1384 | 1385 | (def dedup (xs) 1386 | (with (h (table) acc nil) 1387 | (each x xs 1388 | (unless (h x) 1389 | (push x acc) 1390 | (set (h x)))) 1391 | (rev acc))) 1392 | 1393 | (def single (x) (and (acons x) (no (cdr x)))) 1394 | 1395 | (def intersperse (x ys) 1396 | (and ys (cons (car ys) 1397 | (mappend [list x _] (cdr ys))))) 1398 | 1399 | (def counts (seq (o c (table))) 1400 | (if (no seq) 1401 | c 1402 | (do (++ (c (car seq) 0)) 1403 | (counts (cdr seq) c)))) 1404 | 1405 | (def commonest (seq) 1406 | (with (winner nil n 0) 1407 | (each (k v) (counts seq) 1408 | (when (> v n) (= winner k n v))) 1409 | (list winner n))) 1410 | 1411 | (def reduce (f xs) 1412 | (if (cddr xs) 1413 | (reduce f (cons (f (car xs) (cadr xs)) (cddr xs))) 1414 | (apply f xs))) 1415 | 1416 | (def rreduce (f xs) 1417 | (if (cddr xs) 1418 | (f (car xs) (rreduce f (cdr xs))) 1419 | (apply f xs))) 1420 | 1421 | (let argsym (uniq) 1422 | 1423 | (def parse-format (str) 1424 | (accum a 1425 | (with (chars nil i -1) 1426 | (w/instring s str 1427 | (whilet c (readc s) 1428 | (case c 1429 | #\# (do (a (coerce (rev chars) 'string)) 1430 | (wipe chars) 1431 | (a (read s))) 1432 | #\~ (do (a (coerce (rev chars) 'string)) 1433 | (wipe chars) 1434 | (readc s) 1435 | (a (list argsym (++ i)))) 1436 | (push c chars)))) 1437 | (when chars 1438 | (a (coerce (rev chars) 'string)))))) 1439 | 1440 | (mac prf (str . args) 1441 | `(let ,argsym (list ,@args) 1442 | (pr ,@(parse-format str)))) 1443 | ) 1444 | 1445 | (def load (file) 1446 | (w/infile f file 1447 | (w/uniq eof 1448 | (whiler e (read f eof) eof 1449 | (eval e))))) 1450 | 1451 | (def positive (x) 1452 | (and (number x) (> x 0))) 1453 | 1454 | (mac w/table (var . body) 1455 | `(let ,var (table) ,@body ,var)) 1456 | 1457 | (def ero args 1458 | (w/stdout (stderr) 1459 | (each a args 1460 | (write a) 1461 | (writec #\space)) 1462 | (writec #\newline)) 1463 | (car args)) 1464 | 1465 | (def queue () (list nil nil 0)) 1466 | 1467 | ; Despite call to atomic, once had some sign this wasn't thread-safe. 1468 | ; Keep an eye on it. 1469 | 1470 | (def enq (obj q) 1471 | (atomic 1472 | (++ (q 2)) 1473 | (if (no (car q)) 1474 | (= (cadr q) (= (car q) (list obj))) 1475 | (= (cdr (cadr q)) (list obj) 1476 | (cadr q) (cdr (cadr q)))) 1477 | (car q))) 1478 | 1479 | (def deq (q) 1480 | (atomic (unless (is (q 2) 0) (-- (q 2))) 1481 | (pop (car q)))) 1482 | 1483 | ; Should redef len to do this, and make queues lists annotated queue. 1484 | 1485 | (def qlen (q) (q 2)) 1486 | 1487 | (def qlist (q) (car q)) 1488 | 1489 | (def enq-limit (val q (o limit 1000)) 1490 | (atomic 1491 | (unless (< (qlen q) limit) 1492 | (deq q)) 1493 | (enq val q))) 1494 | 1495 | (def median (ns) 1496 | ((sort > ns) (trunc (/ (len ns) 2)))) 1497 | 1498 | (mac noisy-each (n var val . body) 1499 | (w/uniq (gn gc) 1500 | `(with (,gn ,n ,gc 0) 1501 | (each ,var ,val 1502 | (when (multiple (++ ,gc) ,gn) 1503 | (pr ".") 1504 | (flushout) 1505 | ) 1506 | ,@body) 1507 | (prn) 1508 | (flushout)))) 1509 | 1510 | (mac point (name . body) 1511 | (w/uniq (g p) 1512 | `(ccc (fn (,g) 1513 | (let ,name (fn ((o ,p)) (,g ,p)) 1514 | ,@body))))) 1515 | 1516 | (mac catch body 1517 | `(point throw ,@body)) 1518 | 1519 | (def downcase (x) 1520 | (let downc (fn (c) 1521 | (let n (coerce c 'int) 1522 | (if (or (< 64 n 91) (< 191 n 215) (< 215 n 223)) 1523 | (coerce (+ n 32) 'char) 1524 | c))) 1525 | (case (type x) 1526 | string (map downc x) 1527 | char (downc x) 1528 | sym (sym (map downc (coerce x 'string))) 1529 | (err "Can't downcase" x)))) 1530 | 1531 | (def upcase (x) 1532 | (let upc (fn (c) 1533 | (let n (coerce c 'int) 1534 | (if (or (< 96 n 123) (< 223 n 247) (< 247 n 255)) 1535 | (coerce (- n 32) 'char) 1536 | c))) 1537 | (case (type x) 1538 | string (map upc x) 1539 | char (upc x) 1540 | sym (sym (map upc (coerce x 'string))) 1541 | (err "Can't upcase" x)))) 1542 | 1543 | (def inc (x (o n 1)) 1544 | (coerce (+ (coerce x 'int) n) (type x))) 1545 | 1546 | (def range (start end) 1547 | (if (> start end) 1548 | nil 1549 | (cons start (range (inc start) end)))) 1550 | 1551 | (def mismatch (s1 s2) 1552 | (catch 1553 | (on c s1 1554 | (when (isnt c (s2 index)) 1555 | (throw index))))) 1556 | 1557 | (def memtable (ks) 1558 | (let h (table) 1559 | (each k ks (set (h k))) 1560 | h)) 1561 | 1562 | (= bar* " | ") 1563 | 1564 | (mac w/bars body 1565 | (w/uniq (out needbars) 1566 | `(let ,needbars nil 1567 | (do ,@(map (fn (e) 1568 | `(let ,out (tostring ,e) 1569 | (unless (is ,out "") 1570 | (if ,needbars 1571 | (pr bar* ,out) 1572 | (do (set ,needbars) 1573 | (pr ,out)))))) 1574 | body))))) 1575 | 1576 | (def len< (x n) (< (len x) n)) 1577 | 1578 | (def len> (x n) (> (len x) n)) 1579 | 1580 | (mac thread body 1581 | `(new-thread (fn () ,@body))) 1582 | 1583 | (mac trav (x . fs) 1584 | (w/uniq g 1585 | `((afn (,g) 1586 | (when ,g 1587 | ,@(map [list _ g] fs))) 1588 | ,x))) 1589 | 1590 | (mac or= (place expr) 1591 | (let (binds val setter) (setforms place) 1592 | `(atwiths ,binds 1593 | (or ,val (,setter ,expr))))) 1594 | 1595 | (= hooks* (table)) 1596 | 1597 | (def hook (name . args) 1598 | (aif (hooks* name) (apply it args))) 1599 | 1600 | (mac defhook (name . rest) 1601 | `(= (hooks* ',name) (fn ,@rest))) 1602 | 1603 | (mac out (expr) `(pr ,(tostring (eval expr)))) 1604 | 1605 | ; if renamed this would be more natural for (map [_ user] pagefns*) 1606 | 1607 | (def get (index) [_ index]) 1608 | 1609 | (= savers* (table)) 1610 | 1611 | (mac fromdisk (var file init load save) 1612 | (w/uniq (gf gv) 1613 | `(unless (bound ',var) 1614 | (do1 (= ,var (iflet ,gf (file-exists ,file) 1615 | (,load ,gf) 1616 | ,init)) 1617 | (= (savers* ',var) (fn (,gv) (,save ,gv ,file))))))) 1618 | 1619 | (mac diskvar (var file) 1620 | `(fromdisk ,var ,file nil readfile1 writefile)) 1621 | 1622 | (mac disktable (var file) 1623 | `(fromdisk ,var ,file (table) load-table save-table)) 1624 | 1625 | (mac todisk (var (o expr var)) 1626 | `((savers* ',var) 1627 | ,(if (is var expr) var `(= ,var ,expr)))) 1628 | 1629 | 1630 | (mac evtil (expr test) 1631 | (w/uniq gv 1632 | `(let ,gv ,expr 1633 | (while (no (,test ,gv)) 1634 | (= ,gv ,expr)) 1635 | ,gv))) 1636 | 1637 | (def rand-key (h) 1638 | (if (empty h) 1639 | nil 1640 | (let n (rand (len h)) 1641 | (catch 1642 | (each (k v) h 1643 | (when (is (-- n) -1) 1644 | (throw k))))))) 1645 | 1646 | (def ratio (test xs) 1647 | (if (empty xs) 1648 | 0 1649 | (/ (count test xs) (len xs)))) 1650 | 1651 | 1652 | ; any logical reason I can't say (push x (if foo y z)) ? 1653 | ; eval would have to always ret 2 things, the val and where it came from 1654 | ; idea: implicit tables of tables; setf empty field, becomes table 1655 | ; or should setf on a table just take n args? 1656 | 1657 | ; idea: use constants in functional position for currying? 1658 | ; (1 foo) would mean (fn args (apply foo 1 args)) 1659 | ; another solution would be to declare certain symbols curryable, and 1660 | ; if > was, >_10 would mean [> _ 10] 1661 | ; or just say what the hell and make _ ssyntax for currying 1662 | ; idea: make >10 ssyntax for [> _ 10] 1663 | ; solution to the "problem" of improper lists: allow any atom as a list 1664 | ; terminator, not just nil. means list recursion should terminate on 1665 | ; atom rather than nil, (def empty (x) (or (atom x) (is x ""))) 1666 | ; table should be able to take an optional initial-value. handle in sref. 1667 | ; warn about code of form (if (= )) -- probably mean is 1668 | ; warn when a fn has a parm that's already defined as a macro. 1669 | ; (def foo (after) (after)) 1670 | ; idea: a fn (nothing) that returns a special gensym which is ignored 1671 | ; by map, so can use map in cases when don't want all the vals 1672 | ; idea: anaph macro so instead of (aand x y) say (anaph and x y) 1673 | ; idea: foo.bar!baz as an abbrev for (foo bar 'baz) 1674 | ; or something a bit more semantic? 1675 | ; could uniq be (def uniq () (annotate 'symbol (list 'u))) again? 1676 | ; idea: use x- for (car x) and -x for (cdr x) (but what about math -?) 1677 | ; idea: get rid of strings and just use symbols 1678 | ; could a string be (#\a #\b . "") ? 1679 | ; better err msg when , outside of a bq 1680 | ; idea: parameter (p foo) means in body foo is (pair arg) 1681 | ; idea: make ('string x) equiv to (coerce x 'string) ? or isa? 1682 | ; quoted atoms in car valuable unused semantic space 1683 | ; idea: if (defun foo (x y) ...), make (foo 1) return (fn (y) (foo 1 y)) 1684 | ; probably would lead to lots of errors when call with missing args 1685 | ; but would be really dense with . notation, (foo.1 2) 1686 | ; or use special ssyntax for currying: (foo@1 2) 1687 | ; remember, can also double; could use foo::bar to mean something 1688 | ; wild idea: inline defs for repetitive code 1689 | ; same args as fn you're in 1690 | ; variant of compose where first fn only applied to first arg? 1691 | ; (> (len x) y) means (>+len x y) 1692 | ; use ssyntax underscore for a var? 1693 | ; foo_bar means [foo _ bar] 1694 | ; what does foo:_:bar mean? 1695 | ; matchcase 1696 | ; idea: atable that binds it to table, assumes input is a list 1697 | ; crazy that finding the top 100 nos takes so long: 1698 | ; (let bb (n-of 1000 (rand 50)) (time10 (bestn 100 > bb))) 1699 | ; time: 2237 msec. -> now down to 850 msec 1700 | 1701 | -------------------------------------------------------------------------------- /arc3.1/array.arc: -------------------------------------------------------------------------------- 1 | ; inspired by http://awwx.ws/table-rw3 2 | 3 | (def parse-array-items (port (o acc nil)) 4 | ((scheme skip-whitespace) port) 5 | (if (is (peekc port) #\]) 6 | (do (readc port) `(list ,@(rev acc))) 7 | (let x (read port) 8 | (push x acc) 9 | (parse-array-items port acc)))) 10 | 11 | (extend-readtable #\[ parse-array-items) 12 | -------------------------------------------------------------------------------- /arc3.1/as.scm: -------------------------------------------------------------------------------- 1 | ; mzscheme -m -f as.scm 2 | ; (tl) 3 | ; (asv) 4 | ; http://localhost:8080 5 | 6 | (require mzscheme) ; promise we won't redefine mzscheme bindings 7 | 8 | (require "ac.scm") 9 | (require "brackets.scm") 10 | (use-bracket-readtable) 11 | 12 | (aload "arc.arc") 13 | (aload "libs.arc") 14 | 15 | (tl) 16 | 17 | -------------------------------------------------------------------------------- /arc3.1/between0.arc: -------------------------------------------------------------------------------- 1 | ; http://awwx.ws/between0 2 | 3 | (mac between (var expr within . body) 4 | (w/uniq first 5 | `(let ,first t 6 | (each ,var ,expr 7 | (unless ,first ,within) 8 | (wipe ,first) 9 | ,@body)))) 10 | -------------------------------------------------------------------------------- /arc3.1/blog.arc: -------------------------------------------------------------------------------- 1 | ; Blog tool example. 20 Jan 08, rev 21 May 09. 2 | 3 | ; To run: 4 | ; arc> (load "blog.arc") 5 | ; arc> (bsv) 6 | ; go to http://localhost:8080/blog 7 | 8 | (= postdir* "arc/posts/" maxid* 0 posts* (table)) 9 | 10 | (= blogtitle* "A Blog") 11 | 12 | (deftem post id nil title nil text nil) 13 | 14 | (def load-posts () 15 | (each id (map int (dir postdir*)) 16 | (= maxid* (max maxid* id) 17 | (posts* id) (temload 'post (string postdir* id))))) 18 | 19 | (def save-post (p) (save-table p (string postdir* p!id))) 20 | 21 | (def post (id) (posts* (errsafe:int id))) 22 | 23 | (mac blogpage body 24 | `(whitepage 25 | (center 26 | (widtable 600 27 | (tag b (link blogtitle* "blog")) 28 | (br 3) 29 | ,@body 30 | (br 3) 31 | (w/bars (link "archive") 32 | (link "new post" "newpost")))))) 33 | 34 | (defop viewpost req (blogop post-page req)) 35 | 36 | (def blogop (f req) 37 | (aif (post (arg req "id")) 38 | (f (get-user req) it) 39 | (blogpage (pr "No such post.")))) 40 | 41 | (def permalink (p) (string "viewpost?id=" p!id)) 42 | 43 | (def post-page (user p) (blogpage (display-post user p))) 44 | 45 | (def display-post (user p) 46 | (tag b (link p!title (permalink p))) 47 | (when user 48 | (sp) 49 | (link "[edit]" (string "editpost?id=" p!id))) 50 | (br2) 51 | (pr p!text)) 52 | 53 | (defopl newpost req 54 | (whitepage 55 | (aform [let u (get-user _) 56 | (post-page u (addpost u (arg _ "t") (arg _ "b")))] 57 | (tab (row "title" (input "t" "" 60)) 58 | (row "text" (textarea "b" 10 80)) 59 | (row "" (submit)))))) 60 | 61 | (def addpost (user title text) 62 | (let p (inst 'post 'id (++ maxid*) 'title title 'text text) 63 | (save-post p) 64 | (= (posts* p!id) p))) 65 | 66 | (defopl editpost req (blogop edit-page req)) 67 | 68 | (def edit-page (user p) 69 | (whitepage 70 | (vars-form user 71 | `((string title ,p!title t t) (text text ,p!text t t)) 72 | (fn (name val) (= (p name) val)) 73 | (fn () (save-post p) 74 | (post-page user p))))) 75 | 76 | (defop archive req 77 | (blogpage 78 | (tag ul 79 | (each p (map post (rev (range 1 maxid*))) 80 | (tag li (link p!title (permalink p))))))) 81 | 82 | (defop blog req 83 | (let user (get-user req) 84 | (blogpage 85 | (for i 0 4 86 | (awhen (posts* (- maxid* i)) 87 | (display-post user it) 88 | (br 3)))))) 89 | 90 | (def bsv () 91 | (ensure-dir postdir*) 92 | (load-posts) 93 | (asv)) 94 | 95 | 96 | -------------------------------------------------------------------------------- /arc3.1/brackets.scm: -------------------------------------------------------------------------------- 1 | ; From Eli Barzilay, eli@barzilay.org 2 | 3 | ;> (require "brackets.scm") 4 | ;> (use-bracket-readtable) 5 | ;> ([+ _ 1] 10) 6 | ;11 7 | 8 | (module brackets mzscheme 9 | 10 | ; main reader function for []s 11 | ; recursive read starts with default readtable's [ parser, 12 | ; but nested reads still use the curent readtable: 13 | 14 | (define (read-square-brackets ch port src line col pos) 15 | `(fn (_) 16 | ,(read/recursive port #\[ #f))) 17 | 18 | ; a readtable that is just like the builtin except for []s 19 | 20 | (define bracket-readtable 21 | (make-readtable #f #\[ 'terminating-macro read-square-brackets)) 22 | 23 | ; call this to set the global readtable 24 | 25 | (provide use-bracket-readtable) 26 | 27 | (define (use-bracket-readtable) 28 | (current-readtable bracket-readtable)) 29 | 30 | ; these two implement the required functionality for #reader 31 | 32 | ;(define (*read inp) 33 | ; (parameterize ((current-readtable bracket-readtable)) 34 | ; (read inp))) 35 | 36 | (define (*read . args) 37 | (parameterize ((current-readtable bracket-readtable)) 38 | (read (if (null? args) (current-input-port) (car args))))) 39 | 40 | (define (*read-syntax src port) 41 | (parameterize ((current-readtable bracket-readtable)) 42 | (read-syntax src port))) 43 | 44 | ; and the need to be provided as `read' and `read-syntax' 45 | 46 | (provide (rename *read read) (rename *read-syntax read-syntax)) 47 | 48 | ) 49 | -------------------------------------------------------------------------------- /arc3.1/code.arc: -------------------------------------------------------------------------------- 1 | ; Code analysis. Spun off 21 Dec 07. 2 | 3 | ; Ought to do more of this in Arc. One of the biggest advantages 4 | ; of Lisp is messing with code. 5 | 6 | (def codelines (file) 7 | (w/infile in file 8 | (summing test 9 | (whilet line (readline in) 10 | (test (aand (find nonwhite line) (isnt it #\;))))))) 11 | 12 | (def codeflat (file) 13 | (len (flat (readall (infile file))))) 14 | 15 | (def codetree (file) 16 | (treewise + (fn (x) 1) (readall (infile file)))) 17 | 18 | (def code-density (file) 19 | (/ (codetree file) (codelines file))) 20 | 21 | (def tokcount (files) 22 | (let counts (table) 23 | (each f files 24 | (each token (flat (readall (infile f))) 25 | (++ (counts token 0)))) 26 | counts)) 27 | 28 | (def common-tokens (files) 29 | (let counts (tokcount files) 30 | (let ranking nil 31 | (maptable (fn (k v) 32 | (unless (nonop k) 33 | (insort (compare > cadr) (list k v) ranking))) 34 | counts) 35 | ranking))) 36 | 37 | (def nonop (x) 38 | (in x 'quote 'unquote 'quasiquote 'unquote-splicing)) 39 | 40 | (def common-operators (files) 41 | (keep [and (isa (car _) 'sym) (bound (car _))] (common-tokens files))) 42 | 43 | (def top40 (xs) 44 | (map prn (firstn 40 xs)) 45 | t) 46 | 47 | (def space-eaters (files) 48 | (let counts (tokcount files) 49 | (let ranking nil 50 | (maptable (fn (k v) 51 | (when (and (isa k 'sym) (bound k)) 52 | (insort (compare > [* (len (string (car _))) 53 | (cadr _)]) 54 | (list k v (* (len (string k)) v)) 55 | ranking))) 56 | counts) 57 | ranking))) 58 | 59 | ;(top40 (space-eaters allfiles*)) 60 | 61 | (mac flatlen args `(len (flat ',args))) 62 | -------------------------------------------------------------------------------- /arc3.1/copyright: -------------------------------------------------------------------------------- 1 | This software is copyright (c) Paul Graham and Robert Morris. Permission 2 | to use it is granted under the Perl Foundations's Artistic License 2.0. 3 | -------------------------------------------------------------------------------- /arc3.1/extend-readtable0.arc: -------------------------------------------------------------------------------- 1 | ; http://awwx.ws/extend-readtable0 2 | 3 | (def extend-readtable (c parser) 4 | (scheme 5 | (current-readtable 6 | (make-readtable (current-readtable) 7 | c 8 | 'non-terminating-macro 9 | (lambda (ch port src line col pos) 10 | (parser port)))))) 11 | -------------------------------------------------------------------------------- /arc3.1/extend0.arc: -------------------------------------------------------------------------------- 1 | ; http://awwx.ws/extend0 2 | 3 | (mac extend (name arglist test . body) 4 | (w/uniq args 5 | `(let orig ,name 6 | (= ,name 7 | (fn ,args 8 | (aif (apply (fn ,arglist ,test) ,args) 9 | (apply (fn ,arglist ,@body) ,args) 10 | (apply orig ,args))))))) 11 | -------------------------------------------------------------------------------- /arc3.1/how-to-run-news: -------------------------------------------------------------------------------- 1 | To run News: 2 | 3 | tar xvf arc3.1.tar 4 | 5 | cd arc3.1 6 | 7 | mkdir arc 8 | 9 | echo "myname" > arc/admins 10 | 11 | mzscheme -f as.scm 12 | 13 | at the arc prompt: 14 | 15 | (load "news.arc") 16 | 17 | (nsv) 18 | 19 | go to http://localhost:8080 20 | 21 | click on login, and create an account called myname 22 | 23 | you should now be logged in as an admin 24 | 25 | manually give at least 10 karma to your initial set of users 26 | 27 | don't worry about "user break" messages when restarting News 28 | 29 | 30 | 31 | To customize News: 32 | 33 | change the variables at the top of news.arc 34 | 35 | 36 | 37 | To improve performance: 38 | 39 | (= static-max-age* 7200) ; browsers can cache static files for 7200 sec 40 | 41 | (declare 'direct-calls t) ; you promise not to redefine fns as tables 42 | 43 | (declare 'explicit-flush t) ; you take responsibility for flushing output 44 | ; (all existing news code already does) 45 | -------------------------------------------------------------------------------- /arc3.1/html.arc: -------------------------------------------------------------------------------- 1 | ; HTML Utils. 2 | 3 | 4 | (def color (r g b) 5 | (with (c (table) 6 | f (fn (x) (if (< x 0) 0 (> x 255) 255 x))) 7 | (= (c 'r) (f r) (c 'g) (f g) (c 'b) (f b)) 8 | c)) 9 | 10 | (def dehex (str) (errsafe (coerce str 'int 16))) 11 | 12 | (defmemo hex>color (str) 13 | (and (is (len str) 6) 14 | (with (r (dehex (cut str 0 2)) 15 | g (dehex (cut str 2 4)) 16 | b (dehex (cut str 4 6))) 17 | (and r g b 18 | (color r g b))))) 19 | 20 | (defmemo gray (n) (color n n n)) 21 | 22 | (= white (gray 255) 23 | black (gray 0) 24 | linkblue (color 0 0 190) 25 | orange (color 255 102 0) 26 | darkred (color 180 0 0) 27 | darkblue (color 0 0 120) 28 | ) 29 | 30 | (= opmeths* (table)) 31 | 32 | (mac opmeth args 33 | `(opmeths* (list ,@args))) 34 | 35 | (mac attribute (tag opt f) 36 | `(= (opmeths* (list ',tag ',opt)) ,f)) 37 | 38 | (= hexreps (table)) 39 | 40 | (for i 0 255 (= (hexreps i) 41 | (let s (coerce i 'string 16) 42 | (if (is (len s) 1) (+ "0" s) s)))) 43 | 44 | (defmemo hexrep (col) 45 | (+ (hexreps (col 'r)) (hexreps (col 'g)) (hexreps (col 'b)))) 46 | 47 | (def opcolor (key val) 48 | (w/uniq gv 49 | `(whenlet ,gv ,val 50 | (pr ,(string " " key "=#") (hexrep ,gv))))) 51 | 52 | (def opstring (key val) 53 | `(aif ,val (pr ,(+ " " key "=\"") it #\"))) 54 | 55 | (def opnum (key val) 56 | `(aif ,val (pr ,(+ " " key "=") it))) 57 | 58 | (def opsym (key val) 59 | `(pr ,(+ " " key "=") ,val)) 60 | 61 | (def opsel (key val) 62 | `(if ,val (pr " selected"))) 63 | 64 | (def opcheck (key val) 65 | `(if ,val (pr " checked"))) 66 | 67 | (def opesc (key val) 68 | `(awhen ,val 69 | (pr ,(string " " key "=\"")) 70 | (if (isa it 'string) (pr-escaped it) (pr it)) 71 | (pr #\"))) 72 | 73 | ; need to escape more? =? 74 | 75 | (def pr-escaped (x) 76 | (each c x 77 | (pr (case c #\< "<" 78 | #\> ">" 79 | #\" """ 80 | #\& "&" 81 | c)))) 82 | 83 | (attribute a href opstring) 84 | (attribute a rel opstring) 85 | (attribute a class opstring) 86 | (attribute a id opsym) 87 | (attribute a onclick opstring) 88 | (attribute body alink opcolor) 89 | (attribute body bgcolor opcolor) 90 | (attribute body leftmargin opnum) 91 | (attribute body link opcolor) 92 | (attribute body marginheight opnum) 93 | (attribute body marginwidth opnum) 94 | (attribute body topmargin opnum) 95 | (attribute body vlink opcolor) 96 | (attribute font color opcolor) 97 | (attribute font face opstring) 98 | (attribute font size opnum) 99 | (attribute form action opstring) 100 | (attribute form method opsym) 101 | (attribute img align opsym) 102 | (attribute img border opnum) 103 | (attribute img height opnum) 104 | (attribute img width opnum) 105 | (attribute img vspace opnum) 106 | (attribute img hspace opnum) 107 | (attribute img src opstring) 108 | (attribute input name opstring) 109 | (attribute input size opnum) 110 | (attribute input type opsym) 111 | (attribute input value opesc) 112 | (attribute input checked opcheck) 113 | (attribute select name opstring) 114 | (attribute option selected opsel) 115 | (attribute table bgcolor opcolor) 116 | (attribute table border opnum) 117 | (attribute table cellpadding opnum) 118 | (attribute table cellspacing opnum) 119 | (attribute table width opstring) 120 | (attribute textarea cols opnum) 121 | (attribute textarea name opstring) 122 | (attribute textarea rows opnum) 123 | (attribute textarea wrap opsym) 124 | (attribute td align opsym) 125 | (attribute td bgcolor opcolor) 126 | (attribute td colspan opnum) 127 | (attribute td width opnum) 128 | (attribute td valign opsym) 129 | (attribute td class opstring) 130 | (attribute tr bgcolor opcolor) 131 | (attribute hr color opcolor) 132 | (attribute span class opstring) 133 | (attribute span align opstring) 134 | (attribute span id opsym) 135 | (attribute rss version opstring) 136 | 137 | 138 | (mac gentag args (start-tag args)) 139 | 140 | (mac tag (spec . body) 141 | `(do ,(start-tag spec) 142 | ,@body 143 | ,(end-tag spec))) 144 | 145 | (mac tag-if (test spec . body) 146 | `(if ,test 147 | (tag ,spec ,@body) 148 | (do ,@body))) 149 | 150 | (def start-tag (spec) 151 | (if (atom spec) 152 | `(pr ,(string "<" spec ">")) 153 | (let opts (tag-options (car spec) (pair (cdr spec))) 154 | (if (all [isa _ 'string] opts) 155 | `(pr ,(string "<" (car spec) (apply string opts) ">")) 156 | `(do (pr ,(string "<" (car spec))) 157 | ,@(map (fn (opt) 158 | (if (isa opt 'string) 159 | `(pr ,opt) 160 | opt)) 161 | opts) 162 | (pr ">")))))) 163 | 164 | (def end-tag (spec) 165 | `(pr ,(string ""))) 166 | 167 | (def literal (x) 168 | (case (type x) 169 | sym (in x nil t) 170 | cons (caris x 'quote) 171 | t)) 172 | 173 | ; Returns a list whose elements are either strings, which can 174 | ; simply be printed out, or expressions, which when evaluated 175 | ; generate output. 176 | 177 | (def tag-options (spec options) 178 | (if (no options) 179 | '() 180 | (let ((opt val) . rest) options 181 | (let meth (if (is opt 'style) opstring (opmeth spec opt)) 182 | (if meth 183 | (if val 184 | (cons (if (precomputable-tagopt val) 185 | (tostring (eval (meth opt val))) 186 | (meth opt val)) 187 | (tag-options spec rest)) 188 | (tag-options spec rest)) 189 | (do 190 | (pr "") 191 | (tag-options spec rest))))))) 192 | 193 | (def precomputable-tagopt (val) 194 | (and (literal val) 195 | (no (and (is (type val) 'string) (find #\@ val))))) 196 | 197 | (def br ((o n 1)) 198 | (repeat n (pr "
")) 199 | (prn)) 200 | 201 | (def br2 () (prn "

")) 202 | 203 | (mac center body `(tag center ,@body)) 204 | (mac underline body `(tag u ,@body)) 205 | (mac tab body `(tag (table border 0) ,@body)) 206 | (mac tr body `(tag tr ,@body)) 207 | 208 | (let pratoms (fn (body) 209 | (if (or (no body) 210 | (all [and (acons _) (isnt (car _) 'quote)] 211 | body)) 212 | body 213 | `((pr ,@body)))) 214 | 215 | (mac td body `(tag td ,@(pratoms body))) 216 | (mac trtd body `(tr (td ,@(pratoms body)))) 217 | (mac tdr body `(tag (td align 'right) ,@(pratoms body))) 218 | (mac tdcolor (col . body) `(tag (td bgcolor ,col) ,@(pratoms body))) 219 | ) 220 | 221 | (mac row args 222 | `(tr ,@(map [list 'td _] args))) 223 | 224 | (mac prrow args 225 | (w/uniq g 226 | `(tr ,@(map (fn (a) 227 | `(let ,g ,a 228 | (if (number ,g) 229 | (tdr (pr ,g)) 230 | (td (pr ,g))))) 231 | args)))) 232 | 233 | (mac prbold body `(tag b (pr ,@body))) 234 | 235 | (def para args 236 | (gentag p) 237 | (when args (apply pr args))) 238 | 239 | (def menu (name items (o sel nil)) 240 | (tag (select name name) 241 | (each i items 242 | (tag (option selected (is i sel)) 243 | (pr i))))) 244 | 245 | (mac whitepage body 246 | `(tag html 247 | (tag (body bgcolor white alink linkblue) ,@body))) 248 | 249 | (def errpage args (whitepage (apply prn args))) 250 | 251 | (def blank-url () "s.gif") 252 | 253 | ; Could memoize these. 254 | 255 | ; If h = 0, doesn't affect table column widths in some Netscapes. 256 | 257 | (def hspace (n) (gentag img src (blank-url) height 1 width n)) 258 | (def vspace (n) (gentag img src (blank-url) height n width 0)) 259 | (def vhspace (h w) (gentag img src (blank-url) height h width w)) 260 | 261 | (mac new-hspace (n) 262 | (if (number n) 263 | `(pr ,(string "")) 264 | `(pr ""))) 265 | 266 | ;(def spacerow (h) (tr (td (vspace h)))) 267 | 268 | (def spacerow (h) (pr "")) 269 | 270 | ; For use as nested table. 271 | 272 | (mac zerotable body 273 | `(tag (table border 0 cellpadding 0 cellspacing 0) 274 | ,@body)) 275 | 276 | ; was `(tag (table border 0 cellpadding 0 cellspacing 7) ,@body) 277 | 278 | (mac sptab body 279 | `(tag (table style "border-spacing: 7px 0px;") ,@body)) 280 | 281 | (mac widtable (w . body) 282 | `(tag (table width ,w) (tr (td ,@body)))) 283 | 284 | (def cellpr (x) (pr (or x " "))) 285 | 286 | (def but ((o text "submit") (o name nil)) 287 | (gentag input type 'submit name name value text)) 288 | 289 | (def submit ((o val "submit")) 290 | (gentag input type 'submit value val)) 291 | 292 | (def buts (name . texts) 293 | (if (no texts) 294 | (but) 295 | (do (but (car texts) name) 296 | (each text (cdr texts) 297 | (pr " ") 298 | (but text name))))) 299 | 300 | (mac spanrow (n . body) 301 | `(tr (tag (td colspan ,n) ,@body))) 302 | 303 | (mac form (action . body) 304 | `(tag (form method "post" action ,action) ,@body)) 305 | 306 | (mac textarea (name rows cols . body) 307 | `(tag (textarea name ,name rows ,rows cols ,cols) ,@body)) 308 | 309 | (def input (name (o val "") (o size 10)) 310 | (gentag input type 'text name name value val size size)) 311 | 312 | (mac inputs args 313 | `(tag (table border 0) 314 | ,@(map (fn ((name label len text)) 315 | (w/uniq (gl gt) 316 | `(let ,gl ,len 317 | (tr (td (pr ',label ":")) 318 | (if (isa ,gl 'cons) 319 | (td (textarea ',name (car ,gl) (cadr ,gl) 320 | (let ,gt ,text (if ,gt (pr ,gt))))) 321 | (td (gentag input type ',(if (is label 'password) 322 | 'password 323 | 'text) 324 | name ',name 325 | size ,len 326 | value ,text))))))) 327 | (tuples args 4)))) 328 | 329 | (def single-input (label name chars btext (o pwd)) 330 | (pr label) 331 | (gentag input type (if pwd 'password 'text) name name size chars) 332 | (sp) 333 | (submit btext)) 334 | 335 | (mac cdata body 336 | `(do (pr ""))) 339 | 340 | (def eschtml (str) 341 | (tostring 342 | (each c str 343 | (pr (case c #\< "<" 344 | #\> ">" 345 | #\" """ 346 | #\' "'" 347 | #\& "&" 348 | c))))) 349 | 350 | (def esc-tags (str) 351 | (tostring 352 | (each c str 353 | (pr (case c #\< "<" 354 | #\> ">" 355 | #\& "&" 356 | c))))) 357 | 358 | (def nbsp () (pr " ")) 359 | 360 | (def link (text (o dest text) (o color)) 361 | (tag (a href dest) 362 | (tag-if color (font color color) 363 | (pr text)))) 364 | 365 | (def underlink (text (o dest text)) 366 | (tag (a href dest) (tag u (pr text)))) 367 | 368 | (def striptags (s) 369 | (let intag nil 370 | (tostring 371 | (each c s 372 | (if (is c #\<) (set intag) 373 | (is c #\>) (wipe intag) 374 | (no intag) (pr c)))))) 375 | 376 | (def clean-url (u) 377 | (rem [in _ #\" #\' #\< #\>] u)) 378 | 379 | (def shortlink (url) 380 | (unless (or (no url) (< (len url) 7)) 381 | (link (cut url 7) url))) 382 | 383 | ; this should be one regexp 384 | 385 | (def parafy (str) 386 | (let ink nil 387 | (tostring 388 | (each c str 389 | (pr c) 390 | (unless (whitec c) (set ink)) 391 | (when (is c #\newline) 392 | (unless ink (pr "

")) 393 | (wipe ink)))))) 394 | 395 | (mac spanclass (name . body) 396 | `(tag (span class ',name) ,@body)) 397 | 398 | (def pagemessage (text) 399 | (when text (prn text) (br2))) 400 | 401 | ; Could be stricter. Memoized because looking for chars in Unicode 402 | ; strings is terribly inefficient in Mzscheme. 403 | 404 | (defmemo valid-url (url) 405 | (and (len> url 10) 406 | (or (begins url "http://") 407 | (begins url "https://")) 408 | (~find [in _ #\< #\> #\" #\'] url))) 409 | 410 | (mac fontcolor (c . body) 411 | (w/uniq g 412 | `(let ,g ,c 413 | (if ,g 414 | (tag (font color ,g) ,@body) 415 | (do ,@body))))) 416 | -------------------------------------------------------------------------------- /arc3.1/js.arc: -------------------------------------------------------------------------------- 1 | (def butlast (xs) 2 | (firstn (- (len xs) 1) xs)) 3 | 4 | ; '(a b c d . e) => '(a b c d e) 5 | 6 | (def nil-terminate (xs) 7 | (if (no xs) 8 | nil 9 | (and (cdr xs) (atom (cdr xs))) 10 | (cons (car xs) (cons (cdr xs) nil)) 11 | (cons (car xs) (nil-terminate (cdr xs))))) 12 | 13 | (let nest-lev 0 14 | 15 | (def js-q () 16 | (repeat nest-lev (pr #\\)) 17 | (pr #\")) 18 | 19 | (def js-open-q () 20 | (js-q) 21 | (= nest-lev (+ 1 (* 2 nest-lev)))) 22 | 23 | (def js-close-q () 24 | (= nest-lev (/ (- nest-lev 1) 2)) 25 | (js-q))) 26 | 27 | (mac js-w/qs body 28 | `(do (js-open-q) 29 | ,@body 30 | (js-close-q))) 31 | 32 | (def js-quote (x) 33 | (if (acons x) 34 | (apply js-array x) 35 | (number x) 36 | (pr x) 37 | (js-w/qs (js1s x)))) 38 | 39 | (def js-charesc (c) 40 | (case c #\newline (pr "\\n") 41 | #\tab (pr "\\t") 42 | #\return (pr "\\r") 43 | #\\ (pr "\\\\") 44 | #\' (js-q) 45 | (pr c))) 46 | 47 | ; an eachif would make conditional unnecessary 48 | 49 | (def js-str/charesc (c/s) 50 | (js-w/qs 51 | (if (isa c/s 'char) (js-charesc c/s) 52 | (isa c/s 'string) (each c c/s 53 | (js-charesc c))))) 54 | 55 | (def js-infix (op . args) 56 | (between a args (pr op) 57 | (js1s a))) 58 | 59 | (def js-infix-w/parens (op . args) 60 | (pr #\() 61 | (apply js-infix op args) 62 | (pr #\))) 63 | 64 | (def js-w/commas (xs) 65 | (apply js-infix #\, xs)) 66 | 67 | (def js-obj args 68 | (pr #\{) 69 | (between (k v) (pair args) (pr #\,) 70 | (js1s k) 71 | (pr #\:) 72 | (js1s v)) 73 | (pr #\})) 74 | 75 | (def js-array args 76 | (pr #\[) 77 | (js-w/commas args) 78 | (pr #\])) 79 | 80 | (def js-ref args 81 | (js1s (car args)) 82 | (each a (cdr args) 83 | (pr #\[) 84 | (js1s a) 85 | (pr #\]))) 86 | 87 | (def arglist (xs) 88 | (pr #\() 89 | (js-w/commas xs) 90 | (pr #\))) 91 | 92 | (def js-fncall (f . args) 93 | (js1s f) 94 | (arglist args)) 95 | 96 | (def js-call1 (x arg) 97 | (if (and (acons arg) (is (car arg) 'quasiquote)) 98 | (js-ref x (cons 'quote (cdr arg))) 99 | (js-fncall x arg))) 100 | 101 | (def js-call (x . arg/s) 102 | (if (single arg/s) 103 | (apply js-call1 x arg/s) 104 | (apply js-fncall x arg/s))) 105 | 106 | (def js-new (C . args) 107 | (pr "new ") 108 | (js1s `(,C ,@args))) 109 | 110 | (def js-typeof args 111 | (pr "typeof ") 112 | (each a args 113 | (js1s a))) 114 | 115 | ; bad name when everything is an expression 116 | 117 | (def retblock (exprs) 118 | (pr #\{ 119 | "return ") 120 | (js-w/commas exprs) 121 | (pr #\; #\})) 122 | 123 | (def js-fn (args . body) 124 | (pr #\( "function") 125 | (if (no args) 126 | (do (arglist nil) 127 | (retblock body)) 128 | (atom args) 129 | (do (arglist nil) 130 | (retblock 131 | (cons `(= ,args 132 | (Array.prototype.slice.call 133 | arguments)) 134 | body))) 135 | (dotted args) 136 | (let args1 (nil-terminate args) 137 | (arglist (butlast args1)) 138 | (retblock 139 | (cons `(= ,(last args1) 140 | (Array.prototype.slice.call 141 | arguments 142 | ,(- (len args1) 1))) 143 | body))) 144 | (do (arglist args) 145 | (retblock body))) 146 | (pr #\))) 147 | 148 | (def js-if args 149 | (pr #\() 150 | (js1s (car args)) 151 | (each (then else) (pair (cdr args)) 152 | (pr #\?) 153 | (js1s then) 154 | (pr #\:) 155 | (js1s else)) 156 | (pr #\))) 157 | 158 | (def js-= args 159 | (between (var val) (pair args) (pr #\,) 160 | (js1s var) 161 | (pr #\=) 162 | (js1s val))) 163 | 164 | (def js-do exprs 165 | (pr #\() 166 | (js-w/commas exprs) 167 | (pr #\))) 168 | 169 | (def js-while (test . body) 170 | (pr "(function(){" 171 | "while(") (js1s test) (pr "){") 172 | (apply js-do body) 173 | (pr "}" 174 | "}).call(this)")) 175 | 176 | (= js-macs* (table)) 177 | 178 | (mac js-mac (name args . body) 179 | `(= (js-macs* ',name) (fn ,args (js1s ,@body)))) 180 | 181 | (def js1 (s) 182 | (if (caris s 'quote) (apply js-quote (cdr s)) 183 | (or (isa s 'char) 184 | (isa s 'string)) (js-str/charesc s) 185 | (no s) (pr 'null) 186 | (atom s) (pr s) 187 | (in (car s) '+ '- 188 | '* '/ '>= '<= 189 | '> '< '% '== 190 | '=== '!= '!== 191 | '+= '-= '*= '/= 192 | '%= '&& '\|\| 193 | '\,) (apply js-infix-w/parens s) 194 | (or (caris s '\.) 195 | (caris s '..)) (apply js-infix (cons '|.| (cdr s))) 196 | (caris s 'list) (apply js-array (cdr s)) 197 | (caris s 'obj) (apply js-obj (cdr s)) 198 | (caris s 'ref) (apply js-ref (cdr s)) 199 | (caris s 'new) (apply js-new (cdr s)) 200 | (caris s 'typeof) (apply js-typeof (cdr s)) 201 | (caris s 'do) (apply js-do (cdr s)) 202 | (caris s 'if) (apply js-if (cdr s)) 203 | (caris s 'fn) (apply js-fn (cdr s)) 204 | (caris s '=) (apply js-= (cdr s)) 205 | (caris s 'while) (apply js-while (cdr s)) 206 | (caris s 'mac) (eval `(js-mac ,@(cdr s))) 207 | (js-macs* (car s)) (apply (js-macs* (car s)) (cdr s)) 208 | (apply js-call s))) 209 | 210 | (def js1s args 211 | (between a args (pr #\,) 212 | (js1 a))) 213 | 214 | (def js-repl () 215 | (pr "sweet> ") 216 | (let expr (read) 217 | (if (iso expr '(sour)) 218 | (do (prn "Bye!") nil) 219 | (do (js expr) (js-repl))))) 220 | 221 | (def js args 222 | (if (no args) 223 | (do (prn "Welcome to SweetScript! Type (sour) to leave.") 224 | (js-repl)) 225 | (do (apply js1s args) 226 | (prn #\;)))) 227 | 228 | ; js alias 229 | (def sweet args (apply js args)) 230 | 231 | ; macros 232 | 233 | (js `(do 234 | 235 | (mac let (var val . body) 236 | (w/uniq gvar 237 | `(do (= ,gvar ,val) 238 | ,@(tree-subst var gvar body)))) 239 | 240 | (mac with (parms . body) 241 | (if (no parms) 242 | `(do ,@body) 243 | `(let ,(car parms) ,(cadr parms) 244 | (with ,(cddr parms) ,@body)))) 245 | 246 | (mac when (test . body) 247 | `(if ,test (do ,@body))) 248 | 249 | (mac unless (test . body) 250 | `(if (! ,test) (do ,@body))) 251 | 252 | (mac until (test . body) 253 | `(while (! ,test) ,@body)) 254 | 255 | (mac def (name parms . body) 256 | `(= ,name (fn ,parms ,@body))) 257 | 258 | ; html templating system inspired by html.arc 259 | ; 260 | ; sweet> (tag input (type "text") 261 | ; (tag ul () 262 | ; (tag li () "apples") 263 | ; (tag li () "bananas"))) 264 | ; (('<'+'input'+' '+('type'+'='+'\'text\''+' ')+'>')+(('<'+'ul'+'>')+(('<'+'li'+'>')+'apples'+(''))+(('<'+'li'+'>')+'bananas'+(''))+(''))+('')); 265 | 266 | (mac parse-attrs (attrs) 267 | (let acc nil 268 | (each (k v) (pair attrs) 269 | (= acc (+ acc `(',k "=" ',v " ")))) 270 | (push '+ acc) 271 | acc)) 272 | 273 | (mac start-tag (spec attrs) 274 | (if (no attrs) 275 | `(+ "<" ',spec ">") 276 | `(+ "<" ',spec " " (parse-attrs ,attrs) ">"))) 277 | 278 | (mac end-tag (spec) 279 | `(+ "")) 280 | 281 | (mac tag (spec attrs . body) 282 | `(+ (start-tag ,spec ,attrs) 283 | ,@body 284 | (end-tag ,spec))) 285 | 286 | ; jQuery helper macro 287 | ; Example usage: ($ "p.neat" 288 | ; (addClass "ohmy") 289 | ; (show "slow")) 290 | 291 | (mac $ (selector . args) 292 | `(.. (jQuery ,selector) ,@args)) 293 | 294 | ; Examples from http://documentcloud.github.com/underscore/#styles 295 | 296 | ; Collections 297 | 298 | (_.each [1 2 3] (fn (x) (alert x))) 299 | (_.each {one 1 two 2 three 3} (fn (x) (alert x))) 300 | 301 | (_.map [1 2 3] (fn (x) (* x 3))) 302 | (_.map {one 1 two 2 three 3} (fn (x) (* x 3))) 303 | 304 | (= sum (_.reduce [1 2 3] (fn (memo x) (+ memo x)) 0)) 305 | 306 | (= list [[0 1] [2 3] [4 5]] 307 | flat (_.reduceRight list (fn (a b) (.. a (concat b))) [])) 308 | 309 | (= even (_.detect [1 2 3 4 5 6] (fn (x) (== (% x 2) 0)))) 310 | 311 | ; alias select 312 | (= evens (_.filter [1 2 3 4 5 6] (fn (x) (== (% x 2) 0)))) 313 | 314 | (= odds (_.reject [1 2 3 4 5 6] (fn (x) (== (% x 2) 0)))) 315 | 316 | (_.all [true 1 null "yes"]) 317 | 318 | (_.any [true 1 null "yes"]) 319 | 320 | (_.include [1 2 3] 3) 321 | 322 | (_.invoke [[5 1 7] [3 2 1]] "sort") 323 | 324 | (let stooges [{name "moe" age 40} {name "larry" age 50} 325 | {name "curly" age 60}] 326 | (_.pluck stooges "name")) 327 | 328 | (let stooges [{name "moe" age 40} {name "larry" age 50} 329 | {name "curly" age 60}] 330 | (_.max stooges (fn (stooge) stooge.age))) 331 | 332 | (let numbers [10 5 100 2 1000] 333 | (_.min numbers)) 334 | 335 | (_.sortBy [1 2 3 4 5 6] (fn (x) (Math.sin x))) 336 | 337 | (_.sortedIndex [10 20 30 40 50] 35) 338 | 339 | ((fn () (_.toArray arguments (slice 0))) 1 2 3) 340 | 341 | (_.size {one 1 two 2 three 3}) 342 | 343 | ; Function (uh, ahem) Functions 344 | 345 | (let f (fn (greeting) 346 | (+ greeting ": " this.name)) 347 | (= f (_.bind f {name "moe"} "hi")) 348 | (f)) 349 | 350 | ; Example program 351 | ; Compiled output goes in static/sweet-example.js, which 352 | ; is linked to from static/sweet-example.html 353 | ; Depends on underscore.js and jQuery 354 | 355 | (do 356 | 357 | (= xs []) 358 | 359 | (def render () 360 | ($ "#xs" (empty)) 361 | (_.each xs (fn (x) 362 | ($ "#xs" (append (tag div () x)))))) 363 | 364 | ($ (tag input ()) 365 | (change (fn () 366 | (xs.unshift ($ this (val))) 367 | ($ this (val "")) 368 | (render))) 369 | (appendTo "body")) 370 | 371 | ($ (tag div (id "xs")) 372 | (appendTo "body"))) 373 | 374 | 375 | )) 376 | -------------------------------------------------------------------------------- /arc3.1/libs.arc: -------------------------------------------------------------------------------- 1 | (map load '("strings.arc" 2 | "pprint.arc" 3 | "code.arc" 4 | "html.arc" 5 | "srv.arc" 6 | "app.arc" 7 | "prompt.arc" 8 | 9 | "extend0.arc" 10 | "scheme0.arc" 11 | "extend-readtable0.arc" 12 | 13 | "skipwhite1.arc" 14 | "table-rw3.arc" 15 | "array.arc" 16 | 17 | "between0.arc" 18 | 19 | "js.arc" 20 | )) 21 | -------------------------------------------------------------------------------- /arc3.1/person.arc: -------------------------------------------------------------------------------- 1 | ; sweet-script example 2 | ; hacking with ryan, 12/28/10 3 | 4 | (js `(do 5 | 6 | 7 | (def personUpdateStern () 8 | (if (== this.laughter 0) 9 | (= this.stern true) 10 | (> this.laughter 5) 11 | (= this.stern)) 12 | this.stern) 13 | 14 | (def personUpdateLaughter () 15 | (if (! this.stern) 16 | (+= this.laughter 10) 17 | (do (-- this.laughter) 18 | (if (< this.laughter -10) 19 | (+= this.laughter 10000)))) 20 | this.stern) 21 | 22 | (def person () 23 | {laughter 0 24 | stern false 25 | updateStern personUpdateStern 26 | updateLaughter personUpdateLaughter}) 27 | 28 | (= evan (person) ryan (person)) 29 | 30 | 31 | )) 32 | -------------------------------------------------------------------------------- /arc3.1/pprint.arc: -------------------------------------------------------------------------------- 1 | ; Pretty-Printing. Spun off 4 Aug 06. 2 | 3 | ; todo: indentation of long ifs; quasiquote, unquote, unquote-splicing 4 | 5 | (= bodops* (fill-table (table) 6 | '(let 2 with 1 while 1 def 2 fn 1 rfn 2 afn 1 7 | when 1 unless 1 after 1 whilet 2 for 3 each 2 whenlet 2 awhen 1 8 | whitepage 0 tag 1 form 1 aform 1 aformh 1 w/link 1 textarea 3 9 | ))) 10 | 11 | (= oneline* 35) ; print exprs less than this long on one line 12 | 13 | ; If returns nil, can assume it didn't have to break expr. 14 | 15 | (def ppr (expr (o col 0) (o noindent nil)) 16 | (if (or (atom expr) (dotted expr)) 17 | (do (unless noindent (sp col)) 18 | (write expr) 19 | nil) 20 | (is (car expr) 'quote) 21 | (do (unless noindent (sp col)) 22 | (pr "'") 23 | (ppr (cadr expr) (+ col 1) t)) 24 | (bodops* (car expr)) 25 | (do (unless noindent (sp col)) 26 | (let whole (tostring (write expr)) 27 | (if (< (len whole) oneline*) 28 | (do (pr whole) nil) 29 | (ppr-progn expr col noindent)))) 30 | (do (unless noindent (sp col)) 31 | (let whole (tostring (write expr)) 32 | (if (< (len whole) oneline*) 33 | (do (pr whole) nil) 34 | (ppr-call expr col noindent)))))) 35 | 36 | (def ppr-progn (expr col noindent) 37 | (lpar) 38 | (let n (bodops* (car expr)) 39 | (let str (tostring (write-spaced (firstn n expr))) 40 | (unless (is n 0) (pr str) (sp)) 41 | (ppr (expr n) (+ col (len str) 2) t)) 42 | (map (fn (e) (prn) (ppr e (+ col 2))) 43 | (nthcdr (+ n 1) expr))) 44 | (rpar) 45 | t) 46 | 47 | (def ppr-call (expr col noindent) 48 | (lpar) 49 | (let carstr (tostring (write (car expr))) 50 | (pr carstr) 51 | (if (cdr expr) 52 | (do (sp) 53 | (let broke (ppr (cadr expr) (+ col (len carstr) 2) t) 54 | (pprest (cddr expr) 55 | (+ col (len carstr) 2) 56 | (no broke))) 57 | t) 58 | (do (rpar) t)))) 59 | 60 | (def pprest (exprs col (o oneline t)) 61 | (if (and oneline 62 | (all (fn (e) 63 | (or (atom e) (and (is (car e) 'quote) (atom (cadr e))))) 64 | exprs)) 65 | (do (map (fn (e) (pr " ") (write e)) 66 | exprs) 67 | (rpar)) 68 | (do (when exprs 69 | (each e exprs (prn) (ppr e col))) 70 | (rpar)))) 71 | 72 | (def write-spaced (xs) 73 | (when xs 74 | (write (car xs)) 75 | (each x (cdr xs) (pr " ") (write x)))) 76 | 77 | (def sp ((o n 1)) (repeat n (pr " "))) 78 | (def lpar () (pr "(")) 79 | (def rpar () (pr ")")) 80 | 81 | -------------------------------------------------------------------------------- /arc3.1/prompt.arc: -------------------------------------------------------------------------------- 1 | ; Prompt: Web-based programming application. 4 Aug 06. 2 | 3 | (= appdir* "arc/apps/") 4 | 5 | (defop prompt req 6 | (let user (get-user req) 7 | (if (admin user) 8 | (prompt-page user) 9 | (pr "Sorry.")))) 10 | 11 | (def prompt-page (user . msg) 12 | (ensure-dir appdir*) 13 | (ensure-dir (string appdir* user)) 14 | (whitepage 15 | (prbold "Prompt") 16 | (hspace 20) 17 | (pr user " | ") 18 | (link "logout") 19 | (when msg (hspace 10) (apply pr msg)) 20 | (br2) 21 | (tag (table border 0 cellspacing 10) 22 | (each app (dir (+ appdir* user)) 23 | (tr (td app) 24 | (td (ulink user 'edit (edit-app user app))) 25 | (td (ulink user 'run (run-app user app))) 26 | (td (hspace 40) 27 | (ulink user 'delete (rem-app user app)))))) 28 | (br2) 29 | (aform (fn (req) 30 | (when-umatch user req 31 | (aif (goodname (arg req "app")) 32 | (edit-app user it) 33 | (prompt-page user "Bad name.")))) 34 | (tab (row "name:" (input "app") (submit "create app")))))) 35 | 36 | (def app-path (user app) 37 | (and user app (+ appdir* user "/" app))) 38 | 39 | (def read-app (user app) 40 | (aand (app-path user app) 41 | (file-exists it) 42 | (readfile it))) 43 | 44 | (def write-app (user app exprs) 45 | (awhen (app-path user app) 46 | (w/outfile o it 47 | (each e exprs (write e o))))) 48 | 49 | (def rem-app (user app) 50 | (let file (app-path user app) 51 | (if (file-exists file) 52 | (do (rmfile (app-path user app)) 53 | (prompt-page user "Program " app " deleted.")) 54 | (prompt-page user "No such app.")))) 55 | 56 | (def edit-app (user app) 57 | (whitepage 58 | (pr "user: " user " app: " app) 59 | (br2) 60 | (aform (fn (req) 61 | (let u2 (get-user req) 62 | (if (is u2 user) 63 | (do (when (is (arg req "cmd") "save") 64 | (write-app user app (readall (arg req "exprs")))) 65 | (prompt-page user)) 66 | (login-page 'both nil 67 | (fn (u ip) (prompt-page u)))))) 68 | (textarea "exprs" 10 82 69 | (pprcode (read-app user app))) 70 | (br2) 71 | (buts 'cmd "save" "cancel")))) 72 | 73 | (def pprcode (exprs) 74 | (each e exprs 75 | (ppr e) 76 | (pr "\n\n"))) 77 | 78 | (def view-app (user app) 79 | (whitepage 80 | (pr "user: " user " app: " app) 81 | (br2) 82 | (tag xmp (pprcode (read-app user app))))) 83 | 84 | (def run-app (user app) 85 | (let exprs (read-app user app) 86 | (if exprs 87 | (on-err (fn (c) (pr "Error: " (details c))) 88 | (fn () (map eval exprs))) 89 | (prompt-page user "Error: No application " app " for user " user)))) 90 | 91 | (wipe repl-history*) 92 | 93 | (defop repl req 94 | (if (admin (get-user req)) 95 | (replpage req) 96 | (pr "Sorry."))) 97 | 98 | (def replpage (req) 99 | (whitepage 100 | (repl (readall (or (arg req "expr") "")) "repl"))) 101 | 102 | (def repl (exprs url) 103 | (each expr exprs 104 | (on-err (fn (c) (push (list expr c t) repl-history*)) 105 | (fn () 106 | (= that (eval expr) thatexpr expr) 107 | (push (list expr that) repl-history*)))) 108 | (form url 109 | (textarea "expr" 8 60) 110 | (sp) 111 | (submit)) 112 | (tag xmp 113 | (each (expr val err) (firstn 20 repl-history*) 114 | (pr "> ") 115 | (ppr expr) 116 | (prn) 117 | (prn (if err "Error: " "") 118 | (ellipsize (tostring (write val)) 800))))) 119 | 120 | -------------------------------------------------------------------------------- /arc3.1/scheme0.arc: -------------------------------------------------------------------------------- 1 | ; http://awwx.ws/scheme0 2 | ; modified! 3 | 4 | (extend ac (s env) (and (errsafe:acons s) (is (car s) 'scheme)) 5 | `(begin ,@(cdr s))) 6 | 7 | (= ac-denil (scheme ac-denil)) 8 | (= ac-global-name (scheme ac-global-name)) 9 | (= ac-niltree (scheme ac-niltree)) 10 | 11 | (mac ac-set-global (name val) 12 | (w/uniq (gname v) 13 | `(with (,gname (ac-global-name ,name) 14 | ,v ,val) 15 | (scheme (namespace-set-variable-value! ,gname ,v)) 16 | nil))) 17 | 18 | (= scheme-f (read "#f")) 19 | (= scheme-t (read "#t")) 20 | -------------------------------------------------------------------------------- /arc3.1/skipwhite1.arc: -------------------------------------------------------------------------------- 1 | ; place in own library to abide by the LGPL 2 | ; 3 | ; skip-whitespace is copied from 4 | ; http://download.plt-scheme.org/doc/352/html/mzscheme/mzscheme-Z-H-11.html#node_sec_11.2.8 5 | ; which has the following licence: 6 | ; 7 | ; Copyright ©1995-2006 Matthew Flatt 8 | ; 9 | ; Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Library General Public License, Version 2 published by the Free Software Foundation. 10 | ; 11 | ; [ ] in source changed to ( ) to avoid conflict with brackets.scm 12 | 13 | (scheme:define (skip-whitespace port) 14 | ;; Skips whitespace characters, sensitive to the current 15 | ;; readtable's definition of whitespace 16 | (let ((ch (peek-char port))) 17 | (unless (eof-object? ch) 18 | ;; Consult current readtable: 19 | (let-values (((like-ch/sym proc dispatch-proc) 20 | (readtable-mapping (current-readtable) ch))) 21 | ;; If like-ch/sym is whitespace, then ch is whitespace 22 | (when (and (char? like-ch/sym) 23 | (char-whitespace? like-ch/sym)) 24 | (read-char port) 25 | (skip-whitespace port)))))) 26 | -------------------------------------------------------------------------------- /arc3.1/srv.arc: -------------------------------------------------------------------------------- 1 | ; HTTP Server. 2 | 3 | ; To improve performance with static files, set static-max-age*. 4 | 5 | (= arcdir* "arc/" logdir* "arc/logs/" staticdir* "static/") 6 | 7 | (= quitsrv* nil breaksrv* nil) 8 | 9 | (def serve ((o port 8080)) 10 | (wipe quitsrv*) 11 | (ensure-srvdirs) 12 | (map [apply new-bgthread _] pending-bgthreads*) 13 | (w/socket s port 14 | (setuid 2) ; XXX switch from root to pg 15 | (prn "ready to serve port " port) 16 | (flushout) 17 | (= currsock* s) 18 | (until quitsrv* 19 | (handle-request s breaksrv*))) 20 | (prn "quit server")) 21 | 22 | (def serve1 ((o port 8080)) 23 | (w/socket s port (handle-request s t))) 24 | 25 | (def ensure-srvdirs () 26 | (map ensure-dir (list arcdir* logdir* staticdir*))) 27 | 28 | (= srv-noisy* nil) 29 | 30 | ; http requests currently capped at 2 meg by socket-accept 31 | 32 | ; should threads process requests one at a time? no, then 33 | ; a browser that's slow consuming the data could hang the 34 | ; whole server. 35 | 36 | ; wait for a connection from a browser and start a thread 37 | ; to handle it. also arrange to kill that thread if it 38 | ; has not completed in threadlife* seconds. 39 | 40 | (= threadlife* 30 requests* 0 requests/ip* (table) 41 | throttle-ips* (table) ignore-ips* (table) spurned* (table)) 42 | 43 | (def handle-request (s breaksrv) 44 | (if breaksrv 45 | (handle-request-1 s) 46 | (errsafe (handle-request-1 s)))) 47 | 48 | (def handle-request-1 (s) 49 | (let (i o ip) (socket-accept s) 50 | (if (and (or (ignore-ips* ip) (abusive-ip ip)) 51 | (++ (spurned* ip 0))) 52 | (force-close i o) 53 | (do (++ requests*) 54 | (++ (requests/ip* ip 0)) 55 | (with (th1 nil th2 nil) 56 | (= th1 (thread 57 | (after (handle-request-thread i o ip) 58 | (close i o) 59 | (kill-thread th2)))) 60 | (= th2 (thread 61 | (sleep threadlife*) 62 | (unless (dead th1) 63 | (prn "srv thread took too long for " ip)) 64 | (break-thread th1) 65 | (force-close i o)))))))) 66 | 67 | ; Returns true if ip has made req-limit* requests in less than 68 | ; req-window* seconds. If an ip is throttled, only 1 request is 69 | ; allowed per req-window* seconds. If an ip makes req-limit* 70 | ; requests in less than dos-window* seconds, it is a treated as a DoS 71 | ; attack and put in ignore-ips* (for this server invocation). 72 | 73 | ; To adjust this while running, adjust the req-window* time, not 74 | ; req-limit*, because algorithm doesn't enforce decreases in the latter. 75 | 76 | (= req-times* (table) req-limit* 30 req-window* 10 dos-window* 2) 77 | 78 | (def abusive-ip (ip) 79 | (and (only.> (requests/ip* ip) 250) 80 | (let now (seconds) 81 | (do1 (if (req-times* ip) 82 | (and (>= (qlen (req-times* ip)) 83 | (if (throttle-ips* ip) 1 req-limit*)) 84 | (let dt (- now (deq (req-times* ip))) 85 | (if (< dt dos-window*) (set (ignore-ips* ip))) 86 | (< dt req-window*))) 87 | (do (= (req-times* ip) (queue)) 88 | nil)) 89 | (enq now (req-times* ip)))))) 90 | 91 | (def handle-request-thread (i o ip) 92 | (with (nls 0 lines nil line nil responded nil t0 (msec)) 93 | (after 94 | (whilet c (unless responded (readc i)) 95 | (if srv-noisy* (pr c)) 96 | (if (is c #\newline) 97 | (if (is (++ nls) 2) 98 | (let (type op args n cooks) (parseheader (rev lines)) 99 | (let t1 (msec) 100 | (case type 101 | get (respond o op args cooks ip) 102 | post (handle-post i o op args n cooks ip) 103 | (respond-err o "Unknown request: " (car lines))) 104 | (log-request type op args cooks ip t0 t1) 105 | (set responded))) 106 | (do (push (string (rev line)) lines) 107 | (wipe line))) 108 | (unless (is c #\return) 109 | (push c line) 110 | (= nls 0)))) 111 | (close i o))) 112 | (harvest-fnids)) 113 | 114 | (def log-request (type op args cooks ip t0 t1) 115 | (with (parsetime (- t1 t0) respondtime (- (msec) t1)) 116 | (srvlog 'srv ip 117 | parsetime 118 | respondtime 119 | (if (> (+ parsetime respondtime) 1000) "***" "") 120 | type 121 | op 122 | (let arg1 (car args) 123 | (if (caris arg1 "fnid") "" arg1)) 124 | cooks))) 125 | 126 | ; Could ignore return chars (which come from textarea fields) here by 127 | ; (unless (is c #\return) (push c line)) 128 | 129 | (def handle-post (i o op args n cooks ip) 130 | (if srv-noisy* (pr "Post Contents: ")) 131 | (if (no n) 132 | (respond-err o "Post request without Content-Length.") 133 | (let line nil 134 | (whilet c (and (> n 0) (readc i)) 135 | (if srv-noisy* (pr c)) 136 | (-- n) 137 | (push c line)) 138 | (if srv-noisy* (pr "\n\n")) 139 | (respond o op (+ (parseargs (string (rev line))) args) cooks ip)))) 140 | 141 | (= header* "HTTP/1.1 200 OK 142 | Content-Type: text/html; charset=utf-8 143 | Connection: close") 144 | 145 | (= type-header* (table)) 146 | 147 | (def gen-type-header (ctype) 148 | (+ "HTTP/1.0 200 OK 149 | Content-Type: " 150 | ctype 151 | " 152 | Connection: close")) 153 | 154 | (map (fn ((k v)) (= (type-header* k) (gen-type-header v))) 155 | '((gif "image/gif") 156 | (jpg "image/jpeg") 157 | (png "image/png") 158 | (text/html "text/html; charset=utf-8"))) 159 | 160 | (= rdheader* "HTTP/1.0 302 Moved") 161 | 162 | (= srvops* (table) redirector* (table) optimes* (table) opcounts* (table)) 163 | 164 | (def save-optime (name elapsed) 165 | ; this is the place to put a/b testing 166 | ; toggle a flag and push elapsed into one of two lists 167 | (++ (opcounts* name 0)) 168 | (unless (optimes* name) (= (optimes* name) (queue))) 169 | (enq-limit elapsed (optimes* name) 1000)) 170 | 171 | ; For ops that want to add their own headers. They must thus remember 172 | ; to prn a blank line before anything meant to be part of the page. 173 | 174 | (mac defop-raw (name parms . body) 175 | (w/uniq t1 176 | `(= (srvops* ',name) 177 | (fn ,parms 178 | (let ,t1 (msec) 179 | (do1 (do ,@body) 180 | (save-optime ',name (- (msec) ,t1)))))))) 181 | 182 | (mac defopr-raw (name parms . body) 183 | `(= (redirector* ',name) t 184 | (srvops* ',name) (fn ,parms ,@body))) 185 | 186 | (mac defop (name parm . body) 187 | (w/uniq gs 188 | `(do (wipe (redirector* ',name)) 189 | (defop-raw ,name (,gs ,parm) 190 | (w/stdout ,gs (prn) ,@body))))) 191 | 192 | ; Defines op as a redirector. Its retval is new location. 193 | 194 | (mac defopr (name parm . body) 195 | (w/uniq gs 196 | `(do (set (redirector* ',name)) 197 | (defop-raw ,name (,gs ,parm) 198 | ,@body)))) 199 | 200 | ;(mac testop (name . args) `((srvops* ',name) ,@args)) 201 | 202 | (deftem request 203 | args nil 204 | cooks nil 205 | ip nil) 206 | 207 | (= unknown-msg* "Unknown." max-age* (table) static-max-age* nil) 208 | 209 | (def respond (str op args cooks ip) 210 | (w/stdout str 211 | (iflet f (srvops* op) 212 | (let req (inst 'request 'args args 'cooks cooks 'ip ip) 213 | (if (redirector* op) 214 | (do (prn rdheader*) 215 | (prn "Location: " (f str req)) 216 | (prn)) 217 | (do (prn header*) 218 | (awhen (max-age* op) 219 | (prn "Cache-Control: max-age=" it)) 220 | (f str req)))) 221 | (let filetype (static-filetype op) 222 | (aif (and filetype (file-exists (string staticdir* op))) 223 | (do (prn (type-header* filetype)) 224 | (awhen static-max-age* 225 | (prn "Cache-Control: max-age=" it)) 226 | (prn) 227 | (w/infile i it 228 | (whilet b (readb i) 229 | (writeb b str)))) 230 | (respond-err str unknown-msg*)))))) 231 | 232 | (def static-filetype (sym) 233 | (let fname (coerce sym 'string) 234 | (and (~find #\/ fname) 235 | (case (downcase (last (check (tokens fname #\.) ~single))) 236 | "gif" 'gif 237 | "jpg" 'jpg 238 | "jpeg" 'jpg 239 | "png" 'png 240 | "css" 'text/html 241 | "txt" 'text/html 242 | "htm" 'text/html 243 | "html" 'text/html 244 | "arc" 'text/html 245 | )))) 246 | 247 | (def respond-err (str msg . args) 248 | (w/stdout str 249 | (prn header*) 250 | (prn) 251 | (apply pr msg args))) 252 | 253 | (def parseheader (lines) 254 | (let (type op args) (parseurl (car lines)) 255 | (list type 256 | op 257 | args 258 | (and (is type 'post) 259 | (some (fn (s) 260 | (and (begins s "Content-Length:") 261 | (errsafe:coerce (cadr (tokens s)) 'int))) 262 | (cdr lines))) 263 | (some (fn (s) 264 | (and (begins s "Cookie:") 265 | (parsecookies s))) 266 | (cdr lines))))) 267 | 268 | ; (parseurl "GET /p1?foo=bar&ug etc") -> (get p1 (("foo" "bar") ("ug"))) 269 | 270 | (def parseurl (s) 271 | (let (type url) (tokens s) 272 | (let (base args) (tokens url #\?) 273 | (list (sym (downcase type)) 274 | (sym (cut base 1)) 275 | (if args 276 | (parseargs args) 277 | nil))))) 278 | 279 | ; I don't urldecode field names or anything in cookies; correct? 280 | 281 | (def parseargs (s) 282 | (map (fn ((k v)) (list k (urldecode v))) 283 | (map [tokens _ #\=] (tokens s #\&)))) 284 | 285 | (def parsecookies (s) 286 | (map [tokens _ #\=] 287 | (cdr (tokens s [or (whitec _) (is _ #\;)])))) 288 | 289 | (def arg (req key) (alref req!args key)) 290 | 291 | ; *** Warning: does not currently urlencode args, so if need to do 292 | ; that replace v with (urlencode v). 293 | 294 | (def reassemble-args (req) 295 | (aif req!args 296 | (apply string "?" (intersperse '& 297 | (map (fn ((k v)) 298 | (string k '= v)) 299 | it))) 300 | "")) 301 | 302 | (= fns* (table) fnids* nil timed-fnids* nil) 303 | 304 | ; count on huge (expt 64 10) size of fnid space to avoid clashes 305 | 306 | (def new-fnid () 307 | (check (sym (rand-string 10)) ~fns* (new-fnid))) 308 | 309 | (def fnid (f) 310 | (atlet key (new-fnid) 311 | (= (fns* key) f) 312 | (push key fnids*) 313 | key)) 314 | 315 | (def timed-fnid (lasts f) 316 | (atlet key (new-fnid) 317 | (= (fns* key) f) 318 | (push (list key (seconds) lasts) timed-fnids*) 319 | key)) 320 | 321 | ; Within f, it will be bound to the fn's own fnid. Remember that this is 322 | ; so low-level that need to generate the newline to separate from the headers 323 | ; within the body of f. 324 | 325 | (mac afnid (f) 326 | `(atlet it (new-fnid) 327 | (= (fns* it) ,f) 328 | (push it fnids*) 329 | it)) 330 | 331 | ;(defop test-afnid req 332 | ; (tag (a href (url-for (afnid (fn (req) (prn) (pr "my fnid is " it))))) 333 | ; (pr "click here"))) 334 | 335 | ; To be more sophisticated, instead of killing fnids, could first 336 | ; replace them with fns that tell the server it's harvesting too 337 | ; aggressively if they start to get called. But the right thing to 338 | ; do is estimate what the max no of fnids can be and set the harvest 339 | ; limit there-- beyond that the only solution is to buy more memory. 340 | 341 | (def harvest-fnids ((o n 50000)) ; was 20000 342 | (when (len> fns* n) 343 | (pull (fn ((id created lasts)) 344 | (when (> (since created) lasts) 345 | (wipe (fns* id)) 346 | t)) 347 | timed-fnids*) 348 | (atlet nharvest (trunc (/ n 10)) 349 | (let (kill keep) (split (rev fnids*) nharvest) 350 | (= fnids* (rev keep)) 351 | (each id kill 352 | (wipe (fns* id))))))) 353 | 354 | (= fnurl* "/x" rfnurl* "/r" rfnurl2* "/y" jfnurl* "/a") 355 | 356 | (= dead-msg* "\nUnknown or expired link.") 357 | 358 | (defop-raw x (str req) 359 | (w/stdout str 360 | (aif (fns* (sym (arg req "fnid"))) 361 | (it req) 362 | (pr dead-msg*)))) 363 | 364 | (defopr-raw y (str req) 365 | (aif (fns* (sym (arg req "fnid"))) 366 | (w/stdout str (it req)) 367 | "deadlink")) 368 | 369 | ; For asynchronous calls; discards the page. Would be better to tell 370 | ; the fn not to generate it. 371 | 372 | (defop-raw a (str req) 373 | (aif (fns* (sym (arg req "fnid"))) 374 | (tostring (it req)))) 375 | 376 | (defopr r req 377 | (aif (fns* (sym (arg req "fnid"))) 378 | (it req) 379 | "deadlink")) 380 | 381 | (defop deadlink req 382 | (pr dead-msg*)) 383 | 384 | (def url-for (fnid) 385 | (string fnurl* "?fnid=" fnid)) 386 | 387 | (def flink (f) 388 | (string fnurl* "?fnid=" (fnid (fn (req) (prn) (f req))))) 389 | 390 | (def rflink (f) 391 | (string rfnurl* "?fnid=" (fnid f))) 392 | 393 | ; Since it's just an expr, gensym a parm for (ignored) args. 394 | 395 | (mac w/link (expr . body) 396 | `(tag (a href (flink (fn (,(uniq)) ,expr))) 397 | ,@body)) 398 | 399 | (mac w/rlink (expr . body) 400 | `(tag (a href (rflink (fn (,(uniq)) ,expr))) 401 | ,@body)) 402 | 403 | (mac onlink (text . body) 404 | `(w/link (do ,@body) (pr ,text))) 405 | 406 | (mac onrlink (text . body) 407 | `(w/rlink (do ,@body) (pr ,text))) 408 | 409 | ; bad to have both flink and linkf; rename flink something like fnid-link 410 | 411 | (mac linkf (text parms . body) 412 | `(tag (a href (flink (fn ,parms ,@body))) (pr ,text))) 413 | 414 | (mac rlinkf (text parms . body) 415 | `(tag (a href (rflink (fn ,parms ,@body))) (pr ,text))) 416 | 417 | ;(defop top req (linkf 'whoami? (req) (pr "I am " (get-user req)))) 418 | 419 | ;(defop testf req (w/link (pr "ha ha ha") (pr "laugh"))) 420 | 421 | (mac w/link-if (test expr . body) 422 | `(tag-if ,test (a href (flink (fn (,(uniq)) ,expr))) 423 | ,@body)) 424 | 425 | (def fnid-field (id) 426 | (gentag input type 'hidden name 'fnid value id)) 427 | 428 | ; f should be a fn of one arg, which will be http request args. 429 | 430 | (def fnform (f bodyfn (o redir)) 431 | (tag (form method 'post action (if redir rfnurl2* fnurl*)) 432 | (fnid-field (fnid f)) 433 | (bodyfn))) 434 | 435 | ; Could also make a version that uses just an expr, and var capture. 436 | ; Is there a way to ensure user doesn't use "fnid" as a key? 437 | 438 | (mac aform (f . body) 439 | (w/uniq ga 440 | `(tag (form method 'post action fnurl*) 441 | (fnid-field (fnid (fn (,ga) 442 | (prn) 443 | (,f ,ga)))) 444 | ,@body))) 445 | 446 | ;(defop test1 req 447 | ; (fnform (fn (req) (prn) (pr req)) 448 | ; (fn () (single-input "" 'foo 20 "submit")))) 449 | 450 | ;(defop test2 req 451 | ; (aform (fn (req) (pr req)) 452 | ; (single-input "" 'foo 20 "submit"))) 453 | 454 | ; Like aform except creates a fnid that will last for lasts seconds 455 | ; (unless the server is restarted). 456 | 457 | (mac taform (lasts f . body) 458 | (w/uniq (gl gf gi ga) 459 | `(withs (,gl ,lasts 460 | ,gf (fn (,ga) (prn) (,f ,ga))) 461 | (tag (form method 'post action fnurl*) 462 | (fnid-field (if ,gl (timed-fnid ,gl ,gf) (fnid ,gf))) 463 | ,@body)))) 464 | 465 | (mac arform (f . body) 466 | `(tag (form method 'post action rfnurl*) 467 | (fnid-field (fnid ,f)) 468 | ,@body)) 469 | 470 | ; overlong 471 | 472 | (mac tarform (lasts f . body) 473 | (w/uniq (gl gf) 474 | `(withs (,gl ,lasts ,gf ,f) 475 | (tag (form method 'post action rfnurl*) 476 | (fnid-field (if ,gl (timed-fnid ,gl ,gf) (fnid ,gf))) 477 | ,@body)))) 478 | 479 | (mac aformh (f . body) 480 | `(tag (form method 'post action fnurl*) 481 | (fnid-field (fnid ,f)) 482 | ,@body)) 483 | 484 | (mac arformh (f . body) 485 | `(tag (form method 'post action rfnurl2*) 486 | (fnid-field (fnid ,f)) 487 | ,@body)) 488 | 489 | ; only unique per server invocation 490 | 491 | (= unique-ids* (table)) 492 | 493 | (def unique-id ((o len 8)) 494 | (let id (sym (rand-string (max 5 len))) 495 | (if (unique-ids* id) 496 | (unique-id) 497 | (= (unique-ids* id) id)))) 498 | 499 | (def srvlog (type . args) 500 | (w/appendfile o (logfile-name type) 501 | (w/stdout o (atomic (apply prs (seconds) args) (prn))))) 502 | 503 | (def logfile-name (type) 504 | (string logdir* type "-" (memodate))) 505 | 506 | (with (lastasked nil lastval nil) 507 | 508 | (def memodate () 509 | (let now (seconds) 510 | (if (or (no lastasked) (> (- now lastasked) 60)) 511 | (= lastasked now lastval (datestring)) 512 | lastval))) 513 | 514 | ) 515 | 516 | (defop || req (pr "It's alive.")) 517 | 518 | (defop topips req 519 | (when (admin (get-user req)) 520 | (whitepage 521 | (sptab 522 | (each ip (let leaders nil 523 | (maptable (fn (ip n) 524 | (when (> n 100) 525 | (insort (compare > requests/ip*) 526 | ip 527 | leaders))) 528 | requests/ip*) 529 | leaders) 530 | (let n (requests/ip* ip) 531 | (row ip n (pr (num (* 100 (/ n requests*)) 1))))))))) 532 | 533 | (defop spurned req 534 | (when (admin (get-user req)) 535 | (whitepage 536 | (sptab 537 | (map (fn ((ip n)) (row ip n)) 538 | (sortable spurned*)))))) 539 | 540 | ; eventually promote to general util 541 | 542 | (def sortable (ht (o f >)) 543 | (let res nil 544 | (maptable (fn kv 545 | (insort (compare f cadr) kv res)) 546 | ht) 547 | res)) 548 | 549 | 550 | ; Background Threads 551 | 552 | (= bgthreads* (table) pending-bgthreads* nil) 553 | 554 | (def new-bgthread (id f sec) 555 | (aif (bgthreads* id) (break-thread it)) 556 | (= (bgthreads* id) (new-thread (fn () 557 | (while t 558 | (sleep sec) 559 | (f)))))) 560 | 561 | ; should be a macro for this? 562 | 563 | (mac defbg (id sec . body) 564 | `(do (pull [caris _ ',id] pending-bgthreads*) 565 | (push (list ',id (fn () ,@body) ,sec) 566 | pending-bgthreads*))) 567 | 568 | 569 | 570 | ; Idea: make form fields that know their value type because of 571 | ; gensymed names, and so the receiving fn gets args that are not 572 | ; strings but parsed values. 573 | 574 | -------------------------------------------------------------------------------- /arc3.1/static/arc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evanrmurphy/SweetScript/6b4ed26acc9c2df6fde028f57b19ecb4661943fb/arc3.1/static/arc.png -------------------------------------------------------------------------------- /arc3.1/static/grayarrow.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evanrmurphy/SweetScript/6b4ed26acc9c2df6fde028f57b19ecb4661943fb/arc3.1/static/grayarrow.gif -------------------------------------------------------------------------------- /arc3.1/static/graydown.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evanrmurphy/SweetScript/6b4ed26acc9c2df6fde028f57b19ecb4661943fb/arc3.1/static/graydown.gif -------------------------------------------------------------------------------- /arc3.1/static/lis.py.txt: -------------------------------------------------------------------------------- 1 | ################ Lispy: Scheme Interpreter in Python 2 | 3 | ## (c) Peter Norvig, 2010; See http://norvig.com/lispy.html 4 | 5 | ################ Symbol, Procedure, Env classes 6 | 7 | from __future__ import division 8 | 9 | Symbol = str 10 | 11 | class Env(dict): 12 | "An environment: a dict of {'var':val} pairs, with an outer Env." 13 | def __init__(self, parms=(), args=(), outer=None): 14 | self.update(zip(parms,args)) 15 | self.outer = outer 16 | def find(self, var): 17 | "Find the innermost Env where var appears." 18 | return self if var in self else self.outer.find(var) 19 | 20 | def add_globals(env): 21 | "Add some Scheme standard procedures to an environment." 22 | import math, operator as op 23 | env.update(vars(math)) # sin, sqrt, ... 24 | env.update( 25 | {'+':op.add, '-':op.sub, '*':op.mul, '/':op.div, 'not':op.not_, 26 | '>':op.gt, '<':op.lt, '>=':op.ge, '<=':op.le, '=':op.eq, 27 | 'equal?':op.eq, 'eq?':op.is_, 'length':len, 'cons':lambda x,y:[x]+y, 28 | 'car':lambda x:x[0],'cdr':lambda x:x[1:], 'append':op.add, 29 | 'list':lambda *x:list(x), 'list?': lambda x:isa(x,list), 30 | 'null?':lambda x:x==[], 'symbol?':lambda x: isa(x, Symbol)}) 31 | return env 32 | 33 | global_env = add_globals(Env()) 34 | 35 | isa = isinstance 36 | 37 | ################ eval 38 | 39 | def eval(x, env=global_env): 40 | "Evaluate an expression in an environment." 41 | if isa(x, Symbol): # variable reference 42 | return env.find(x)[x] 43 | elif not isa(x, list): # constant literal 44 | return x 45 | elif x[0] == 'quote': # (quote exp) 46 | (_, exp) = x 47 | return exp 48 | elif x[0] == 'if': # (if test conseq alt) 49 | (_, test, conseq, alt) = x 50 | return eval((conseq if eval(test, env) else alt), env) 51 | elif x[0] == 'set!': # (set! var exp) 52 | (_, var, exp) = x 53 | env.find(var)[var] = eval(exp, env) 54 | elif x[0] == 'define': # (define var exp) 55 | (_, var, exp) = x 56 | env[var] = eval(exp, env) 57 | elif x[0] == 'lambda': # (lambda (var*) exp) 58 | (_, vars, exp) = x 59 | return lambda *args: eval(exp, Env(vars, args, env)) 60 | elif x[0] == 'begin': # (begin exp*) 61 | for exp in x[1:]: 62 | val = eval(exp, env) 63 | return val 64 | else: # (proc exp*) 65 | exps = [eval(exp, env) for exp in x] 66 | proc = exps.pop(0) 67 | return proc(*exps) 68 | 69 | ################ parse, read, and user interaction 70 | 71 | def read(s): 72 | "Read a Scheme expression from a string." 73 | return read_from(tokenize(s)) 74 | 75 | parse = read 76 | 77 | def tokenize(s): 78 | "Convert a string into a list of tokens." 79 | return s.replace('(',' ( ').replace(')',' ) ').split() 80 | 81 | def read_from(tokens): 82 | "Read an expression from a sequence of tokens." 83 | if len(tokens) == 0: 84 | raise SyntaxError('unexpected EOF while reading') 85 | token = tokens.pop(0) 86 | if '(' == token: 87 | L = [] 88 | while tokens[0] != ')': 89 | L.append(read_from(tokens)) 90 | tokens.pop(0) # pop off ')' 91 | return L 92 | elif ')' == token: 93 | raise SyntaxError('unexpected )') 94 | else: 95 | return atom(token) 96 | 97 | def atom(token): 98 | "Numbers become numbers; every other token is a symbol." 99 | try: return int(token) 100 | except ValueError: 101 | try: return float(token) 102 | except ValueError: 103 | return Symbol(token) 104 | 105 | def to_string(exp): 106 | "Convert a Python object back into a Lisp-readable string." 107 | return '('+' '.join(map(to_string, exp))+')' if isa(exp, list) else str(exp) 108 | 109 | def repl(prompt='lis.py> '): 110 | "A prompt-read-eval-print loop." 111 | while True: 112 | val = eval(parse(raw_input(prompt))) 113 | if val is not None: print to_string(val) 114 | -------------------------------------------------------------------------------- /arc3.1/static/robots.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evanrmurphy/SweetScript/6b4ed26acc9c2df6fde028f57b19ecb4661943fb/arc3.1/static/robots.txt -------------------------------------------------------------------------------- /arc3.1/static/s.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evanrmurphy/SweetScript/6b4ed26acc9c2df6fde028f57b19ecb4661943fb/arc3.1/static/s.gif -------------------------------------------------------------------------------- /arc3.1/static/sweet-example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /arc3.1/static/sweet-example.js: -------------------------------------------------------------------------------- 1 | (xs=[],render=(function(){return jQuery("#xs").empty(),_.each(xs,(function(x){return jQuery("#xs").append((("<"+"div"+">")+x+("")));}));}),jQuery((("<"+"input"+">")+(""))).change((function(){return xs.unshift(jQuery(this).val()),jQuery(this).val(""),render();})).appendTo("body"),jQuery((("<"+"div"+" "+("id"+"="+"\"xs\""+" ")+">")+(""))).appendTo("body")); 2 | -------------------------------------------------------------------------------- /arc3.1/static/sweet.coffee: -------------------------------------------------------------------------------- 1 | # JavaScript port of http://norvig.com/lispy.html 2 | 3 | # Borrowed from http://javascript.crockford.com/remedial.html 4 | # to help distinguish arrays from other objects 5 | typeOf = (value) -> 6 | s = typeof value 7 | if s is 'object' 8 | if value 9 | if value instanceof Array 10 | s = 'array' 11 | else 12 | s = 'null' 13 | s 14 | 15 | isa = (x, y) -> 16 | typeOf(x) is y 17 | 18 | ################ Symbol, Procedure, Env classes 19 | 20 | Symbol = "string" 21 | list = "array" 22 | 23 | class Env 24 | constructor: (parms=[], args=[], outer=null) -> 25 | _(_.zip parms, args).each (keyVal) -> 26 | [key, val] = keyVal 27 | this[key] = val 28 | @outer = outer 29 | find: (Var) -> 30 | if Var of this then this else @outer?.find(Var) 31 | 32 | addGlobals = (env) -> 33 | _(env).extend 34 | '+': (x,y) -> x+y 35 | 'cons': (x,y) -> [x].concat(y) 36 | 'car': (xs) -> xs[0] 37 | 'cdr': (xs) -> xs[1..] 38 | env 39 | 40 | globalEnv = addGlobals(new Env) 41 | 42 | ################ Eval 43 | 44 | Eval = (x, env=globalEnv) -> 45 | console.log 'in Eval' 46 | console.log 'x is', x 47 | console.log 'env is', env 48 | if isa x, Symbol # variable reference 49 | console.log 'variable reference' 50 | env.find(x)[x] 51 | else if not isa x, list # constant literal 52 | console.log 'constant literal' 53 | x 54 | else if x[0] is 'quote' # (quote exp) 55 | [_, exp] = x 56 | exp 57 | else if x[0] is 'if' # (if test conseq alt) 58 | [_, test, conseq, alt] = x 59 | Eval (if Eval(test, env) then conseq else alt), env 60 | else if x[0] is '=' # (= var exp) 61 | console.log '(= var exp)' 62 | [_, Var, exp] = x 63 | if env.find(Var) 64 | env.find(Var)[Var] = Eval exp, env 65 | else 66 | env[Var] = Eval exp, env 67 | else if x[0] is 'fn' # (fn (var*) exp) 68 | [_, vars, exp] = x 69 | (args...) -> Eval exp, Env(vars, args, env) # should be new Env(vars...? 70 | else if x[0] is 'do' # (do exp*) 71 | val = Eval(exp, env) for exp in x[1..] 72 | val 73 | else # (proc exp*) 74 | console.log '(proc exp*)' 75 | exps = (Eval(exp, env) for exp in x) 76 | proc = exps.shift() 77 | console.log 'proc is', proc 78 | console.log 'exps is', exps 79 | proc exps... 80 | 81 | ################ parse, read and user interaction 82 | 83 | read = (s) -> 84 | readFrom tokenize(s) 85 | 86 | parse = read 87 | 88 | tokenize = (s) -> 89 | _(s.replace('(',' ( ').replace(')',' ) ').split(' ')).without('') 90 | 91 | readFrom = (tokens) -> 92 | if tokens.length == 0 93 | alert 'unexpected EOF while reading' 94 | token = tokens.shift() 95 | if '(' == token 96 | L = [] 97 | while tokens[0] != ')' 98 | L.push(readFrom tokens) 99 | tokens.shift() # pop off ')' 100 | L 101 | else if ')' == token 102 | alert 'unexpected )' 103 | else 104 | atom token 105 | 106 | # Still needs to distinguish numbers from symbols 107 | atom = (token) -> 108 | if token.match /^\d+\.?$/ 109 | parseInt token 110 | else if token.match /^\d*\.\d+$/ 111 | parseFloat token 112 | else 113 | "#{token}" 114 | 115 | ToString = (exp) -> 116 | if isa exp, list 117 | '(' + (_(exp).map ToString).join(' ') + ')' 118 | else 119 | exp.toString() 120 | 121 | # Could use better UI than prompt + alert 122 | repl = (p='sweet> ') -> 123 | while input != '(quit)' 124 | input = (prompt p) 125 | val = Eval(parse input) 126 | alert(ToString val) 127 | 128 | window.repl = repl 129 | window.read = read 130 | window.parse = parse 131 | window.tokenize = tokenize 132 | window.ToString = ToString 133 | window.atom = atom 134 | window.Env = Env 135 | window.globalEnv = globalEnv 136 | window.Eval = Eval 137 | 138 | repl() 139 | -------------------------------------------------------------------------------- /arc3.1/static/sweet.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /arc3.1/static/sweet.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var Env, Eval, Symbol, ToString, addGlobals, atom, globalEnv, isa, list, parse, read, readFrom, repl, tokenize, typeOf; 3 | var __slice = Array.prototype.slice; 4 | typeOf = function(value) { 5 | var s; 6 | s = typeof value; 7 | if (s === 'object') { 8 | if (value) { 9 | if (value instanceof Array) { 10 | s = 'array'; 11 | } else { 12 | s = 'null'; 13 | } 14 | } 15 | } 16 | return s; 17 | }; 18 | isa = function(x, y) { 19 | return typeOf(x) === y; 20 | }; 21 | Symbol = "string"; 22 | list = "array"; 23 | Env = function() { 24 | function Env(parms, args, outer) { 25 | if (parms == null) { 26 | parms = []; 27 | } 28 | if (args == null) { 29 | args = []; 30 | } 31 | if (outer == null) { 32 | outer = null; 33 | } 34 | _(_.zip(parms, args)).each(function(keyVal) { 35 | var key, val; 36 | key = keyVal[0], val = keyVal[1]; 37 | return this[key] = val; 38 | }); 39 | this.outer = outer; 40 | } 41 | Env.prototype.find = function(Var) { 42 | var _ref; 43 | if (Var in this) { 44 | return this; 45 | } else { 46 | return (_ref = this.outer) != null ? _ref.find(Var) : void 0; 47 | } 48 | }; 49 | return Env; 50 | }(); 51 | addGlobals = function(env) { 52 | _(env).extend({ 53 | '+': function(x, y) { 54 | return x + y; 55 | }, 56 | 'cons': function(x, y) { 57 | return [x].concat(y); 58 | }, 59 | 'car': function(xs) { 60 | return xs[0]; 61 | }, 62 | 'cdr': function(xs) { 63 | return xs.slice(1); 64 | } 65 | }); 66 | return env; 67 | }; 68 | globalEnv = addGlobals(new Env); 69 | Eval = function(x, env) { 70 | var Var, alt, conseq, exp, exps, proc, test, val, vars, _, _i, _j, _len, _len2, _ref, _results; 71 | if (env == null) { 72 | env = globalEnv; 73 | } 74 | console.log('in Eval'); 75 | console.log('x is', x); 76 | console.log('env is', env); 77 | if (isa(x, Symbol)) { 78 | console.log('variable reference'); 79 | return env.find(x)[x]; 80 | } else if (!isa(x, list)) { 81 | console.log('constant literal'); 82 | return x; 83 | } else if (x[0] === 'quote') { 84 | _ = x[0], exp = x[1]; 85 | return exp; 86 | } else if (x[0] === 'if') { 87 | _ = x[0], test = x[1], conseq = x[2], alt = x[3]; 88 | return Eval((Eval(test, env) ? conseq : alt), env); 89 | } else if (x[0] === '=') { 90 | console.log('(= var exp)'); 91 | _ = x[0], Var = x[1], exp = x[2]; 92 | if (env.find(Var)) { 93 | return env.find(Var)[Var] = Eval(exp, env); 94 | } else { 95 | return env[Var] = Eval(exp, env); 96 | } 97 | } else if (x[0] === 'fn') { 98 | _ = x[0], vars = x[1], exp = x[2]; 99 | return function() { 100 | var args; 101 | args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; 102 | return Eval(exp, Env(vars, args, env)); 103 | }; 104 | } else if (x[0] === 'do') { 105 | _ref = x.slice(1); 106 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 107 | exp = _ref[_i]; 108 | val = Eval(exp, env); 109 | } 110 | return val; 111 | } else { 112 | console.log('(proc exp*)'); 113 | exps = (function() { 114 | _results = []; 115 | for (_j = 0, _len2 = x.length; _j < _len2; _j++) { 116 | exp = x[_j]; 117 | _results.push(Eval(exp, env)); 118 | } 119 | return _results; 120 | }()); 121 | proc = exps.shift(); 122 | console.log('proc is', proc); 123 | console.log('exps is', exps); 124 | return proc.apply(proc, exps); 125 | } 126 | }; 127 | read = function(s) { 128 | return readFrom(tokenize(s)); 129 | }; 130 | parse = read; 131 | tokenize = function(s) { 132 | return _(s.replace('(', ' ( ').replace(')', ' ) ').split(' ')).without(''); 133 | }; 134 | readFrom = function(tokens) { 135 | var L, token; 136 | if (tokens.length === 0) { 137 | alert('unexpected EOF while reading'); 138 | } 139 | token = tokens.shift(); 140 | if ('(' === token) { 141 | L = []; 142 | while (tokens[0] !== ')') { 143 | L.push(readFrom(tokens)); 144 | } 145 | tokens.shift(); 146 | return L; 147 | } else if (')' === token) { 148 | return alert('unexpected )'); 149 | } else { 150 | return atom(token); 151 | } 152 | }; 153 | atom = function(token) { 154 | if (token.match(/^\d+\.?$/)) { 155 | return parseInt(token); 156 | } else if (token.match(/^\d*\.\d+$/)) { 157 | return parseFloat(token); 158 | } else { 159 | return "" + token; 160 | } 161 | }; 162 | ToString = function(exp) { 163 | if (isa(exp, list)) { 164 | return '(' + (_(exp).map(ToString)).join(' ') + ')'; 165 | } else { 166 | return exp.toString(); 167 | } 168 | }; 169 | repl = function(p) { 170 | var input, val, _results; 171 | if (p == null) { 172 | p = 'sweet> '; 173 | } 174 | _results = []; 175 | while (input !== '(quit)') { 176 | input = prompt(p); 177 | val = Eval(parse(input)); 178 | _results.push(alert(ToString(val))); 179 | } 180 | return _results; 181 | }; 182 | window.repl = repl; 183 | window.read = read; 184 | window.parse = parse; 185 | window.tokenize = tokenize; 186 | window.ToString = ToString; 187 | window.atom = atom; 188 | window.Env = Env; 189 | window.globalEnv = globalEnv; 190 | window.Eval = Eval; 191 | repl(); 192 | }).call(this); 193 | -------------------------------------------------------------------------------- /arc3.1/static/underscore.js: -------------------------------------------------------------------------------- 1 | // Underscore.js 1.1.3 2 | // (c) 2010 Jeremy Ashkenas, DocumentCloud Inc. 3 | // Underscore is freely distributable under the MIT license. 4 | // Portions of Underscore are inspired or borrowed from Prototype, 5 | // Oliver Steele's Functional, and John Resig's Micro-Templating. 6 | // For all details and documentation: 7 | // http://documentcloud.github.com/underscore 8 | 9 | (function() { 10 | 11 | // Baseline setup 12 | // -------------- 13 | 14 | // Establish the root object, `window` in the browser, or `global` on the server. 15 | var root = this; 16 | 17 | // Save the previous value of the `_` variable. 18 | var previousUnderscore = root._; 19 | 20 | // Establish the object that gets returned to break out of a loop iteration. 21 | var breaker = {}; 22 | 23 | // Save bytes in the minified (but not gzipped) version: 24 | var ArrayProto = Array.prototype, ObjProto = Object.prototype; 25 | 26 | // Create quick reference variables for speed access to core prototypes. 27 | var slice = ArrayProto.slice, 28 | unshift = ArrayProto.unshift, 29 | toString = ObjProto.toString, 30 | hasOwnProperty = ObjProto.hasOwnProperty; 31 | 32 | // All **ECMAScript 5** native function implementations that we hope to use 33 | // are declared here. 34 | var 35 | nativeForEach = ArrayProto.forEach, 36 | nativeMap = ArrayProto.map, 37 | nativeReduce = ArrayProto.reduce, 38 | nativeReduceRight = ArrayProto.reduceRight, 39 | nativeFilter = ArrayProto.filter, 40 | nativeEvery = ArrayProto.every, 41 | nativeSome = ArrayProto.some, 42 | nativeIndexOf = ArrayProto.indexOf, 43 | nativeLastIndexOf = ArrayProto.lastIndexOf, 44 | nativeIsArray = Array.isArray, 45 | nativeKeys = Object.keys; 46 | 47 | // Create a safe reference to the Underscore object for use below. 48 | var _ = function(obj) { return new wrapper(obj); }; 49 | 50 | // Export the Underscore object for **CommonJS**, with backwards-compatibility 51 | // for the old `require()` API. If we're not in CommonJS, add `_` to the 52 | // global object. 53 | if (typeof module !== 'undefined' && module.exports) { 54 | module.exports = _; 55 | _._ = _; 56 | } else { 57 | root._ = _; 58 | } 59 | 60 | // Current version. 61 | _.VERSION = '1.1.3'; 62 | 63 | // Collection Functions 64 | // -------------------- 65 | 66 | // The cornerstone, an `each` implementation, aka `forEach`. 67 | // Handles objects implementing `forEach`, arrays, and raw objects. 68 | // Delegates to **ECMAScript 5**'s native `forEach` if available. 69 | var each = _.each = _.forEach = function(obj, iterator, context) { 70 | var value; 71 | if (nativeForEach && obj.forEach === nativeForEach) { 72 | obj.forEach(iterator, context); 73 | } else if (_.isNumber(obj.length)) { 74 | for (var i = 0, l = obj.length; i < l; i++) { 75 | if (iterator.call(context, obj[i], i, obj) === breaker) return; 76 | } 77 | } else { 78 | for (var key in obj) { 79 | if (hasOwnProperty.call(obj, key)) { 80 | if (iterator.call(context, obj[key], key, obj) === breaker) return; 81 | } 82 | } 83 | } 84 | }; 85 | 86 | // Return the results of applying the iterator to each element. 87 | // Delegates to **ECMAScript 5**'s native `map` if available. 88 | _.map = function(obj, iterator, context) { 89 | if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context); 90 | var results = []; 91 | each(obj, function(value, index, list) { 92 | results[results.length] = iterator.call(context, value, index, list); 93 | }); 94 | return results; 95 | }; 96 | 97 | // **Reduce** builds up a single result from a list of values, aka `inject`, 98 | // or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available. 99 | _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) { 100 | var initial = memo !== void 0; 101 | if (nativeReduce && obj.reduce === nativeReduce) { 102 | if (context) iterator = _.bind(iterator, context); 103 | return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator); 104 | } 105 | each(obj, function(value, index, list) { 106 | if (!initial && index === 0) { 107 | memo = value; 108 | } else { 109 | memo = iterator.call(context, memo, value, index, list); 110 | } 111 | }); 112 | return memo; 113 | }; 114 | 115 | // The right-associative version of reduce, also known as `foldr`. 116 | // Delegates to **ECMAScript 5**'s native `reduceRight` if available. 117 | _.reduceRight = _.foldr = function(obj, iterator, memo, context) { 118 | if (nativeReduceRight && obj.reduceRight === nativeReduceRight) { 119 | if (context) iterator = _.bind(iterator, context); 120 | return memo !== void 0 ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator); 121 | } 122 | var reversed = (_.isArray(obj) ? obj.slice() : _.toArray(obj)).reverse(); 123 | return _.reduce(reversed, iterator, memo, context); 124 | }; 125 | 126 | // Return the first value which passes a truth test. Aliased as `detect`. 127 | _.find = _.detect = function(obj, iterator, context) { 128 | var result; 129 | any(obj, function(value, index, list) { 130 | if (iterator.call(context, value, index, list)) { 131 | result = value; 132 | return true; 133 | } 134 | }); 135 | return result; 136 | }; 137 | 138 | // Return all the elements that pass a truth test. 139 | // Delegates to **ECMAScript 5**'s native `filter` if available. 140 | // Aliased as `select`. 141 | _.filter = _.select = function(obj, iterator, context) { 142 | if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context); 143 | var results = []; 144 | each(obj, function(value, index, list) { 145 | if (iterator.call(context, value, index, list)) results[results.length] = value; 146 | }); 147 | return results; 148 | }; 149 | 150 | // Return all the elements for which a truth test fails. 151 | _.reject = function(obj, iterator, context) { 152 | var results = []; 153 | each(obj, function(value, index, list) { 154 | if (!iterator.call(context, value, index, list)) results[results.length] = value; 155 | }); 156 | return results; 157 | }; 158 | 159 | // Determine whether all of the elements match a truth test. 160 | // Delegates to **ECMAScript 5**'s native `every` if available. 161 | // Aliased as `all`. 162 | _.every = _.all = function(obj, iterator, context) { 163 | iterator = iterator || _.identity; 164 | if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context); 165 | var result = true; 166 | each(obj, function(value, index, list) { 167 | if (!(result = result && iterator.call(context, value, index, list))) return breaker; 168 | }); 169 | return result; 170 | }; 171 | 172 | // Determine if at least one element in the object matches a truth test. 173 | // Delegates to **ECMAScript 5**'s native `some` if available. 174 | // Aliased as `any`. 175 | var any = _.some = _.any = function(obj, iterator, context) { 176 | iterator = iterator || _.identity; 177 | if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context); 178 | var result = false; 179 | each(obj, function(value, index, list) { 180 | if (result = iterator.call(context, value, index, list)) return breaker; 181 | }); 182 | return result; 183 | }; 184 | 185 | // Determine if a given value is included in the array or object using `===`. 186 | // Aliased as `contains`. 187 | _.include = _.contains = function(obj, target) { 188 | if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1; 189 | var found = false; 190 | any(obj, function(value) { 191 | if (found = value === target) return true; 192 | }); 193 | return found; 194 | }; 195 | 196 | // Invoke a method (with arguments) on every item in a collection. 197 | _.invoke = function(obj, method) { 198 | var args = slice.call(arguments, 2); 199 | return _.map(obj, function(value) { 200 | return (method ? value[method] : value).apply(value, args); 201 | }); 202 | }; 203 | 204 | // Convenience version of a common use case of `map`: fetching a property. 205 | _.pluck = function(obj, key) { 206 | return _.map(obj, function(value){ return value[key]; }); 207 | }; 208 | 209 | // Return the maximum element or (element-based computation). 210 | _.max = function(obj, iterator, context) { 211 | if (!iterator && _.isArray(obj)) return Math.max.apply(Math, obj); 212 | var result = {computed : -Infinity}; 213 | each(obj, function(value, index, list) { 214 | var computed = iterator ? iterator.call(context, value, index, list) : value; 215 | computed >= result.computed && (result = {value : value, computed : computed}); 216 | }); 217 | return result.value; 218 | }; 219 | 220 | // Return the minimum element (or element-based computation). 221 | _.min = function(obj, iterator, context) { 222 | if (!iterator && _.isArray(obj)) return Math.min.apply(Math, obj); 223 | var result = {computed : Infinity}; 224 | each(obj, function(value, index, list) { 225 | var computed = iterator ? iterator.call(context, value, index, list) : value; 226 | computed < result.computed && (result = {value : value, computed : computed}); 227 | }); 228 | return result.value; 229 | }; 230 | 231 | // Sort the object's values by a criterion produced by an iterator. 232 | _.sortBy = function(obj, iterator, context) { 233 | return _.pluck(_.map(obj, function(value, index, list) { 234 | return { 235 | value : value, 236 | criteria : iterator.call(context, value, index, list) 237 | }; 238 | }).sort(function(left, right) { 239 | var a = left.criteria, b = right.criteria; 240 | return a < b ? -1 : a > b ? 1 : 0; 241 | }), 'value'); 242 | }; 243 | 244 | // Use a comparator function to figure out at what index an object should 245 | // be inserted so as to maintain order. Uses binary search. 246 | _.sortedIndex = function(array, obj, iterator) { 247 | iterator = iterator || _.identity; 248 | var low = 0, high = array.length; 249 | while (low < high) { 250 | var mid = (low + high) >> 1; 251 | iterator(array[mid]) < iterator(obj) ? low = mid + 1 : high = mid; 252 | } 253 | return low; 254 | }; 255 | 256 | // Safely convert anything iterable into a real, live array. 257 | _.toArray = function(iterable) { 258 | if (!iterable) return []; 259 | if (iterable.toArray) return iterable.toArray(); 260 | if (_.isArray(iterable)) return iterable; 261 | if (_.isArguments(iterable)) return slice.call(iterable); 262 | return _.values(iterable); 263 | }; 264 | 265 | // Return the number of elements in an object. 266 | _.size = function(obj) { 267 | return _.toArray(obj).length; 268 | }; 269 | 270 | // Array Functions 271 | // --------------- 272 | 273 | // Get the first element of an array. Passing **n** will return the first N 274 | // values in the array. Aliased as `head`. The **guard** check allows it to work 275 | // with `_.map`. 276 | _.first = _.head = function(array, n, guard) { 277 | return n && !guard ? slice.call(array, 0, n) : array[0]; 278 | }; 279 | 280 | // Returns everything but the first entry of the array. Aliased as `tail`. 281 | // Especially useful on the arguments object. Passing an **index** will return 282 | // the rest of the values in the array from that index onward. The **guard** 283 | // check allows it to work with `_.map`. 284 | _.rest = _.tail = function(array, index, guard) { 285 | return slice.call(array, _.isUndefined(index) || guard ? 1 : index); 286 | }; 287 | 288 | // Get the last element of an array. 289 | _.last = function(array) { 290 | return array[array.length - 1]; 291 | }; 292 | 293 | // Trim out all falsy values from an array. 294 | _.compact = function(array) { 295 | return _.filter(array, function(value){ return !!value; }); 296 | }; 297 | 298 | // Return a completely flattened version of an array. 299 | _.flatten = function(array) { 300 | return _.reduce(array, function(memo, value) { 301 | if (_.isArray(value)) return memo.concat(_.flatten(value)); 302 | memo[memo.length] = value; 303 | return memo; 304 | }, []); 305 | }; 306 | 307 | // Return a version of the array that does not contain the specified value(s). 308 | _.without = function(array) { 309 | var values = slice.call(arguments, 1); 310 | return _.filter(array, function(value){ return !_.include(values, value); }); 311 | }; 312 | 313 | // Produce a duplicate-free version of the array. If the array has already 314 | // been sorted, you have the option of using a faster algorithm. 315 | // Aliased as `unique`. 316 | _.uniq = _.unique = function(array, isSorted) { 317 | return _.reduce(array, function(memo, el, i) { 318 | if (0 == i || (isSorted === true ? _.last(memo) != el : !_.include(memo, el))) memo[memo.length] = el; 319 | return memo; 320 | }, []); 321 | }; 322 | 323 | // Produce an array that contains every item shared between all the 324 | // passed-in arrays. 325 | _.intersect = function(array) { 326 | var rest = slice.call(arguments, 1); 327 | return _.filter(_.uniq(array), function(item) { 328 | return _.every(rest, function(other) { 329 | return _.indexOf(other, item) >= 0; 330 | }); 331 | }); 332 | }; 333 | 334 | // Zip together multiple lists into a single array -- elements that share 335 | // an index go together. 336 | _.zip = function() { 337 | var args = slice.call(arguments); 338 | var length = _.max(_.pluck(args, 'length')); 339 | var results = new Array(length); 340 | for (var i = 0; i < length; i++) results[i] = _.pluck(args, "" + i); 341 | return results; 342 | }; 343 | 344 | // If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**), 345 | // we need this function. Return the position of the first occurrence of an 346 | // item in an array, or -1 if the item is not included in the array. 347 | // Delegates to **ECMAScript 5**'s native `indexOf` if available. 348 | _.indexOf = function(array, item) { 349 | if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item); 350 | for (var i = 0, l = array.length; i < l; i++) if (array[i] === item) return i; 351 | return -1; 352 | }; 353 | 354 | 355 | // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available. 356 | _.lastIndexOf = function(array, item) { 357 | if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) return array.lastIndexOf(item); 358 | var i = array.length; 359 | while (i--) if (array[i] === item) return i; 360 | return -1; 361 | }; 362 | 363 | // Generate an integer Array containing an arithmetic progression. A port of 364 | // the native Python `range()` function. See 365 | // [the Python documentation](http://docs.python.org/library/functions.html#range). 366 | _.range = function(start, stop, step) { 367 | var args = slice.call(arguments), 368 | solo = args.length <= 1, 369 | start = solo ? 0 : args[0], 370 | stop = solo ? args[0] : args[1], 371 | step = args[2] || 1, 372 | len = Math.max(Math.ceil((stop - start) / step), 0), 373 | idx = 0, 374 | range = new Array(len); 375 | while (idx < len) { 376 | range[idx++] = start; 377 | start += step; 378 | } 379 | return range; 380 | }; 381 | 382 | // Function (ahem) Functions 383 | // ------------------ 384 | 385 | // Create a function bound to a given object (assigning `this`, and arguments, 386 | // optionally). Binding with arguments is also known as `curry`. 387 | _.bind = function(func, obj) { 388 | var args = slice.call(arguments, 2); 389 | return function() { 390 | return func.apply(obj || {}, args.concat(slice.call(arguments))); 391 | }; 392 | }; 393 | 394 | // Bind all of an object's methods to that object. Useful for ensuring that 395 | // all callbacks defined on an object belong to it. 396 | _.bindAll = function(obj) { 397 | var funcs = slice.call(arguments, 1); 398 | if (funcs.length == 0) funcs = _.functions(obj); 399 | each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); }); 400 | return obj; 401 | }; 402 | 403 | // Memoize an expensive function by storing its results. 404 | _.memoize = function(func, hasher) { 405 | var memo = {}; 406 | hasher = hasher || _.identity; 407 | return function() { 408 | var key = hasher.apply(this, arguments); 409 | return key in memo ? memo[key] : (memo[key] = func.apply(this, arguments)); 410 | }; 411 | }; 412 | 413 | // Delays a function for the given number of milliseconds, and then calls 414 | // it with the arguments supplied. 415 | _.delay = function(func, wait) { 416 | var args = slice.call(arguments, 2); 417 | return setTimeout(function(){ return func.apply(func, args); }, wait); 418 | }; 419 | 420 | // Defers a function, scheduling it to run after the current call stack has 421 | // cleared. 422 | _.defer = function(func) { 423 | return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1))); 424 | }; 425 | 426 | // Internal function used to implement `_.throttle` and `_.debounce`. 427 | var limit = function(func, wait, debounce) { 428 | var timeout; 429 | return function() { 430 | var context = this, args = arguments; 431 | var throttler = function() { 432 | timeout = null; 433 | func.apply(context, args); 434 | }; 435 | if (debounce) clearTimeout(timeout); 436 | if (debounce || !timeout) timeout = setTimeout(throttler, wait); 437 | }; 438 | }; 439 | 440 | // Returns a function, that, when invoked, will only be triggered at most once 441 | // during a given window of time. 442 | _.throttle = function(func, wait) { 443 | return limit(func, wait, false); 444 | }; 445 | 446 | // Returns a function, that, as long as it continues to be invoked, will not 447 | // be triggered. The function will be called after it stops being called for 448 | // N milliseconds. 449 | _.debounce = function(func, wait) { 450 | return limit(func, wait, true); 451 | }; 452 | 453 | // Returns the first function passed as an argument to the second, 454 | // allowing you to adjust arguments, run code before and after, and 455 | // conditionally execute the original function. 456 | _.wrap = function(func, wrapper) { 457 | return function() { 458 | var args = [func].concat(slice.call(arguments)); 459 | return wrapper.apply(wrapper, args); 460 | }; 461 | }; 462 | 463 | // Returns a function that is the composition of a list of functions, each 464 | // consuming the return value of the function that follows. 465 | _.compose = function() { 466 | var funcs = slice.call(arguments); 467 | return function() { 468 | var args = slice.call(arguments); 469 | for (var i=funcs.length-1; i >= 0; i--) { 470 | args = [funcs[i].apply(this, args)]; 471 | } 472 | return args[0]; 473 | }; 474 | }; 475 | 476 | // Object Functions 477 | // ---------------- 478 | 479 | // Retrieve the names of an object's properties. 480 | // Delegates to **ECMAScript 5**'s native `Object.keys` 481 | _.keys = nativeKeys || function(obj) { 482 | if (_.isArray(obj)) return _.range(0, obj.length); 483 | var keys = []; 484 | for (var key in obj) if (hasOwnProperty.call(obj, key)) keys[keys.length] = key; 485 | return keys; 486 | }; 487 | 488 | // Retrieve the values of an object's properties. 489 | _.values = function(obj) { 490 | return _.map(obj, _.identity); 491 | }; 492 | 493 | // Return a sorted list of the function names available on the object. 494 | // Aliased as `methods` 495 | _.functions = _.methods = function(obj) { 496 | return _.filter(_.keys(obj), function(key){ return _.isFunction(obj[key]); }).sort(); 497 | }; 498 | 499 | // Extend a given object with all the properties in passed-in object(s). 500 | _.extend = function(obj) { 501 | each(slice.call(arguments, 1), function(source) { 502 | for (var prop in source) obj[prop] = source[prop]; 503 | }); 504 | return obj; 505 | }; 506 | 507 | // Create a (shallow-cloned) duplicate of an object. 508 | _.clone = function(obj) { 509 | return _.isArray(obj) ? obj.slice() : _.extend({}, obj); 510 | }; 511 | 512 | // Invokes interceptor with the obj, and then returns obj. 513 | // The primary purpose of this method is to "tap into" a method chain, in 514 | // order to perform operations on intermediate results within the chain. 515 | _.tap = function(obj, interceptor) { 516 | interceptor(obj); 517 | return obj; 518 | }; 519 | 520 | // Perform a deep comparison to check if two objects are equal. 521 | _.isEqual = function(a, b) { 522 | // Check object identity. 523 | if (a === b) return true; 524 | // Different types? 525 | var atype = typeof(a), btype = typeof(b); 526 | if (atype != btype) return false; 527 | // Basic equality test (watch out for coercions). 528 | if (a == b) return true; 529 | // One is falsy and the other truthy. 530 | if ((!a && b) || (a && !b)) return false; 531 | // One of them implements an isEqual()? 532 | if (a.isEqual) return a.isEqual(b); 533 | // Check dates' integer values. 534 | if (_.isDate(a) && _.isDate(b)) return a.getTime() === b.getTime(); 535 | // Both are NaN? 536 | if (_.isNaN(a) && _.isNaN(b)) return false; 537 | // Compare regular expressions. 538 | if (_.isRegExp(a) && _.isRegExp(b)) 539 | return a.source === b.source && 540 | a.global === b.global && 541 | a.ignoreCase === b.ignoreCase && 542 | a.multiline === b.multiline; 543 | // If a is not an object by this point, we can't handle it. 544 | if (atype !== 'object') return false; 545 | // Check for different array lengths before comparing contents. 546 | if (a.length && (a.length !== b.length)) return false; 547 | // Nothing else worked, deep compare the contents. 548 | var aKeys = _.keys(a), bKeys = _.keys(b); 549 | // Different object sizes? 550 | if (aKeys.length != bKeys.length) return false; 551 | // Recursive comparison of contents. 552 | for (var key in a) if (!(key in b) || !_.isEqual(a[key], b[key])) return false; 553 | return true; 554 | }; 555 | 556 | // Is a given array or object empty? 557 | _.isEmpty = function(obj) { 558 | if (_.isArray(obj) || _.isString(obj)) return obj.length === 0; 559 | for (var key in obj) if (hasOwnProperty.call(obj, key)) return false; 560 | return true; 561 | }; 562 | 563 | // Is a given value a DOM element? 564 | _.isElement = function(obj) { 565 | return !!(obj && obj.nodeType == 1); 566 | }; 567 | 568 | // Is a given value an array? 569 | // Delegates to ECMA5's native Array.isArray 570 | _.isArray = nativeIsArray || function(obj) { 571 | return !!(obj && obj.concat && obj.unshift && !obj.callee); 572 | }; 573 | 574 | // Is a given variable an arguments object? 575 | _.isArguments = function(obj) { 576 | return !!(obj && obj.callee); 577 | }; 578 | 579 | // Is a given value a function? 580 | _.isFunction = function(obj) { 581 | return !!(obj && obj.constructor && obj.call && obj.apply); 582 | }; 583 | 584 | // Is a given value a string? 585 | _.isString = function(obj) { 586 | return !!(obj === '' || (obj && obj.charCodeAt && obj.substr)); 587 | }; 588 | 589 | // Is a given value a number? 590 | _.isNumber = function(obj) { 591 | return !!(obj === 0 || (obj && obj.toExponential && obj.toFixed)); 592 | }; 593 | 594 | // Is the given value NaN -- this one is interesting. NaN != NaN, and 595 | // isNaN(undefined) == true, so we make sure it's a number first. 596 | _.isNaN = function(obj) { 597 | return toString.call(obj) === '[object Number]' && isNaN(obj); 598 | }; 599 | 600 | // Is a given value a boolean? 601 | _.isBoolean = function(obj) { 602 | return obj === true || obj === false; 603 | }; 604 | 605 | // Is a given value a date? 606 | _.isDate = function(obj) { 607 | return !!(obj && obj.getTimezoneOffset && obj.setUTCFullYear); 608 | }; 609 | 610 | // Is the given value a regular expression? 611 | _.isRegExp = function(obj) { 612 | return !!(obj && obj.test && obj.exec && (obj.ignoreCase || obj.ignoreCase === false)); 613 | }; 614 | 615 | // Is a given value equal to null? 616 | _.isNull = function(obj) { 617 | return obj === null; 618 | }; 619 | 620 | // Is a given variable undefined? 621 | _.isUndefined = function(obj) { 622 | return obj === void 0; 623 | }; 624 | 625 | // Utility Functions 626 | // ----------------- 627 | 628 | // Run Underscore.js in *noConflict* mode, returning the `_` variable to its 629 | // previous owner. Returns a reference to the Underscore object. 630 | _.noConflict = function() { 631 | root._ = previousUnderscore; 632 | return this; 633 | }; 634 | 635 | // Keep the identity function around for default iterators. 636 | _.identity = function(value) { 637 | return value; 638 | }; 639 | 640 | // Run a function **n** times. 641 | _.times = function (n, iterator, context) { 642 | for (var i = 0; i < n; i++) iterator.call(context, i); 643 | }; 644 | 645 | // Add your own custom functions to the Underscore object, ensuring that 646 | // they're correctly added to the OOP wrapper as well. 647 | _.mixin = function(obj) { 648 | each(_.functions(obj), function(name){ 649 | addToWrapper(name, _[name] = obj[name]); 650 | }); 651 | }; 652 | 653 | // Generate a unique integer id (unique within the entire client session). 654 | // Useful for temporary DOM ids. 655 | var idCounter = 0; 656 | _.uniqueId = function(prefix) { 657 | var id = idCounter++; 658 | return prefix ? prefix + id : id; 659 | }; 660 | 661 | // By default, Underscore uses ERB-style template delimiters, change the 662 | // following template settings to use alternative delimiters. 663 | _.templateSettings = { 664 | evaluate : /<%([\s\S]+?)%>/g, 665 | interpolate : /<%=([\s\S]+?)%>/g 666 | }; 667 | 668 | // JavaScript micro-templating, similar to John Resig's implementation. 669 | // Underscore templating handles arbitrary delimiters, preserves whitespace, 670 | // and correctly escapes quotes within interpolated code. 671 | _.template = function(str, data) { 672 | var c = _.templateSettings; 673 | var tmpl = 'var __p=[],print=function(){__p.push.apply(__p,arguments);};' + 674 | 'with(obj||{}){__p.push(\'' + 675 | str.replace(/\\/g, '\\\\') 676 | .replace(/'/g, "\\'") 677 | .replace(c.interpolate, function(match, code) { 678 | return "'," + code.replace(/\\'/g, "'") + ",'"; 679 | }) 680 | .replace(c.evaluate || null, function(match, code) { 681 | return "');" + code.replace(/\\'/g, "'") 682 | .replace(/[\r\n\t]/g, ' ') + "__p.push('"; 683 | }) 684 | .replace(/\r/g, '\\r') 685 | .replace(/\n/g, '\\n') 686 | .replace(/\t/g, '\\t') 687 | + "');}return __p.join('');"; 688 | var func = new Function('obj', tmpl); 689 | return data ? func(data) : func; 690 | }; 691 | 692 | // The OOP Wrapper 693 | // --------------- 694 | 695 | // If Underscore is called as a function, it returns a wrapped object that 696 | // can be used OO-style. This wrapper holds altered versions of all the 697 | // underscore functions. Wrapped objects may be chained. 698 | var wrapper = function(obj) { this._wrapped = obj; }; 699 | 700 | // Expose `wrapper.prototype` as `_.prototype` 701 | _.prototype = wrapper.prototype; 702 | 703 | // Helper function to continue chaining intermediate results. 704 | var result = function(obj, chain) { 705 | return chain ? _(obj).chain() : obj; 706 | }; 707 | 708 | // A method to easily add functions to the OOP wrapper. 709 | var addToWrapper = function(name, func) { 710 | wrapper.prototype[name] = function() { 711 | var args = slice.call(arguments); 712 | unshift.call(args, this._wrapped); 713 | return result(func.apply(_, args), this._chain); 714 | }; 715 | }; 716 | 717 | // Add all of the Underscore functions to the wrapper object. 718 | _.mixin(_); 719 | 720 | // Add all mutator Array functions to the wrapper. 721 | each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) { 722 | var method = ArrayProto[name]; 723 | wrapper.prototype[name] = function() { 724 | method.apply(this._wrapped, arguments); 725 | return result(this._wrapped, this._chain); 726 | }; 727 | }); 728 | 729 | // Add all accessor Array functions to the wrapper. 730 | each(['concat', 'join', 'slice'], function(name) { 731 | var method = ArrayProto[name]; 732 | wrapper.prototype[name] = function() { 733 | return result(method.apply(this._wrapped, arguments), this._chain); 734 | }; 735 | }); 736 | 737 | // Start chaining a wrapped Underscore object. 738 | wrapper.prototype.chain = function() { 739 | this._chain = true; 740 | return this; 741 | }; 742 | 743 | // Extracts the result from a wrapped and chained object. 744 | wrapper.prototype.value = function() { 745 | return this._wrapped; 746 | }; 747 | 748 | })(); 749 | -------------------------------------------------------------------------------- /arc3.1/strings.arc: -------------------------------------------------------------------------------- 1 | ; Matching. Spun off 29 Jul 06. 2 | 3 | ; arc> (tostring (writec (coerce 133 'char))) 4 | ; 5 | ;> (define ss (open-output-string)) 6 | ;> (write-char (integer->char 133) ss) 7 | ;> (get-output-string ss) 8 | ;"\u0085" 9 | 10 | (def tokens (s (o sep whitec)) 11 | (let test (testify sep) 12 | (let rec (afn (cs toks tok) 13 | (if (no cs) (consif tok toks) 14 | (test (car cs)) (self (cdr cs) (consif tok toks) nil) 15 | (self (cdr cs) toks (cons (car cs) tok)))) 16 | (rev (map [coerce _ 'string] 17 | (map rev (rec (coerce s 'cons) nil nil))))))) 18 | 19 | ; names of cut, split, halve not optimal 20 | 21 | (def halve (s (o sep whitec)) 22 | (let test (testify sep) 23 | (let rec (afn (cs tok) 24 | (if (no cs) (list (rev tok)) 25 | (test (car cs)) (list cs (rev tok)) 26 | (self (cdr cs) (cons (car cs) tok)))) 27 | (rev (map [coerce _ 'string] 28 | (rec (coerce s 'cons) nil)))))) 29 | 30 | ; maybe promote to arc.arc, but if so include a list clause 31 | 32 | (def positions (test seq) 33 | (accum a 34 | (let f (testify test) 35 | (forlen i seq 36 | (if (f (seq i)) (a i)))))) 37 | 38 | (def lines (s) 39 | (accum a 40 | ((afn ((p . ps)) 41 | (if ps 42 | (do (a (rem #\return (cut s (+ p 1) (car ps)))) 43 | (self ps)) 44 | (a (cut s (+ p 1))))) 45 | (cons -1 (positions #\newline s))))) 46 | 47 | (def slices (s test) 48 | (accum a 49 | ((afn ((p . ps)) 50 | (if ps 51 | (do (a (cut s (+ p 1) (car ps))) 52 | (self ps)) 53 | (a (cut s (+ p 1))))) 54 | (cons -1 (positions test s))))) 55 | 56 | ; > (require (lib "uri-codec.ss" "net")) 57 | ;> (form-urlencoded-decode "x%ce%bbx") 58 | ;"xλx" 59 | 60 | ; first byte: 0-7F, 1 char; c2-df 2; e0-ef 3, f0-f4 4. 61 | 62 | ; Fixed for utf8 by pc. 63 | 64 | (def urldecode (s) 65 | (tostring 66 | (forlen i s 67 | (caselet c (s i) 68 | #\+ (writec #\space) 69 | #\% (do (when (> (- (len s) i) 2) 70 | (writeb (int (cut s (+ i 1) (+ i 3)) 16))) 71 | (++ i 2)) 72 | (writec c))))) 73 | 74 | (def urlencode (s) 75 | (tostring 76 | (each c s 77 | (writec #\%) 78 | (let i (int c) 79 | (if (< i 16) (writec #\0)) 80 | (pr (coerce i 'string 16)))))) 81 | 82 | (mac litmatch (pat string (o start 0)) 83 | (w/uniq (gstring gstart) 84 | `(with (,gstring ,string ,gstart ,start) 85 | (unless (> (+ ,gstart ,(len pat)) (len ,gstring)) 86 | (and ,@(let acc nil 87 | (forlen i pat 88 | (push `(is ,(pat i) (,gstring (+ ,gstart ,i))) 89 | acc)) 90 | (rev acc))))))) 91 | 92 | ; litmatch would be cleaner if map worked for string and integer args: 93 | 94 | ; ,@(map (fn (n c) 95 | ; `(is ,c (,gstring (+ ,gstart ,n)))) 96 | ; (len pat) 97 | ; pat) 98 | 99 | (mac endmatch (pat string) 100 | (w/uniq (gstring glen) 101 | `(withs (,gstring ,string ,glen (len ,gstring)) 102 | (unless (> ,(len pat) (len ,gstring)) 103 | (and ,@(let acc nil 104 | (forlen i pat 105 | (push `(is ,(pat (- (len pat) 1 i)) 106 | (,gstring (- ,glen 1 ,i))) 107 | acc)) 108 | (rev acc))))))) 109 | 110 | (def posmatch (pat seq (o start 0)) 111 | (catch 112 | (if (isa pat 'fn) 113 | (for i start (- (len seq) 1) 114 | (when (pat (seq i)) (throw i))) 115 | (for i start (- (len seq) (len pat)) 116 | (when (headmatch pat seq i) (throw i)))) 117 | nil)) 118 | 119 | (def headmatch (pat seq (o start 0)) 120 | (let p (len pat) 121 | ((afn (i) 122 | (or (is i p) 123 | (and (is (pat i) (seq (+ i start))) 124 | (self (+ i 1))))) 125 | 0))) 126 | 127 | (def begins (seq pat (o start 0)) 128 | (unless (len> pat (- (len seq) start)) 129 | (headmatch pat seq start))) 130 | 131 | (def subst (new old seq) 132 | (let boundary (+ (- (len seq) (len old)) 1) 133 | (tostring 134 | (forlen i seq 135 | (if (and (< i boundary) (headmatch old seq i)) 136 | (do (++ i (- (len old) 1)) 137 | (pr new)) 138 | (pr (seq i))))))) 139 | 140 | (def multisubst (pairs seq) 141 | (tostring 142 | (forlen i seq 143 | (iflet (old new) (find [begins seq (car _) i] pairs) 144 | (do (++ i (- (len old) 1)) 145 | (pr new)) 146 | (pr (seq i)))))) 147 | 148 | ; not a good name 149 | 150 | (def findsubseq (pat seq (o start 0)) 151 | (if (< (- (len seq) start) (len pat)) 152 | nil 153 | (if (headmatch pat seq start) 154 | start 155 | (findsubseq pat seq (+ start 1))))) 156 | 157 | (def blank (s) (~find ~whitec s)) 158 | 159 | (def nonblank (s) (unless (blank s) s)) 160 | 161 | (def trim (s (o where 'both) (o test whitec)) 162 | (withs (f (testify test) 163 | p1 (pos ~f s)) 164 | (if p1 165 | (cut s 166 | (if (in where 'front 'both) p1 0) 167 | (when (in where 'end 'both) 168 | (let i (- (len s) 1) 169 | (while (and (> i p1) (f (s i))) 170 | (-- i)) 171 | (+ i 1)))) 172 | ""))) 173 | 174 | (def num (n (o digits 2) (o trail-zeros nil) (o init-zero nil)) 175 | (withs (comma 176 | (fn (i) 177 | (tostring 178 | (map [apply pr (rev _)] 179 | (rev (intersperse '(#\,) 180 | (tuples (rev (coerce (string i) 'cons)) 181 | 3)))))) 182 | abrep 183 | (let a (abs n) 184 | (if (< digits 1) 185 | (comma (roundup a)) 186 | (exact a) 187 | (string (comma a) 188 | (when (and trail-zeros (> digits 0)) 189 | (string "." (newstring digits #\0)))) 190 | (withs (d (expt 10 digits) 191 | m (/ (roundup (* a d)) d) 192 | i (trunc m) 193 | r (abs (trunc (- (* m d) (* i d))))) 194 | (+ (if (is i 0) 195 | (if (or init-zero (is r 0)) "0" "") 196 | (comma i)) 197 | (withs (rest (string r) 198 | padded (+ (newstring (- digits (len rest)) #\0) 199 | rest) 200 | final (if trail-zeros 201 | padded 202 | (trim padded 'end [is _ #\0]))) 203 | (string (unless (empty final) ".") 204 | final))))))) 205 | (if (and (< n 0) (find [and (digit _) (isnt _ #\0)] abrep)) 206 | (+ "-" abrep) 207 | abrep))) 208 | 209 | 210 | ; English 211 | 212 | (def pluralize (n str) 213 | (if (or (is n 1) (single n)) 214 | str 215 | (string str "s"))) 216 | 217 | (def plural (n x) 218 | (string n #\ (pluralize n x))) 219 | 220 | 221 | ; http://www.eki.ee/letter/chardata.cgi?HTML4=1 222 | ; http://jrgraphix.net/research/unicode_blocks.php?block=1 223 | ; http://home.tiscali.nl/t876506/utf8tbl.html 224 | ; http://www.fileformat.info/info/unicode/block/latin_supplement/utf8test.htm 225 | ; http://en.wikipedia.org/wiki/Utf-8 226 | ; http://unicode.org/charts/charindex2.html 227 | -------------------------------------------------------------------------------- /arc3.1/table-rw3.arc: -------------------------------------------------------------------------------- 1 | ; http://awwx.ws/table-rw3 2 | ; modified! 3 | 4 | (def parse-table-items (port (o acc nil)) 5 | ((scheme skip-whitespace) port) 6 | (if (is (peekc port) #\}) 7 | (do (readc port) `(obj ,@(rev acc))) 8 | (let x (read port) 9 | (push x acc) 10 | (parse-table-items port acc)))) 11 | 12 | (extend-readtable #\{ parse-table-items) 13 | --------------------------------------------------------------------------------