inline | arc0 | arc3.1


September 2015

This is a comparison of the first release of Arc to the latest version. Arc's codebase is an instance of bottom-up design: much of the code in the original is still present in the latest, and the diffs are mostly feature additions and bug fixes rather than refactors or rewrites.

Arc's compiler is written in Scheme, but the language itself is mostly written in Arc. This is possible because the compiler (ac.scm) provides a minimal set of basic features, like the ability to manipulate strings and lists, the ability to construct a function, and so on. Using these basic abilities, the language is able to define itself in arc.arc. It builds itself up similar to geometry: it uses the "axioms" provided by the compiler (assignment, comparison, etc) to define the various "theorems" of the language, like what it means to define a function or to declare a variable. And since your program is written in Arc, just like the language, you can do all of the same things that arc.arc does. You can bend the language into whatever shape is most convenient for your program. This design has a number of benefits, yielding a brief language with a short compiler.

Arc expressions are compiled to Scheme via the "ac" function, and the result is executed using eval. The original codebase runs on MzScheme 372, and the latest runs on Racket.

Left: arc0.tar
    1,157 lines of Scheme (Arc compiler)
    3,450 lines of Arc (core language + stdlibs)

Right: arc3.1.tar
    1,553 lines of Scheme (Arc compiler)
    3,946 lines of Arc (core language + stdlibs)
    2,618 lines of Arc (news.arc, HN's early codebase)

Unfortunately, I wasn't able to find arc1.tar, arc2.tar, or arc3.tar. Their download URLs broke long ago, and archive.org doesn't seem to have any copies. If you happen to have any of them, I would be very grateful if you'd send a copy to sudoarc@gmail.com. news.arc is absent from arc0, making it difficult to trace HN's evolutionary path.




    nonums


1 ./ac.scm = 1 ./ac.scm
2 ; scheme48 <>    
3 ; ,open tables sockets extended-ports c-system-function ascii i/o-internal       
4 ; ,open posix-files handle random pp simple-conditions      
5     2 ; Arc Compiler.
6 ; to do:      
7 ; select, perhaps with threads, or pltt events      
8 ; check argument count for complex arguments      
9   = 3  
10 ; refs.arc, first 300 lines of x, total CPU time including startup. on powerbook. +-    
11 ; scheme48: 31.944u 0.518s 2:13.65 24.2%    0+0k 5+7io 0pf+0w      
12 ; mzscheme: 16.425u 0.489s 0:52.61 32.1%    0+0k 26+22io 0pf+0w      
13        
14 ; dynamic creation of local variables with =      
15 ;   can you create globals inside a procedure?       
16 ;   does action of = depend on whether, at run time,      
17 ;     the variable has a global definition?      
18 ;   what's the scope of such a variable?      
19 ;   though a.lisp seems to create a global, not a local!      
20 ; run-time expansion of macros      
21 ; how do I know if something is a macro at compile time?      
22 ; macros have lexical scope. so how do i know if a lexical      
23 ;   variable is going to be bound to a procedure? or to a macro?      
24 ; what is annotate doing to symbols?      
25 ; tests.arc implies that '(a b) produces a mutable list      
26 ;   so (fn () '(a)) produces a list that can be modified,      
27 ;     and future calls will reflect the modifications      
28 ;   oy. clisp works this way too.      
29 ;   it's not that easy to simulate this.      
30 ; what is this? (def foo1 (x (o y x)) (list x y))      
31        
32 ; it's not clear I translate NILs in the outputs of macros correctly.      
33 ;   I translate (NIL . NIL) to ('NIL . '())      
34 ; I use Scheme apply to call macros.      
35 ;   Scheme apply demands a '()-terminated list.      
36 ;   most macros have a . body argument.      
37 ;   so body is '()-terminated, not NIL-terminated.      
38 ;   solution: ar-false? knows about '()      
39 ; this doesn't work, since var isn't a variable name:      
40 ; (mac or args      
41 ;   (and args      
42 ;        (let var (tag 'symbol (list 'or))      
43 ;          (list 'let var (car args)      
44 ;                (list 'if var var (cons 'or (cdr args)))))))      
45        
46 (module ac mzscheme = 4 (module ac mzscheme
47     5  
48 (provide (all-defined))   6 (provide (all-defined))
49 (require (lib "port.ss"))   7 (require (lib "port.ss"))
50 (require (lib "process.ss"))   8 (require (lib "process.ss"))
51 (require (lib "pretty.ss"))   9 (require (lib "pretty.ss"))
    -+ 10 (require (lib "foreign.ss"))
      11 (unsafe!)
52   = 12  
53 ; compile an Arc expression into a Scheme expression,   13 ; compile an Arc expression into a Scheme expression,
54 ; both represented as s-expressions.   14 ; both represented as s-expressions.
55 ; env is a list of lexically bound variables, which we   15 ; env is a list of lexically bound variables, which we
56 ; need in order to decide whether set should create a global.   16 ; need in order to decide whether set should create a global.
57     17  
58 (define (ac s env)   18 (define (ac s env)
59   (cond ((string? s) (string-copy s))  ; to avoid immutable strings <> 19   (cond ((string? s) (ac-string s env))
60         ((literal? s) s) = 20         ((literal? s) s)
61         ((eqv? s 'nil) (list 'quote 'nil))   21         ((eqv? s 'nil) (list 'quote 'nil))
62         ((ssyntax? s) (ac (expand-ssyntax s) env))   22         ((ssyntax? s) (ac (expand-ssyntax s) env))
63         ((symbol? s) (ac-var-ref s env))   23         ((symbol? s) (ac-var-ref s env))
64         ((ssyntax? (xcar s)) (ac (cons (expand-ssyntax (car s)) (cdr s)) env))   24         ((ssyntax? (xcar s)) (ac (cons (expand-ssyntax (car s)) (cdr s)) env))
65         ((eq? (xcar s) 'quote) (list 'quote (ac-niltree (cadr s))))   25         ((eq? (xcar s) 'quote) (list 'quote (ac-niltree (cadr s))))
66         ((eq? (xcar s) 'quasiquote) (ac-qq (cadr s) env))   26         ((eq? (xcar s) 'quasiquote) (ac-qq (cadr s) env))
67         ((eq? (xcar s) 'if) (ac-if (cdr s) env))   27         ((eq? (xcar s) 'if) (ac-if (cdr s) env))
68         ((eq? (xcar s) 'fn) (ac-fn (cadr s) (cddr s) env))   28         ((eq? (xcar s) 'fn) (ac-fn (cadr s) (cddr s) env))
69         ((eq? (xcar s) 'set) (ac-set (cdr s) env)) <> 29         ((eq? (xcar s) 'assign) (ac-set (cdr s) env))
70         ; this line could be removed without changing semantics   30         ; the next three clauses could be removed without changing semantics
      31         ; ... except that they work for macros (so prob should do this for
      32         ; every elt of s, not just the car)
71         ((eq? (xcar (xcar s)) 'compose) (ac (decompose (cdar s) (cdr s)) env)) = 33         ((eq? (xcar (xcar s)) 'compose) (ac (decompose (cdar s) (cdr s)) env))
    -+ 34         ((eq? (xcar (xcar s)) 'complement) 
      35          (ac (list 'no (cons (cadar s) (cdr s))) env))
      36         ((eq? (xcar (xcar s)) 'andf) (ac-andf s env))
72         ((pair? s) (ac-call (car s) (cdr s) env)) = 37         ((pair? s) (ac-call (car s) (cdr s) env))
73         (#t (err "Bad object in expression" s))))   38         (#t (err "Bad object in expression" s))))
    -+ 39  
      40 (define atstrings #f)
      41  
      42 (define (ac-string s env)
      43   (if atstrings
      44       (if (atpos s 0)
      45           (ac (cons 'string (map (lambda (x)
      46                                    (if (string? x)
      47                                        (unescape-ats x)
      48                                        x))
      49                                  (codestring s)))
      50               env)
      51           (unescape-ats s))
      52       (string-copy s)))          ; avoid immutable strings
74   = 53  
75 (define (literal? x)   54 (define (literal? x)
76   (or (boolean? x)   55   (or (boolean? x)
77       (char? x)   56       (char? x)
78       (string? x)   57       (string? x)
79       (number? x)   58       (number? x)
80       (eq? x '())))   59       (eq? x '())))
81     60  
82 (define (ssyntax? x)   61 (define (ssyntax? x)
83   (and (symbol? x)   62   (and (symbol? x)
84        (not (or (eqv? x '+) (eqv? x '++))) <> 63        (not (or (eqv? x '+) (eqv? x '++) (eqv? x '_)))
85        (let ((name (symbol->string x))) = 64        (let ((name (symbol->string x)))
86          (has-ssyntax-char? name (- (string-length name) 1)))))   65          (has-ssyntax-char? name (- (string-length name) 1)))))
87     66  
88 (define (has-ssyntax-char? string i)   67 (define (has-ssyntax-char? string i)
89   (and (>= i 0)   68   (and (>= i 0)
90        (or (let ((c (string-ref string i)))   69        (or (let ((c (string-ref string i)))
91              (or (eqv? c #\:) (eqv? c #\~))) <> 70              (or (eqv? c #\:) (eqv? c #\~)
      71                  (eqv? c #\&)
      72                  ;(eqv? c #\_) 
      73                  (eqv? c #\.)  (eqv? c #\!)))
92            (has-ssyntax-char? string (- i 1))))) = 74            (has-ssyntax-char? string (- i 1)))))
93     75  
94 (define (read-from-string str)   76 (define (read-from-string str)
95   (let ((port (open-input-string str)))   77   (let ((port (open-input-string str)))
96     (let ((val (read port)))   78     (let ((val (read port)))
97       (close-input-port port)   79       (close-input-port port)
98       val)))   80       val)))
99     81  
    -+ 82 ; Though graphically the right choice, can't use _ for currying
      83 ; because then _!foo becomes a function.  Maybe use <>.  For now
      84 ; leave this off and see how often it would have been useful.
      85  
      86 ; Might want to make ~ have less precedence than &, because
      87 ; ~foo&bar prob should mean (andf (complement foo) bar), not 
      88 ; (complement (andf foo bar)).
      89  
100 (define (expand-ssyntax sym) = 90 (define (expand-ssyntax sym)
    -+ 91   ((cond ((or (insym? #\: sym) (insym? #\~ sym)) expand-compose)
      92          ((or (insym? #\. sym) (insym? #\! sym)) expand-sexpr)
      93          ((insym? #\& sym) expand-and)
      94      ;   ((insym? #\_ sym) expand-curry)
      95          (#t (error "Unknown ssyntax" sym)))
      96    sym))
      97  
      98 (define (expand-compose sym)
101   (let ((elts (map (lambda (tok) = 99   (let ((elts (map (lambda (tok)
102                      (if (eqv? (car tok) #\~)   100                      (if (eqv? (car tok) #\~)
    <> 101                          (if (null? (cdr tok))
      102                              'no
103                          `(complement ,(chars->value (cdr tok)))   103                              `(complement ,(chars->value (cdr tok))))
104                          (chars->value tok))) = 104                          (chars->value tok)))
    <> 105                    (tokens (lambda (c) (eqv? c #\:))
105                    (tokens #\: (symbol->chars sym) '() '()))))   106                            (symbol->chars sym)
      107                            '() 
      108                            '() 
      109                            #f))))
106     (if (null? (cdr elts)) = 110     (if (null? (cdr elts))
107         (car elts)   111         (car elts)
108         (cons 'compose elts))))   112         (cons 'compose elts))))
109     113  
    -+ 114 (define (expand-and sym)
      115   (let ((elts (map chars->value
      116                    (tokens (lambda (c) (eqv? c #\&))
      117                            (symbol->chars sym)
      118                            '()
      119                            '()
      120                            #f))))
      121     (if (null? (cdr elts))
      122         (car elts)
      123         (cons 'andf elts))))
      124  
      125 ; How to include quoted arguments?  Can't treat all as quoted, because 
      126 ; never want to quote fn given as first.  Do we want to allow quote chars 
      127 ; within symbols?  Could be ugly.  
      128  
      129 ; If release, fix the fact that this simply uses v0... as vars.  Should
      130 ; make these vars gensyms.
      131  
      132 (define (expand-curry sym)
      133   (let ((expr (exc (map (lambda (x) 
      134                           (if (pair? x) (chars->value x) x))
      135                         (tokens (lambda (c) (eqv? c #\_)) 
      136                                 (symbol->chars sym) 
      137                                 '() 
      138                                 '() 
      139                                 #t))
      140                     0)))
      141     (list 'fn 
      142           (keep (lambda (s) 
      143                   (and (symbol? s) 
      144                        (eqv? (string-ref (symbol->string s) 0) 
      145                              #\v)))
      146                 expr)
      147           expr)))
      148  
      149 (define (keep f xs)
      150   (cond ((null? xs) '())
      151         ((f (car xs)) (cons (car xs) (keep f (cdr xs))))
      152         (#t (keep f (cdr xs)))))
      153  
      154 (define (exc elts n)
      155   (cond ((null? elts)
      156          '())
      157         ((eqv? (car elts) #\_)
      158          (cons (string->symbol (string-append "v" (number->string n)))
      159                (exc (cdr elts) (+ n 1))))
      160         (#t
      161          (cons (car elts) (exc (cdr elts) n)))))
      162  
      163 (define (expand-sexpr sym)
      164   (build-sexpr (reverse (tokens (lambda (c) (or (eqv? c #\.) (eqv? c #\!)))
      165                                 (symbol->chars sym)
      166                                 '()
      167                                 '()
      168                                 #t))
      169                sym))
      170  
      171 (define (build-sexpr toks orig)
      172   (cond ((null? toks)
      173          'get)
      174         ((null? (cdr toks))
      175          (chars->value (car toks)))
      176         (#t
      177          (list (build-sexpr (cddr toks) orig)
      178                (if (eqv? (cadr toks) #\!)
      179                    (list 'quote (chars->value (car toks)))
      180                    (if (or (eqv? (car toks) #\.) (eqv? (car toks) #\!))
      181                        (err "Bad ssyntax" orig)
      182                        (chars->value (car toks))))))))
      183  
      184 (define (insym? char sym) (member char (symbol->chars sym)))
      185  
110 (define (symbol->chars x) (string->list (symbol->string x))) = 186 (define (symbol->chars x) (string->list (symbol->string x)))
111     187  
112 (define (chars->value chars) (read-from-string (list->string chars)))   188 (define (chars->value chars) (read-from-string (list->string chars)))
113     189  
114 ; result will contain || if separator at end of symbol; could use <>    
115 ; that to mean something      
116        
117 (define (tokens separator source token acc)   190 (define (tokens test source token acc keepsep?)
118   (cond ((null? source) = 191   (cond ((null? source)
    <> 192          (reverse (if (pair? token) 
119          (reverse (cons (reverse token) acc)))   193                       (cons (reverse token) acc)
      194                       acc)))
120         ((eqv? (car source) separator)   195         ((test (car source))
121          (tokens separator   196          (tokens test
122                  (cdr source) = 197                  (cdr source)
123                  '()   198                  '()
    <> 199                  (let ((rec (if (null? token)
      200                             acc
124                  (cons (reverse token) acc)))   201                             (cons (reverse token) acc))))
      202                    (if keepsep?
      203                        (cons (car source) rec)
      204                        rec))
      205                  keepsep?))
125         (#t = 206         (#t
126          (tokens separator <> 207          (tokens test
127                  (cdr source) = 208                  (cdr source)
128                  (cons (car source) token)   209                  (cons (car source) token)
129                  acc)))) <> 210                  acc
130     211                  keepsep?))))
131 ; Purely an optimization.  Could in principle do it with a preprocessor      
132 ; instead of adding a line to ac, but only want to do it for evaluated      
133 ; subtrees, and much easier to figure those out in ac.      
134        
135 (define (decompose fns args)      
136   (cond ((null? fns) `((fn vals (car vals)) ,@args))      
137         ((null? (cdr fns)) (cons (car fns) args))      
138         (#t (list (car fns) (decompose (cdr fns) args)))))      
139        
140   = 212  
141 (define (ac-global-name s)   213 (define (ac-global-name s)
142   (string->symbol (string-append "_" (symbol->string s))))   214   (string->symbol (string-append "_" (symbol->string s))))
143     215  
144 (define (ac-var-ref s env)   216 (define (ac-var-ref s env)
145   (if (lex? s env)   217   (if (lex? s env)
146       s   218       s
147       (ac-global-name s)))   219       (ac-global-name s)))
148     220  
149 ; quasiquote   221 ; quasiquote
150     222  
151 (define (ac-qq args env)   223 (define (ac-qq args env)
152   (list 'quasiquote (ac-qq1 1 args env)))   224   (list 'quasiquote (ac-qq1 1 args env)))
153     225  
154 ; process the argument of a quasiquote. keep track of   226 ; process the argument of a quasiquote. keep track of
155 ; depth of nesting. handle unquote only at top level (level = 1).   227 ; depth of nesting. handle unquote only at top level (level = 1).
156 ; complete form, e.g. x or (fn x) or (unquote (fn x))   228 ; complete form, e.g. x or (fn x) or (unquote (fn x))
    -+ 229  
157 (define (ac-qq1 level x env) = 230 (define (ac-qq1 level x env)
158   (cond ((= level 0)   231   (cond ((= level 0)
159          (ac x env))   232          (ac x env))
160         ((and (pair? x) (eqv? (car x) 'unquote))   233         ((and (pair? x) (eqv? (car x) 'unquote))
161          (list 'unquote (ac-qq1 (- level 1) (cadr x) env)))   234          (list 'unquote (ac-qq1 (- level 1) (cadr x) env)))
162         ((and (pair? x) (eqv? (car x) 'unquote-splicing) (= level 1))   235         ((and (pair? x) (eqv? (car x) 'unquote-splicing) (= level 1))
163          (list 'unquote-splicing   236          (list 'unquote-splicing
164                (list 'ar-nil-terminate (ac-qq1 (- level 1) (cadr x) env))))   237                (list 'ar-nil-terminate (ac-qq1 (- level 1) (cadr x) env))))
165         ((and (pair? x) (eqv? (car x) 'quasiquote))   238         ((and (pair? x) (eqv? (car x) 'quasiquote))
166          (list 'quasiquote (ac-qq1 (+ level 1) (cadr x) env)))   239          (list 'quasiquote (ac-qq1 (+ level 1) (cadr x) env)))
167         ((pair? x)   240         ((pair? x)
168          (map (lambda (x) (ac-qq1 level x env)) x)) <> 241          (imap (lambda (x) (ac-qq1 level x env)) x))
169         (#t x))) = 242         (#t x)))
    -+ 243  
      244 ; like map, but don't demand '()-terminated list
      245  
      246 (define (imap f l)
      247   (cond ((pair? l)
      248          (cons (f (car l)) (imap f (cdr l))))
      249         ((null? l)
      250          '())
      251         (#t (f l))))
170   = 252  
171 ; (if) -> nil   253 ; (if) -> nil
172 ; (if x) -> x   254 ; (if x) -> x
173 ; (if t a ...) -> a   255 ; (if t a ...) -> a
174 ; (if nil a b) -> b   256 ; (if nil a b) -> b
175 ; (if nil a b c) -> (if b c)   257 ; (if nil a b c) -> (if b c)
176     258  
177 (define (ac-if args env)   259 (define (ac-if args env)
178   (cond ((null? args) ''nil)   260   (cond ((null? args) ''nil)
179         ((null? (cdr args)) (ac (car args) env))   261         ((null? (cdr args)) (ac (car args) env))
180         (#t `(if (not (ar-false? ,(ac (car args) env)))   262         (#t `(if (not (ar-false? ,(ac (car args) env)))
181 ;(not (eq? 'nil ,(ac (car args) env))) +-    
182                  ,(ac (cadr args) env) = 263                  ,(ac (cadr args) env)
183                  ,(ac-if (cddr args) env)))))   264                  ,(ac-if (cddr args) env)))))
184     265  
    -+ 266 (define (ac-dbname! name env)
      267   (if (symbol? name)
      268       (cons (list name) env)
      269       env))
      270  
      271 (define (ac-dbname env)
      272   (cond ((null? env) #f)
      273         ((pair? (car env)) (caar env))
      274         (#t (ac-dbname (cdr env)))))
      275  
185 ; translate fn directly into a lambda if it has ordinary = 276 ; translate fn directly into a lambda if it has ordinary
186 ; parameters, otherwise use a rest parameter and parse it.   277 ; parameters, otherwise use a rest parameter and parse it.
    -+ 278  
187 (define (ac-fn args body env) = 279 (define (ac-fn args body env)
188   (if (ac-complex-args? args)   280   (if (ac-complex-args? args)
189       (ac-complex-fn args body env)   281       (ac-complex-fn args body env)
    <> 282       (ac-nameit
      283        (ac-dbname env)
190       `(lambda ,(let ((a (ac-denil args))) (if (eqv? a 'nil) '() a))   284        `(lambda ,(let ((a (ac-denil args))) (if (eqv? a 'nil) '() a))
191          'nil      
192          ,@(ac-body body (append (ac-arglist args) env)))))   285           ,@(ac-body* body (append (ac-arglist args) env))))))
193   = 286  
194 ; does an fn arg list use optional parameters or destructuring?   287 ; does an fn arg list use optional parameters or destructuring?
195 ; a rest parameter is not complex   288 ; a rest parameter is not complex
    -+ 289  
196 (define (ac-complex-args? args) = 290 (define (ac-complex-args? args)
197   (cond ((eqv? args '()) #f)   291   (cond ((eqv? args '()) #f)
198         ((symbol? args) #f)   292         ((symbol? args) #f)
199         ((and (pair? args) (symbol? (car args)))   293         ((and (pair? args) (symbol? (car args)))
200          (ac-complex-args? (cdr args)))   294          (ac-complex-args? (cdr args)))
201         (#t #t)))   295         (#t #t)))
202     296  
203 ; translate a fn with optional or destructuring args   297 ; translate a fn with optional or destructuring args
204 ; (fn (x (o y x) (o z 21) (x1 x2) . rest) ...)   298 ; (fn (x (o y x) (o z 21) (x1 x2) . rest) ...)
205 ; arguments in top-level list are mandatory (unless optional),   299 ; arguments in top-level list are mandatory (unless optional),
206 ; but it's OK for parts of a list you're destructuring to   300 ; but it's OK for parts of a list you're destructuring to
207 ; be missing.   301 ; be missing.
    -+ 302  
208 (define (ac-complex-fn args body env) = 303 (define (ac-complex-fn args body env)
209   (let* ((ra (ar-gensym))   304   (let* ((ra (ar-gensym))
210          (z (ac-complex-args args env ra #t)))   305          (z (ac-complex-args args env ra #t)))
211     `(lambda ,ra   306     `(lambda ,ra
212        (let* ,z   307        (let* ,z
213          'nil <>    
214          ,@(ac-body body (append (ac-complex-getargs z) env))))))   308          ,@(ac-body* body (append (ac-complex-getargs z) env))))))
215   = 309  
216 ; returns a list of two-element lists, first is variable name,   310 ; returns a list of two-element lists, first is variable name,
217 ; second is (compiled) expression. to be used in a let.   311 ; second is (compiled) expression. to be used in a let.
218 ; caller should extract variables and add to env.   312 ; caller should extract variables and add to env.
219 ; ra is the rest argument to the fn.   313 ; ra is the rest argument to the fn.
220 ; is-params indicates that args are function arguments   314 ; is-params indicates that args are function arguments
221 ;   (not destructuring), so they must be passed or be optional.   315 ;   (not destructuring), so they must be passed or be optional.
    -+ 316  
222 (define (ac-complex-args args env ra is-params) = 317 (define (ac-complex-args args env ra is-params)
223   (cond ((or (eqv? args '()) (eqv? args 'nil)) '())   318   (cond ((or (eqv? args '()) (eqv? args 'nil)) '())
224         ((symbol? args) (list (list args ra)))   319         ((symbol? args) (list (list args ra)))
225         ((pair? args)   320         ((pair? args)
226          (let* ((x (if (and (pair? (car args)) (eqv? (caar args) 'o))   321          (let* ((x (if (and (pair? (car args)) (eqv? (caar args) 'o))
227                        (ac-complex-opt (cadar args)    322                        (ac-complex-opt (cadar args) 
228                                        (if (pair? (cddar args))   323                                        (if (pair? (cddar args))
229                                            (caddar args)    324                                            (caddar args) 
230                                            'nil)   325                                            'nil)
231                                        env    326                                        env 
232                                        ra)   327                                        ra)
233                        (ac-complex-args   328                        (ac-complex-args
234                         (car args)   329                         (car args)
235                         env   330                         env
236                         (if is-params   331                         (if is-params
237                             `(car ,ra)   332                             `(car ,ra)
238                             `(ar-xcar ,ra))   333                             `(ar-xcar ,ra))
239                         #f)))   334                         #f)))
240                 (xa (ac-complex-getargs x)))   335                 (xa (ac-complex-getargs x)))
241            (append x (ac-complex-args (cdr args)   336            (append x (ac-complex-args (cdr args)
242                                       (append xa env)   337                                       (append xa env)
243                                       `(ar-xcdr ,ra)   338                                       `(ar-xcdr ,ra)
244                                       is-params))))   339                                       is-params))))
245         (#t (err "Can't understand fn arg list" args))))   340         (#t (err "Can't understand fn arg list" args))))
246     341  
247 ; (car ra) is the argument   342 ; (car ra) is the argument
248 ; so it's not present if ra is nil or '()   343 ; so it's not present if ra is nil or '()
    -+ 344  
249 (define (ac-complex-opt var expr env ra) = 345 (define (ac-complex-opt var expr env ra)
250   (list (list var `(if (pair? ,ra) (car ,ra) ,(ac expr env)))))   346   (list (list var `(if (pair? ,ra) (car ,ra) ,(ac expr env)))))
251     347  
252 ; extract list of variables from list of two-element lists.   348 ; extract list of variables from list of two-element lists.
    -+ 349  
253 (define (ac-complex-getargs a) = 350 (define (ac-complex-getargs a)
254   (map (lambda (x) (car x)) a))   351   (map (lambda (x) (car x)) a))
255     352  
256 ; (a b . c) -> (a b c)   353 ; (a b . c) -> (a b c)
257 ; a -> (a)   354 ; a -> (a)
    -+ 355  
258 (define (ac-arglist a) = 356 (define (ac-arglist a)
259   (cond ((null? a) '())   357   (cond ((null? a) '())
260         ((symbol? a) (list a))   358         ((symbol? a) (list a))
261         ((symbol? (cdr a)) (list (car a) (cdr a)))   359         ((symbol? (cdr a)) (list (car a) (cdr a)))
262         (#t (cons (car a) (ac-arglist (cdr a))))))   360         (#t (cons (car a) (ac-arglist (cdr a))))))
263     361  
264 (define (ac-body body env)   362 (define (ac-body body env)
    -+ 363   (map (lambda (x) (ac x env)) body))
      364  
      365 ; like ac-body, but spits out a nil expression if empty
      366  
      367 (define (ac-body* body env)
265   (if (null? body) = 368   (if (null? body)
266       '() <> 369       (list (list 'quote 'nil))
267       (cons (ac (car body) env) (ac-body (cdr body) env))))   370       (ac-body body env)))
268   = 371  
269 ; (set v1 expr1 v2 expr2 ...)   372 ; (set v1 expr1 v2 expr2 ...)
270     373  
271 (define (ac-set x env)   374 (define (ac-set x env)
272   `(begin ,@(ac-setn x env)))   375   `(begin ,@(ac-setn x env)))
273     376  
274 (define (ac-setn x env)   377 (define (ac-setn x env)
275   (if (null? x)   378   (if (null? x)
276       '()   379       '()
277       (cons (ac-set1 (ac-macex (car x)) (ac (cadr x) env) env) <> 380       (cons (ac-set1 (ac-macex (car x)) (cadr x) env)
278             (ac-setn (cddr x) env)))) = 381             (ac-setn (cddr x) env))))
279     382  
    -+ 383 ; trick to tell Scheme the name of something, so Scheme
      384 ; debugging and profiling make more sense.
      385  
      386 (define (ac-nameit name v)
      387   (if (symbol? name)
      388       (let ((n (string->symbol (string-append " " (symbol->string name)))))
      389         (list 'let `((,n ,v)) n))
      390       v))
      391  
280 ; = replaced by set, which is only for vars = 392 ; = replaced by set, which is only for vars
281 ; = now defined in arc (is it?)   393 ; = now defined in arc (is it?)
282 ; name is to cause fns to have their arc names for debugging   394 ; name is to cause fns to have their arc names for debugging
283     395  
284 (define (ac-set1 a b env) <> 396 (define (ac-set1 a b1 env)
285   (if (symbol? a) = 397   (if (symbol? a)
286       (let ((name (string->symbol (string-append " " (symbol->string a))))) <> 398       (let ((b (ac b1 (ac-dbname! a env))))
287         (list 'let `((,name ,b))   399         (list 'let `((zz ,b))
288                (cond ((eqv? a 'nil) (err "Can't rebind nil")) = 400                (cond ((eqv? a 'nil) (err "Can't rebind nil"))
289                      ((eqv? a 't) (err "Can't rebind t"))   401                      ((eqv? a 't) (err "Can't rebind t"))
290                      ((lex? a env) `(set! ,a ,name)) <> 402                      ((lex? a env) `(set! ,a zz))
291                      (#t `(namespace-set-variable-value! ',(ac-global-name a)  = 403                      (#t `(namespace-set-variable-value! ',(ac-global-name a) 
292                                                          ,name))) <> 404                                                          zz)))
293                name))   405                'zz))
294       (err "First arg to set must be a symbol" a))) = 406       (err "First arg to set must be a symbol" a)))
    -+ 407  
      408 ; given a list of Arc expressions, return a list of Scheme expressions.
      409 ; for compiling passed arguments.
      410  
      411 (define (ac-args names exprs env)
      412   (if (null? exprs)
      413       '()
      414       (cons (ac (car exprs)
      415                 (ac-dbname! (if (pair? names) (car names) #f) env))
      416             (ac-args (if (pair? names) (cdr names) '())
      417                      (cdr exprs)
      418                      env))))
      419  
      420 ; generate special fast code for ordinary two-operand
      421 ; calls to the following functions. this is to avoid
      422 ; calling e.g. ar-is with its &rest and apply.
      423  
      424 (define ac-binaries
      425   '((is ar-is2)
      426     (< ar-<2)
      427     (> ar->2)
      428     (+ ar-+2)))
      429  
      430 ; (foo bar) where foo is a global variable bound to a procedure.
      431  
      432 (define (ac-global-call fn args env)
      433   (cond ((and (assoc fn ac-binaries) (= (length args) 2))
      434          `(,(cadr (assoc fn ac-binaries)) ,@(ac-args '() args env)))
      435         (#t 
      436          `(,(ac-global-name fn) ,@(ac-args '() args env)))))
295        = 437       
296 ; compile a function call   438 ; compile a function call
297 ; special cases for speed, to avoid compiled output like   439 ; special cases for speed, to avoid compiled output like
298 ;   (ar-apply _pr (list 1 2))   440 ;   (ar-apply _pr (list 1 2))
299 ; which results in 1/2 the CPU time going to GC. Instead:   441 ; which results in 1/2 the CPU time going to GC. Instead:
300 ;   (ar-funcall2 _pr 1 2)   442 ;   (ar-funcall2 _pr 1 2)
    -+ 443 ; and for (foo bar), if foo is a reference to a global variable,
      444 ;   and it's bound to a function, generate (foo bar) instead of
      445 ;   (ar-funcall1 foo bar)
      446  
      447 (define direct-calls #f)
      448  
301 (define (ac-call fn args env) = 449 (define (ac-call fn args env)
302   (let ((macfn (ac-macro? fn)))   450   (let ((macfn (ac-macro? fn)))
303     (cond (macfn   451     (cond (macfn
304            (ac-mac-call macfn args env))   452            (ac-mac-call macfn args env))
305           ((and (pair? fn) (eqv? (car fn) 'fn))   453           ((and (pair? fn) (eqv? (car fn) 'fn))
306            `(,(ac fn env) ,@(map (lambda (x) (ac x env)) args))) <> 454            `(,(ac fn env) ,@(ac-args (cadr fn) args env)))
      455           ((and direct-calls (symbol? fn) (not (lex? fn env)) (bound? fn)
      456                 (procedure? (namespace-variable-value (ac-global-name fn))))
      457            (ac-global-call fn args env))
307           ((= (length args) 0) = 458           ((= (length args) 0)
308            `(ar-funcall0 ,(ac fn env) ,@(map (lambda (x) (ac x env)) args)))   459            `(ar-funcall0 ,(ac fn env) ,@(map (lambda (x) (ac x env)) args)))
309           ((= (length args) 1)   460           ((= (length args) 1)
310            `(ar-funcall1 ,(ac fn env) ,@(map (lambda (x) (ac x env)) args)))   461            `(ar-funcall1 ,(ac fn env) ,@(map (lambda (x) (ac x env)) args)))
311           ((= (length args) 2)   462           ((= (length args) 2)
312            `(ar-funcall2 ,(ac fn env) ,@(map (lambda (x) (ac x env)) args)))   463            `(ar-funcall2 ,(ac fn env) ,@(map (lambda (x) (ac x env)) args)))
313           ((= (length args) 3)   464           ((= (length args) 3)
314            `(ar-funcall3 ,(ac fn env) ,@(map (lambda (x) (ac x env)) args)))   465            `(ar-funcall3 ,(ac fn env) ,@(map (lambda (x) (ac x env)) args)))
315           ((= (length args) 4)   466           ((= (length args) 4)
316            `(ar-funcall4 ,(ac fn env) ,@(map (lambda (x) (ac x env)) args)))   467            `(ar-funcall4 ,(ac fn env) ,@(map (lambda (x) (ac x env)) args)))
317           (#t   468           (#t
318            `(ar-apply ,(ac fn env)   469            `(ar-apply ,(ac fn env)
319                       (list ,@(map (lambda (x) (ac x env)) args)))))))   470                       (list ,@(map (lambda (x) (ac x env)) args)))))))
320     471  
321 (define (ac-mac-call m args env)   472 (define (ac-mac-call m args env)
322   (let ((x1 (apply m (map ac-niltree args))))   473   (let ((x1 (apply m (map ac-niltree args))))
323     (let ((x2 (ac (ac-denil x1) env)))   474     (let ((x2 (ac (ac-denil x1) env)))
324       x2)))   475       x2)))
325     476  
326 ; returns #f or the macro function   477 ; returns #f or the macro function
327     478  
328 (define (ac-macro? fn)   479 (define (ac-macro? fn)
329   (if (symbol? fn)   480   (if (symbol? fn)
330       (let ((v (namespace-variable-value (ac-global-name fn)    481       (let ((v (namespace-variable-value (ac-global-name fn) 
331                                          #t    482                                          #t 
332                                          (lambda () #f))))   483                                          (lambda () #f))))
333         (if (and v   484         (if (and v
334                  (ar-tagged? v)   485                  (ar-tagged? v)
335                  (eq? (ar-type v) 'mac))   486                  (eq? (ar-type v) 'mac))
336             (ar-rep v)   487             (ar-rep v)
337             #f))   488             #f))
338       #f))   489       #f))
339     490  
340 ; macroexpand the outer call of a form as much as possible   491 ; macroexpand the outer call of a form as much as possible
341     492  
342 (define (ac-macex e . once)   493 (define (ac-macex e . once)
343   (if (pair? e)   494   (if (pair? e)
344       (let ((m (ac-macro? (car e))))   495       (let ((m (ac-macro? (car e))))
345         (if m   496         (if m
346             (let ((expansion (ac-denil (apply m (map ac-niltree (cdr e))))))   497             (let ((expansion (ac-denil (apply m (map ac-niltree (cdr e))))))
347               (if (null? once) (ac-macex expansion) expansion))   498               (if (null? once) (ac-macex expansion) expansion))
348             e))   499             e))
349       e))   500       e))
350     501  
351 ; macros return Arc lists, ending with NIL.   502 ; macros return Arc lists, ending with NIL.
352 ; but the Arc compiler expects Scheme lists, ending with '().   503 ; but the Arc compiler expects Scheme lists, ending with '().
353 ; what to do with (is x nil . nil) ?   504 ; what to do with (is x nil . nil) ?
354 ;   the first nil ought to be replaced with 'NIL   505 ;   the first nil ought to be replaced with 'NIL
355 ;   the second with '()   506 ;   the second with '()
356 ; so the rule is: NIL in the car -> 'NIL, NIL in the cdr -> '().   507 ; so the rule is: NIL in the car -> 'NIL, NIL in the cdr -> '().
357 ;   NIL by itself -> NIL   508 ;   NIL by itself -> NIL
358     509  
359 (define (ac-denil x)   510 (define (ac-denil x)
360   (cond ((pair? x) (cons (ac-denil-car (car x)) (ac-denil-cdr (cdr x))))   511   (cond ((pair? x) (cons (ac-denil-car (car x)) (ac-denil-cdr (cdr x))))
361         (#t x)))   512         (#t x)))
362     513  
363 (define (ac-denil-car x)   514 (define (ac-denil-car x)
364   (if (eq? x 'nil)   515   (if (eq? x 'nil)
365       'nil   516       'nil
366       (ac-denil x)))   517       (ac-denil x)))
367     518  
368 (define (ac-denil-cdr x)   519 (define (ac-denil-cdr x)
369   (if (eq? x 'nil)   520   (if (eq? x 'nil)
370       '()   521       '()
371       (ac-denil x)))   522       (ac-denil x)))
372     523  
373 ; is v lexically bound?   524 ; is v lexically bound?
    -+ 525  
374 (define (lex? v env) = 526 (define (lex? v env)
375   (memq v env))   527   (memq v env))
376     528  
377 (define (xcar x)   529 (define (xcar x)
378   (and (pair? x) (car x)))   530   (and (pair? x) (car x)))
379     531  
380 ; #f and '() -> nil for a whole quoted list/tree.   532 ; #f and '() -> nil for a whole quoted list/tree.
381     533  
    -+ 534 ; Arc primitives written in Scheme should look like:
      535  
      536 ; (xdef foo (lambda (lst)
      537 ;           (ac-niltree (scheme-foo (ar-nil-terminate lst)))))
      538  
      539 ; That is, Arc lists are NIL-terminated. When calling a Scheme
      540 ; function that treats an argument as a list, call ar-nil-terminate
      541 ; to change NIL to '(). When returning any data created by Scheme
      542 ; to Arc, call ac-niltree to turn all '() into NIL.
      543 ; (hash-table-get doesn't use its argument as a list, so it doesn't
      544 ; need ar-nil-terminate).
      545  
382 (define (ac-niltree x) = 546 (define (ac-niltree x)
383   (cond ((pair? x) (cons (ac-niltree (car x)) (ac-niltree (cdr x))))   547   (cond ((pair? x) (cons (ac-niltree (car x)) (ac-niltree (cdr x))))
384         ((or (eq? x #f) (eq? x '())) 'nil)   548         ((or (eq? x #f) (eq? x '())) 'nil)
385         (#t x)))   549         (#t x)))
386     550  
    <> 551 ; The next two are optimizations, except work for macros.
      552  
387 ;(define (err msg . args)   553 (define (decompose fns args)
      554   (cond ((null? fns) `((fn vals (car vals)) ,@args))
      555         ((null? (cdr fns)) (cons (car fns) args))
      556         (#t (list (car fns) (decompose (cdr fns) args)))))
388 ;  (display msg)   557  
      558 (define (ac-andf s env)
389 ;  (map (lambda (a) (display " ") (write a))  args)   559   (ac (let ((gs (map (lambda (x) (ar-gensym)) (cdr s))))
      560                `((fn ,gs
      561                    (and ,@(map (lambda (f) `(,f ,@gs))
      562                                (cdar s))))
      563                  ,@(cdr s)))
390 ;  (newline)   564       env))
391 ;  (xxundefined))   565  
392     566 (define err error)
393 (define err error)  ; eli says need to remove xxundefined for speed      
394   = 567  
395 ; run-time primitive procedures   568 ; run-time primitive procedures
396     569  
397 (define (xdef a b) <> 570 ;(define (xdef a b)
398   (namespace-set-variable-value! (ac-global-name a) b)   571 ;  (namespace-set-variable-value! (ac-global-name a) b)
399   b)   572 ;  b)
400   = 573  
    -+ 574 (define-syntax xdef
      575   (syntax-rules ()
      576     ((xxdef a b)
      577      (let ((nm (ac-global-name 'a))
      578            (a b))
      579        (namespace-set-variable-value! nm a)
      580        a))))
      581  
401 (define fn-signatures (make-hash-table 'equal)) = 582 (define fn-signatures (make-hash-table 'equal))
402     583  
403 ; This is a replacement for xdef that stores opeator signatures.   584 ; This is a replacement for xdef that stores opeator signatures.
404 ; Haven't started using it yet.   585 ; Haven't started using it yet.
405     586  
406 (define (odef a parms b)   587 (define (odef a parms b)
407   (namespace-set-variable-value! (ac-global-name a) b)   588   (namespace-set-variable-value! (ac-global-name a) b)
408   (hash-table-put! fn-signatures a (list parms))   589   (hash-table-put! fn-signatures a (list parms))
409   b)   590   b)
410     591  
411 (xdef 'sig fn-signatures) <> 592 (xdef sig fn-signatures)
412   = 593  
413 ; versions of car and cdr for parsing arguments for optional   594 ; versions of car and cdr for parsing arguments for optional
414 ; parameters, that yield nil for nil. maybe we should use   595 ; parameters, that yield nil for nil. maybe we should use
415 ; full Arc car and cdr, so we can destructure more things   596 ; full Arc car and cdr, so we can destructure more things
416     597  
417 (define (ar-xcar x)   598 (define (ar-xcar x)
418   (if (or (eqv? x 'nil) (eqv? x '()))   599   (if (or (eqv? x 'nil) (eqv? x '()))
419       'nil   600       'nil
420       (car x)))   601       (car x)))
421          602       
422 (define (ar-xcdr x)   603 (define (ar-xcdr x)
423   (if (or (eqv? x 'nil) (eqv? x '()))   604   (if (or (eqv? x 'nil) (eqv? x '()))
424       'nil   605       'nil
425       (cdr x)))   606       (cdr x)))
426     607  
427 ; convert #f from a Scheme predicate to NIL.   608 ; convert #f from a Scheme predicate to NIL.
428     609  
429 (define (ar-nill x)   610 (define (ar-nill x)
430   (if (or (eq? x '()) (eq? x #f))   611   (if (or (eq? x '()) (eq? x #f))
431       'nil   612       'nil
432       x))   613       x))
433     614  
434 ; definition of falseness for Arc if.   615 ; definition of falseness for Arc if.
435 ; must include '() since sometimes Arc functions see   616 ; must include '() since sometimes Arc functions see
436 ; Scheme lists (e.g. . body of a macro).   617 ; Scheme lists (e.g. . body of a macro).
437     618  
438 (define (ar-false? x)   619 (define (ar-false? x)
439   (or (eq? x 'nil) (eq? x '()) (eq? x #f)))   620   (or (eq? x 'nil) (eq? x '()) (eq? x #f)))
440     621  
441 #| +-    
442    (if (eq? x 'nil) #t      
443       (if (eq? x '()) #t      
444           (not x)))      
445 |#      
446        
447 ; call a function or perform an array ref, hash ref, &c = 622 ; call a function or perform an array ref, hash ref, &c
448     623  
449 ; Non-fn donstants in functional position are valuable real estate, so <> 624 ; Non-fn constants in functional position are valuable real estate, so
450 ; should figure out the best way to exploit it.   625 ; should figure out the best way to exploit it.  What could (1 foo) or 
      626 ; ('a foo) mean?  Maybe it should mean currying.
451   = 627  
    -+ 628 ; For now the way to make the default val of a hash table be other than
      629 ; nil is to supply the val when doing the lookup.  Later may also let
      630 ; defaults be supplied as an arg to table.  To implement this, need: an 
      631 ; eq table within scheme mapping tables to defaults, and to adapt the 
      632 ; code in arc.arc that reads and writes tables to read and write their 
      633 ; default vals with them.  To make compatible with existing written tables, 
      634 ; just use an atom or 3-elt list to keep the default.
      635  
452 (define (ar-apply fn args) = 636 (define (ar-apply fn args)
453   (cond ((procedure? fn) (apply fn args)) <> 637   (cond ((procedure? fn)
      638          (apply fn args))
      639         ((pair? fn) 
454         ((pair? fn) (list-ref fn (car args)))   640          (list-ref fn (car args)))
      641         ((string? fn) 
455         ((string? fn) (string-ref fn (car args)))   642          (string-ref fn (car args)))
      643         ((hash-table? fn) 
456         ((hash-table? fn) (ar-nill (hash-table-get fn (car args) #f)))   644          (ar-nill (hash-table-get fn
      645                                   (car args) 
      646                                   (if (pair? (cdr args)) (cadr args) #f))))
457 ; experiment: means e.g. [1] is a constant fn = 647 ; experiment: means e.g. [1] is a constant fn
458 ;       ((or (number? fn) (symbol? fn)) fn)   648 ;       ((or (number? fn) (symbol? fn)) fn)
459 ; another possibility: constant in functional pos means it gets    649 ; another possibility: constant in functional pos means it gets 
460 ; passed to the first arg, i.e. ('kids item) means (item 'kids).   650 ; passed to the first arg, i.e. ('kids item) means (item 'kids).
461         (#t (err "Function call on inappropriate object" fn args))))   651         (#t (err "Function call on inappropriate object" fn args))))
462     652  
463 (xdef 'apply (lambda (fn . args) <> 653 (xdef apply (lambda (fn . args)
464                (ar-apply fn (ar-apply-args args)))) = 654                (ar-apply fn (ar-apply-args args))))
465     655  
466 ; special cases of ar-apply for speed and to avoid consing arg lists   656 ; special cases of ar-apply for speed and to avoid consing arg lists
    -+ 657  
467 (define (ar-funcall0 fn) = 658 (define (ar-funcall0 fn)
468   (if (procedure? fn)   659   (if (procedure? fn)
469       (fn)   660       (fn)
470       (ar-apply fn (list))))   661       (ar-apply fn (list))))
471     662  
472 (define (ar-funcall1 fn arg1)   663 (define (ar-funcall1 fn arg1)
473   (if (procedure? fn)   664   (if (procedure? fn)
474       (fn arg1)   665       (fn arg1)
475       (ar-apply fn (list arg1))))   666       (ar-apply fn (list arg1))))
476     667  
477 (define (ar-funcall2 fn arg1 arg2)   668 (define (ar-funcall2 fn arg1 arg2)
478   (if (procedure? fn)   669   (if (procedure? fn)
479       (fn arg1 arg2)   670       (fn arg1 arg2)
480       (ar-apply fn (list arg1 arg2))))   671       (ar-apply fn (list arg1 arg2))))
481     672  
482 (define (ar-funcall3 fn arg1 arg2 arg3)   673 (define (ar-funcall3 fn arg1 arg2 arg3)
483   (if (procedure? fn)   674   (if (procedure? fn)
484       (fn arg1 arg2 arg3)   675       (fn arg1 arg2 arg3)
485       (ar-apply fn (list arg1 arg2 arg3))))   676       (ar-apply fn (list arg1 arg2 arg3))))
486     677  
487 (define (ar-funcall4 fn arg1 arg2 arg3 arg4)   678 (define (ar-funcall4 fn arg1 arg2 arg3 arg4)
488   (if (procedure? fn)   679   (if (procedure? fn)
489       (fn arg1 arg2 arg3 arg4)   680       (fn arg1 arg2 arg3 arg4)
490       (ar-apply fn (list arg1 arg2 arg3 arg4))))   681       (ar-apply fn (list arg1 arg2 arg3 arg4))))
491     682  
492 ; replace the nil at the end of a list with a '()   683 ; replace the nil at the end of a list with a '()
493     684  
494 (define (ar-nil-terminate l)   685 (define (ar-nil-terminate l)
495   (if (or (eqv? l '()) (eqv? l 'nil))   686   (if (or (eqv? l '()) (eqv? l 'nil))
496       '()   687       '()
497       (cons (car l) (ar-nil-terminate (cdr l)))))   688       (cons (car l) (ar-nil-terminate (cdr l)))))
498     689  
499 ; turn the arguments to Arc apply into a list.   690 ; turn the arguments to Arc apply into a list.
500 ; if you call (apply fn 1 2 '(3 4))   691 ; if you call (apply fn 1 2 '(3 4))
501 ; then args is '(1 2 (3 4 . nil) . ())   692 ; then args is '(1 2 (3 4 . nil) . ())
502 ; that is, the main list is a scheme list.   693 ; that is, the main list is a scheme list.
503 ; and we should return '(1 2 3 4 . ())   694 ; and we should return '(1 2 3 4 . ())
504 ; was once (apply apply list (ac-denil args))   695 ; was once (apply apply list (ac-denil args))
505 ; but that didn't work for (apply fn nil)   696 ; but that didn't work for (apply fn nil)
506     697  
507 (define (ar-apply-args args)   698 (define (ar-apply-args args)
508   (cond ((null? args) '())   699   (cond ((null? args) '())
509         ((null? (cdr args)) (ar-nil-terminate (car args)))   700         ((null? (cdr args)) (ar-nil-terminate (car args)))
510         (#t (cons (car args) (ar-apply-args (cdr args))))))   701         (#t (cons (car args) (ar-apply-args (cdr args))))))
511     702  
    <> 703  
      704  
      705  
      706  
512 (xdef 'cons cons)   707 (xdef cons cons)
513   = 708  
514 (xdef 'car (lambda (x) <> 709 (xdef car (lambda (x)
515              (cond ((pair? x)     (car x)) = 710              (cond ((pair? x)     (car x))
516                    ((eqv? x 'nil) 'nil)   711                    ((eqv? x 'nil) 'nil)
517                    ((eqv? x '())  'nil)   712                    ((eqv? x '())  'nil)
518                    (#t            (err "Can't take car of" x)))))   713                    (#t            (err "Can't take car of" x)))))
519     714  
520 (xdef 'cdr (lambda (x) <> 715 (xdef cdr (lambda (x)
521              (cond ((pair? x)     (cdr x)) = 716              (cond ((pair? x)     (cdr x))
522                    ((eqv? x 'nil) 'nil)   717                    ((eqv? x 'nil) 'nil)
523                    ((eqv? x '())  'nil)   718                    ((eqv? x '())  'nil)
524                    (#t            (err "Can't take cdr of" x)))))   719                    (#t            (err "Can't take cdr of" x)))))
525     720  
    -+ 721 (define (tnil x) (if x 't 'nil))
      722  
      723 ; (pairwise pred '(a b c d)) =>
      724 ;   (and (pred a b) (pred b c) (pred c d))
      725 ; pred returns t/nil, as does pairwise
526 ; reduce?  = 726 ; reduce? 
527     727  
528 (define (pairwise pred args base) <> 728 (define (pairwise pred lst)
529   (let ((n (length args)))      
530     (cond ((< n 2) base)   729   (cond ((null? lst) 't)
531           ((= n 2) (apply pred args))   730         ((null? (cdr lst)) 't)
532           (#t (and (pred (car args) (cadr args))   731         ((not (eqv? (pred (car lst) (cadr lst)) 'nil))
533                    (pairwise pred (cdr args) base))))))   732          (pairwise pred (cdr lst)))
      733         (#t 'nil)))
534   = 734  
535 ; not quite right, because behavior of underlying eqv unspecified   735 ; not quite right, because behavior of underlying eqv unspecified
536 ; in many cases according to r5rs   736 ; in many cases according to r5rs
537 ; do we really want is to ret t for distinct strings?   737 ; do we really want is to ret t for distinct strings?
538     738  
539 (xdef 'is (lambda args <> 739 ; for (is x y)
540             (if (or (all (lambda (a) (eqv? (car args) a)) (cdr args))   740  
      741 (define (ar-is2 a b)
      742   (tnil (or (eqv? a b)
541                     (and (all string? args)   743             (and (string? a) (string? b) (string=? a b))
542                          (apply string=? args))      
543                     (all ar-false? args))   744             (and (ar-false? a) (ar-false? b)))))
544                 't 'nil)))   745  
      746 ; for all other uses of is
545   = 747  
    <> 748 (xdef is (lambda args (pairwise ar-is2 args)))
      749  
546 (xdef 'err err)   750 (xdef err err)
547 (xdef 'nil 'nil)   751 (xdef nil 'nil)
548 (xdef 't   't)   752 (xdef t   't)
549   = 753  
550 (define (all test seq)   754 (define (all test seq)
551   (or (null? seq)    755   (or (null? seq) 
552       (and (test (car seq)) (all test (cdr seq)))))   756       (and (test (car seq)) (all test (cdr seq)))))
553     757  
554 ; rather strictly excludes () <>    
555        
556 (define (arc-list? x) (or (pair? x) (eqv? x 'nil)))   758 (define (arc-list? x) (or (pair? x) (eqv? x 'nil) (eqv? x '())))
557        = 759       
558 ; generic +: strings, lists, numbers. <> 760 ; Generic +: strings, lists, numbers.
559 ; problem with generic +: what to return when no args?      
560 ; could even coerce based on type of first arg...   761 ; Return val has same type as first argument.
561   = 762  
562 (xdef '+ (lambda args <> 763 (xdef + (lambda args
563            (cond ((null? args) 0) = 764            (cond ((null? args) 0)
564                  ((all string? args)  <> 765                  ((char-or-string? (car args))
565                   (apply string-append args))   766                   (apply string-append
      767                          (map (lambda (a) (ar-coerce a 'string))
      768                               args)))
566                  ((all arc-list? args)    769                  ((arc-list? (car args)
567                   (ac-niltree (apply append (map ar-nil-terminate args)))) = 770                   (ac-niltree (apply append (map ar-nil-terminate args))))
568                  (#t (apply + args)))))   771                  (#t (apply + args)))))
569     772  
    <> 773 (define (char-or-string? x) (or (string? x) (char? x)))
      774  
      775 (define (ar-+2 x y)
      776   (cond ((char-or-string? x)
      777          (string-append (ar-coerce x 'string) (ar-coerce y 'string)))
      778         ((and (arc-list? x) (arc-list? y))
      779          (ac-niltree (append (ar-nil-terminate x) (ar-nil-terminate y))))
      780         (#t (+ x y))))
      781  
570 (xdef '- -)   782 (xdef - -)
571 (xdef '* *)   783 (xdef * *)
572 (xdef '/ /)   784 (xdef / /)
573 (xdef 'mod modulo)   785 (xdef mod modulo)
574 (xdef 'expt expt)   786 (xdef expt expt)
575 (xdef 'sqrt sqrt)   787 (xdef sqrt sqrt)
576   = 788  
577 ; generic comparison   789 ; generic comparison
578     790  
579 (define (arc> . args) <> 791 (define (ar->2 x y)
580   (cond ((all number? args) (apply > args))   792   (tnil (cond ((and (number? x) (number? y)) (> x y))
581         ((all string? args) (pairwise string>? args #f))   793               ((and (string? x) (string? y)) (string>? x y))
582         ((all symbol? args) (pairwise (lambda (x y)      
583                                         (string>? (symbol->string x)    794               ((and (symbol? x) (symbol? y)) (string>? (symbol->string x)
584                                                   (symbol->string y)))   795                                                        (symbol->string y)))
585                                  args   796               ((and (char? x) (char? y)) (char>? x y))
586                                      #f))   797               (#t (> x y)))))
587         ((all char?   args) (pairwise char>?   args #f))   798  
588         (#t                 (apply > args))))      
589 (xdef '>  (lambda args (if (apply arc> args) 't 'nil)))   799 (xdef > (lambda args (pairwise ar->2 args)))
590   = 800  
591 (define (arc< . args) <> 801 (define (ar-<2 x y)
592   (cond ((all number? args) (apply < args))   802   (tnil (cond ((and (number? x) (number? y)) (< x y))
593         ((all string? args) (pairwise string<? args #f))   803               ((and (string? x) (string? y)) (string<? x y))
594         ((all symbol? args) (pairwise (lambda (x y)      
595                                         (string<? (symbol->string x)    804               ((and (symbol? x) (symbol? y)) (string<? (symbol->string x)
596                                                   (symbol->string y)))   805                                                        (symbol->string y)))
597                                  args   806               ((and (char? x) (char? y)) (char<? x y))
598                                      #f))   807               (#t (< x y)))))
599         ((all char?   args) (pairwise char<?   args #f))   808  
600         (#t                 (apply < args))))      
601 (xdef '<  (lambda args (if (apply arc< args) 't 'nil)))   809 (xdef < (lambda args (pairwise ar-<2 args)))
602   = 810  
603 (xdef 'len (lambda (x) <> 811 (xdef len (lambda (x)
604              (cond ((string? x) (string-length x)) = 812              (cond ((string? x) (string-length x))
605                    ((hash-table? x) (hash-table-count x))   813                    ((hash-table? x) (hash-table-count x))
606                    (#t (length (ar-nil-terminate x))))))   814                    (#t (length (ar-nil-terminate x))))))
607     815  
608 (define (ar-tagged? x)   816 (define (ar-tagged? x)
609   (and (vector? x) (eq? (vector-ref x 0) 'tagged)))   817   (and (vector? x) (eq? (vector-ref x 0) 'tagged)))
610     818  
611 (define (ar-tag type rep)   819 (define (ar-tag type rep)
612   (cond ((eqv? (ar-type rep) type) rep)   820   (cond ((eqv? (ar-type rep) type) rep)
613         (#t (vector 'tagged type rep))))   821         (#t (vector 'tagged type rep))))
    <> 822  
614 (xdef 'annotate ar-tag)   823 (xdef annotate ar-tag)
615   = 824  
616 ; (type nil) -> sym   825 ; (type nil) -> sym
    -+ 826  
      827 (define (exint? x) (and (integer? x) (exact? x)))
617   = 828  
618 (define (ar-type x)   829 (define (ar-type x)
619   (cond ((ar-tagged? x)     (vector-ref x 1))   830   (cond ((ar-tagged? x)     (vector-ref x 1))
620         ((pair? x)          'cons)   831         ((pair? x)          'cons)
621         ((symbol? x)        'sym)   832         ((symbol? x)        'sym)
622         ((null? x)          'sym)   833         ((null? x)          'sym)
623         ((procedure? x)     'fn)   834         ((procedure? x)     'fn)
624         ((char? x)          'char)   835         ((char? x)          'char)
625         ((string? x)        'string)   836         ((string? x)        'string)
626         ((integer? x)       'int) <> 837         ((exint? x)         'int)
627         ((number? x)        'num)     ; unsure about this = 838         ((number? x)        'num)     ; unsure about this
628         ((hash-table? x)    'table)   839         ((hash-table? x)    'table)
629         ((output-port? x)   'output)   840         ((output-port? x)   'output)
630         ((input-port? x)    'input)   841         ((input-port? x)    'input)
631         ((tcp-listener? x)  'socket)   842         ((tcp-listener? x)  'socket)
632         ((exn? x)           'exception)   843         ((exn? x)           'exception)
    -+ 844         ((thread? x)        'thread)
633         (#t                 (err "Type: unknown type" x)))) = 845         (#t                 (err "Type: unknown type" x))))
634 (xdef 'type ar-type) <> 846 (xdef type ar-type)
635   = 847  
636 (define (ar-rep x)   848 (define (ar-rep x)
637   (if (ar-tagged? x)   849   (if (ar-tagged? x)
638       (vector-ref x 2)   850       (vector-ref x 2)
639       x))   851       x))
    <> 852  
640 (xdef 'rep ar-rep)   853 (xdef rep ar-rep)
641   = 854  
642 ; currently rather a joke: returns interned symbols   855 ; currently rather a joke: returns interned symbols
643     856  
644 (define ar-gensym-count 0)   857 (define ar-gensym-count 0)
    -+ 858  
645 (define (ar-gensym) = 859 (define (ar-gensym)
646   (set! ar-gensym-count (+ ar-gensym-count 1))   860   (set! ar-gensym-count (+ ar-gensym-count 1))
647   (string->symbol (string-append "gs" (number->string ar-gensym-count))))   861   (string->symbol (string-append "gs" (number->string ar-gensym-count))))
    <> 862  
648 (xdef 'uniq ar-gensym)   863 (xdef uniq ar-gensym)
649   = 864  
650 (xdef 'ccc call-with-current-continuation) <> 865 (xdef ccc call-with-current-continuation)
651   = 866  
652 (xdef 'infile  open-input-file) <> 867 (xdef infile  open-input-file)
      868  
653 (xdef 'outfile (lambda (f . args)    869 (xdef outfile (lambda (f . args) 
654                  (open-output-file f  = 870                  (open-output-file f 
655                                    'text   871                                    'text
656                                    (if (equal? args '(append))   872                                    (if (equal? args '(append))
657                                        'append   873                                        'append
658                                        'truncate))))   874                                        'truncate))))
659     875  
660 (xdef 'instring  open-input-string) <> 876 (xdef instring  open-input-string)
661 (xdef 'outstring open-output-string)   877 (xdef outstring open-output-string)
662   = 878  
663 ; use as general fn for looking inside things   879 ; use as general fn for looking inside things
664     880  
665 (xdef 'inside get-output-string) <> 881 (xdef inside get-output-string)
666   = 882  
667 (xdef 'close (lambda (p) <>    
668                (cond ((input-port? p)   (close-input-port p))      
669                      ((output-port? p)  (close-output-port p))      
670                      ((tcp-listener? p) (tcp-close p))      
671                      (#t (err "Can't close " p)))      
672                'nil))      
673        
674 (xdef 'stdout current-output-port)  ; should be a vars   883 (xdef stdout current-output-port)  ; should be a vars
675 (xdef 'stdin  current-input-port)    884 (xdef stdin  current-input-port) 
676 (xdef 'stderr current-error-port)   885 (xdef stderr current-error-port)
677   = 886  
678 (xdef 'call-w/stdout <> 887 (xdef call-w/stdout
679       (lambda (port thunk) = 888       (lambda (port thunk)
680         (parameterize ((current-output-port port)) (thunk))))   889         (parameterize ((current-output-port port)) (thunk))))
681     890  
682 (xdef 'call-w/stdin <> 891 (xdef call-w/stdin
683       (lambda (port thunk) = 892       (lambda (port thunk)
684         (parameterize ((current-input-port port)) (thunk))))   893         (parameterize ((current-input-port port)) (thunk))))
685     894  
686 ; (readc stream) <>    
687 ; nil stream means stdout      
688 ; returns nil on eof      
689        
690 (xdef 'readc (lambda (str)    895 (xdef readc (lambda str
691                (let ((p (if (ar-false? str)   896               (let ((c (read-char (if (pair? str)
      897                                       (car str)
692                             (current-input-port)   898                                       (current-input-port)))))
693                             str)))      
694                  (let ((c (read-char p)))      
695                    (if (eof-object? c) 'nil c)))))   899                 (if (eof-object? c) 'nil c))))
696   = 900  
    <> 901  
697 (xdef 'readb (lambda (str)   902 (xdef readb (lambda str
698                (let ((p (if (ar-false? str)   903               (let ((c (read-byte (if (pair? str)
      904                                       (car str)
699                             (current-input-port)   905                                       (current-input-port)))))
700                             str)))      
701                  (let ((c (read-byte p)))      
702                    (if (eof-object? c) 'nil c)))))   906                 (if (eof-object? c) 'nil c))))
703   = 907  
704 (xdef 'peekc (lambda (str)  <> 908 (xdef peekc (lambda str 
705                (let ((p (if (ar-false? str)   909               (let ((c (peek-char (if (pair? str)
      910                                       (car str)
706                             (current-input-port)   911                                       (current-input-port)))))
707                             str)))      
708                  (let ((c (peek-char p)))      
709                    (if (eof-object? c) 'nil c)))))   912                 (if (eof-object? c) 'nil c))))
710   = 913  
711 (xdef 'writec (lambda (c . args)  <> 914 (xdef writec (lambda (c . args) 
712                 (write-char c  = 915                 (write-char c 
713                             (if (pair? args)    916                             (if (pair? args) 
714                                 (car args)    917                                 (car args) 
715                                 (current-output-port)))   918                                 (current-output-port)))
716                 c))   919                 c))
717     920  
718 (xdef 'writeb (lambda (b . args)  <> 921 (xdef writeb (lambda (b . args) 
719                 (write-byte b  = 922                 (write-byte b 
720                             (if (pair? args)    923                             (if (pair? args) 
721                                 (car args)    924                                 (car args) 
722                                 (current-output-port)))   925                                 (current-output-port)))
723                 b))   926                 b))
724     927  
    <> 928 (define explicit-flush #f)
725 (xdef 'write (lambda args   929  
726                (if (pair? args)   930 (define (printwith f args)
727                    (write (ac-denil (car args))   931   (let ((port (if (> (length args) 1)
728                           (if (pair? (cdr args))      
729                               (cadr args)   932                   (cadr args)
730                               (current-output-port))))   933                   (current-output-port))))
731                (flush-output)      
732                'nil))      
733        
734 (xdef 'disp (lambda args      
735               (if (pair? args)   934     (when (pair? args)
736                   (display (ac-denil (car args))   935       (f (ac-denil (car args)) port))
737                            (if (pair? (cdr args))       
738                                (cadr args)      
739                                (current-output-port))))   936     (unless explicit-flush (flush-output port)))
740               (flush-output)      
741               'nil))   937   'nil)
742   = 938  
    -+ 939 (xdef write (lambda args (printwith write   args)))
      940 (xdef disp  (lambda args (printwith display args)))
      941  
743 ; sread = scheme read. eventually replace by writing read = 942 ; sread = scheme read. eventually replace by writing read
744     943  
745 (xdef 'sread (lambda (p eof) <> 944 (xdef sread (lambda (p eof)
746                (let ((expr (read p))) = 945                (let ((expr (read p)))
747                  (if (eof-object? expr) eof expr))))   946                  (if (eof-object? expr) eof expr))))
748     947  
749 ; these work in PLT but not scheme48   948 ; these work in PLT but not scheme48
750     949  
751 (define char->ascii char->integer)   950 (define char->ascii char->integer)
752 (define ascii->char integer->char)   951 (define ascii->char integer->char)
753     952  
    <> 953 (define (iround x) (inexact->exact (round x)))
      954  
754 (xdef 'coerce (lambda (x type . args)   955 (define (ar-coerce x type . args)
755                 (cond    956   (cond 
756                   ((ar-tagged? x) (err "Can't coerce annotated object"))   957     ((ar-tagged? x) (err "Can't coerce annotated object"))
757                   ((eqv? type (ar-type x)) x)   958     ((eqv? type (ar-type x)) x)
758        
759                   ((char? x)      (case type   959     ((char? x)      (case type
760                                     ((int)    (char->ascii x))   960                       ((int)     (char->ascii x))
761                                     ((string) (string x))   961                       ((string)  (string x))
762                                     ((sym)    (string->symbol (string x)))   962                       ((sym)     (string->symbol (string x)))
763                                     (else     (err "Can't coerce" x type))))   963                       (else      (err "Can't coerce" x type))))
764                   ((integer? x)   (case type   964     ((exint? x)     (case type
      965                       ((num)     x)
765                                     ((char)   (ascii->char x))   966                       ((char)    (ascii->char x))
766                                     ((string) (apply number->string x args))   967                       ((string)  (apply number->string x args))
767                                     (else     (err "Can't coerce" x type))))   968                       (else      (err "Can't coerce" x type))))
768                   ((number? x)    (case type   969     ((number? x)    (case type
769                                     ((int)    (round x))   970                       ((int)     (iround x))
770                                     ((char)   (ascii->char (round x)))   971                       ((char)    (ascii->char (iround x)))
771                                     ((string) (apply number->string x args))   972                       ((string)  (apply number->string x args))
772                                     (else     (err "Can't coerce" x type))))   973                       (else      (err "Can't coerce" x type))))
773                   ((string? x)    (case type   974     ((string? x)    (case type
774                                     ((sym)    (string->symbol x))   975                       ((sym)     (string->symbol x))
775                                     ((cons)   (ac-niltree (string->list x)))   976                       ((cons)    (ac-niltree (string->list x)))
776                                     ((int)    (or (apply string->number x args)   977                       ((num)     (or (apply string->number x args)
777                                                   (err "Can't coerce" x type)))   978                                      (err "Can't coerce" x type)))
      979                       ((int)     (let ((n (apply string->number x args)))
      980                                    (if n 
      981                                        (iround n)
      982                                        (err "Can't coerce" x type))))
778                                     (else     (err "Can't coerce" x type))))   983                       (else      (err "Can't coerce" x type))))
779                   ((pair? x)      (case type   984     ((pair? x)      (case type
780                                     ((string) (list->string   985                       ((string)  (apply string-append
      986                                         (map (lambda (y) (ar-coerce y 'string)) 
781                                                (ar-nil-terminate x)))      987                                              (ar-nil-terminate x))))
782                                     (else     (err "Can't coerce" x type))))   988                       (else      (err "Can't coerce" x type))))
783                   ((eqv? x 'nil)  (case type   989     ((eqv? x 'nil)  (case type
784                                     ((string) "")   990                       ((string)  "")
785                                     (else     (err "Can't coerce" x type))))   991                       (else      (err "Can't coerce" x type))))
      992     ((null? x)      (case type
      993                       ((string)  "")
      994                       (else      (err "Can't coerce" x type))))
786                   ((symbol? x)    (case type    995     ((symbol? x)    (case type 
787                                     ((string) (symbol->string x))   996                       ((string)  (symbol->string x))
788                                     (else     (err "Can't coerce" x type))))   997                       (else      (err "Can't coerce" x type))))
789                   (#t             x))))   998     (#t             x)))
790   = 999  
    <> 1000 (xdef coerce ar-coerce)
      1001  
791 (xdef 'open-socket  (lambda (num) (tcp-listen num 50 #t)))    1002 (xdef open-socket  (lambda (num) (tcp-listen num 50 #t))) 
792   = 1003  
793 ; the 2050 means http requests currently capped at 2 meg   1004 ; the 2050 means http requests currently capped at 2 meg
794 ; http://list.cs.brown.edu/pipermail/plt-scheme/2005-August/009414.html   1005 ; http://list.cs.brown.edu/pipermail/plt-scheme/2005-August/009414.html
795     1006  
796 (xdef 'socket-accept (lambda (s) <> 1007 (xdef socket-accept (lambda (s)
      1008                       (let ((oc (current-custodian))
      1009                             (nc (make-custodian)))
      1010                         (current-custodian nc)
797                        (call-with-values   1011                         (call-with-values
798                          (lambda () (tcp-accept s)) = 1012                          (lambda () (tcp-accept s))
799                          (lambda (in out)   1013                          (lambda (in out)
800                            (list (make-limited-input-port in 100000 #t) <> 1014                            (let ((in1 (make-limited-input-port in 100000 #t)))
      1015                              (current-custodian oc)
      1016                              (associate-custodian nc in1 out)
      1017                              (list in1
801                                  out   1018                                    out
802                                  (let-values (((us them) (tcp-addresses out)))   1019                                    (let-values (((us them) (tcp-addresses out)))
803                                    them))))))   1020                                                them))))))))
804   = 1021  
    <> 1022 ; allow Arc to give up root privileges after it
      1023 ; calls open-socket. thanks, Eli!
      1024 (define setuid (get-ffi-obj 'setuid #f (_fun _int -> _int)))
      1025 (xdef setuid setuid)
      1026  
805 (xdef 'thread thread)   1027 (xdef new-thread thread)
806 (xdef 'kill-thread kill-thread)   1028 (xdef kill-thread kill-thread)
807 (xdef 'break-thread break-thread)   1029 (xdef break-thread break-thread)
      1030 (xdef current-thread current-thread)
808   = 1031  
809 (define (wrapnil f) (lambda args (apply f args) 'nil))   1032 (define (wrapnil f) (lambda args (apply f args) 'nil))
810     1033  
811 (xdef 'sleep (wrapnil sleep)) <> 1034 (xdef sleep (wrapnil sleep))
812   = 1035  
813 ; Will system "execute" a half-finished string if thread killed   1036 ; Will system "execute" a half-finished string if thread killed
814 ; in the middle of generating it?     1037 ; in the middle of generating it?  
815     1038  
816 (xdef 'system (wrapnil system)) <> 1039 (xdef system (wrapnil system))
817   = 1040  
818 (xdef 'pipe-from (lambda (cmd) <> 1041 (xdef pipe-from (lambda (cmd)
819                    (let ((tf (ar-tmpname))) = 1042                    (let ((tf (ar-tmpname)))
820                      (system (string-append cmd " > " tf))   1043                      (system (string-append cmd " > " tf))
821                      (let ((str (open-input-file tf)))   1044                      (let ((str (open-input-file tf)))
822                        (system (string-append "rm -f " tf))   1045                        (system (string-append "rm -f " tf))
823                        str))))   1046                        str))))
824                       1047                    
825 (define (ar-tmpname)   1048 (define (ar-tmpname)
826   (call-with-input-file "/dev/urandom"   1049   (call-with-input-file "/dev/urandom"
827     (lambda (rstr)   1050     (lambda (rstr)
828       (do ((s "/tmp/")   1051       (do ((s "/tmp/")
829            (c (read-char rstr) (read-char rstr))   1052            (c (read-char rstr) (read-char rstr))
830            (i 0 (+ i 1)))   1053            (i 0 (+ i 1)))
831           ((>= i 16) s)   1054           ((>= i 16) s)
832         (set! s (string-append s   1055         (set! s (string-append s
833                                (string   1056                                (string
834                                  (integer->char   1057                                  (integer->char
835                                    (+ (char->integer #\a)   1058                                    (+ (char->integer #\a)
836                                       (modulo   1059                                       (modulo
837                                         (char->integer (read-char rstr))   1060                                         (char->integer (read-char rstr))
838                                         26))))))))))   1061                                         26))))))))))
839     1062  
840 ; PLT scheme provides only eq? and equal? hash tables,   1063 ; PLT scheme provides only eq? and equal? hash tables,
841 ; we need the latter for strings.   1064 ; we need the latter for strings.
842     1065  
    <> 1066 (xdef table (lambda args
843 (xdef 'table (lambda () (make-hash-table 'equal)))   1067               (let ((h (make-hash-table 'equal)))
      1068                 (if (pair? args) ((car args) h))
      1069                 h)))
844   = 1070  
845 ;(xdef 'table (lambda args <> 1071 ;(xdef table (lambda args
846 ;               (fill-table (make-hash-table 'equal)  = 1072 ;               (fill-table (make-hash-table 'equal) 
847 ;                           (if (pair? args) (ac-denil (car args)) '()))))   1073 ;                           (if (pair? args) (ac-denil (car args)) '()))))
848                       1074                    
849 (define (fill-table h pairs)   1075 (define (fill-table h pairs)
850   (if (eq? pairs '())   1076   (if (eq? pairs '())
851       h   1077       h
852       (let ((pair (car pairs)))   1078       (let ((pair (car pairs)))
853         (begin (hash-table-put! h (car pair) (cadr pair))   1079         (begin (hash-table-put! h (car pair) (cadr pair))
854                (fill-table h (cdr pairs))))))   1080                (fill-table h (cdr pairs))))))
855     1081  
856 (xdef 'maptable (lambda (fn table)               ; arg is (fn (key value) ...) <> 1082 (xdef maptable (lambda (fn table)               ; arg is (fn (key value) ...)
857                   (hash-table-for-each table fn) = 1083                   (hash-table-for-each table fn)
858                   table))   1084                   table))
859     1085  
860 (xdef 'protect (lambda (during after) <> 1086 (define (protect during after)
861                   (dynamic-wind (lambda () #t) during after)))   1087   (dynamic-wind (lambda () #t) during after))
862   = 1088  
    -+ 1089 (xdef protect protect)
      1090  
863 ; need to use a better seed = 1091 ; need to use a better seed
864     1092  
865 (xdef 'rand random) <> 1093 (xdef rand random)
866   = 1094  
    <> 1095 (xdef dir (lambda (name)
867 (xdef 'dir (lambda (name) (map path->string (directory-list name))))   1096             (ac-niltree (map path->string (directory-list name)))))
868   = 1097  
    <> 1098 ; Would def mkdir in terms of make-directory and call that instead
      1099 ; of system in ensure-dir, but make-directory is too weak: it doesn't
      1100 ; create intermediate directories like mkdir -p.
      1101  
869 (xdef 'file-exists (lambda (name)   1102 (xdef file-exists (lambda (name)
870                      (if (file-exists? name) name 'nil))) = 1103                      (if (file-exists? name) name 'nil)))
871     1104  
872 (xdef 'dir-exists (lambda (name) <> 1105 (xdef dir-exists (lambda (name)
873                      (if (directory-exists? name) name 'nil))) = 1106                      (if (directory-exists? name) name 'nil)))
874     1107  
875 (xdef 'rmfile (wrapnil delete-file)) <> 1108 (xdef rmfile (wrapnil delete-file))
876   = 1109  
    -+ 1110 (xdef mvfile (lambda (old new)
      1111                 (rename-file-or-directory old new #t)
      1112                 'nil))
      1113  
877 ; top level read-eval-print = 1114 ; top level read-eval-print
878 ; tle kept as a way to get a break loop when a scheme err   1115 ; tle kept as a way to get a break loop when a scheme err
879     1116  
880 (define (arc-eval expr)    1117 (define (arc-eval expr) 
881   (eval (ac expr '()) (interaction-environment))) <> 1118   (eval (ac expr '())))
882   = 1119  
883 (define (tle)   1120 (define (tle)
884   (display "Arc> ")   1121   (display "Arc> ")
885   (let ((expr (read)))   1122   (let ((expr (read)))
886     (when (not (eqv? expr ':a))   1123     (when (not (eqv? expr ':a))
887       (write (arc-eval expr))   1124       (write (arc-eval expr))
888       (newline)   1125       (newline)
889       (tle))))   1126       (tle))))
890     1127  
891 (define last-condition* #f)   1128 (define last-condition* #f)
892     1129  
893 (define (tl)   1130 (define (tl)
894   (display "Use (quit) to quit, (tl) to return here after an interrupt.\n")   1131   (display "Use (quit) to quit, (tl) to return here after an interrupt.\n")
895   (tl2))   1132   (tl2))
896     1133  
897 (define (tl2)   1134 (define (tl2)
898   (display "arc> ")   1135   (display "arc> ")
899   (on-err (lambda (c)    1136   (on-err (lambda (c) 
900             (set! last-condition* c)   1137             (set! last-condition* c)
901             (display "Error: ")   1138             (display "Error: ")
902             (write (exn-message c))   1139             (write (exn-message c))
903             (newline)   1140             (newline)
904             (tl2))   1141             (tl2))
905     (lambda ()   1142     (lambda ()
906       (let ((expr (read)))   1143       (let ((expr (read)))
907         (if (eqv? expr ':a)   1144         (if (eqv? expr ':a)
908             'done   1145             'done
909             (let ((val (arc-eval expr)))   1146             (let ((val (arc-eval expr)))
910               (write (ac-denil val))   1147               (write (ac-denil val))
911               (namespace-set-variable-value! '_that val)   1148               (namespace-set-variable-value! '_that val)
912               (namespace-set-variable-value! '_thatexpr expr)   1149               (namespace-set-variable-value! '_thatexpr expr)
913               (newline)   1150               (newline)
914               (tl2)))))))   1151               (tl2)))))))
915     1152  
916 (define (aload1 p)   1153 (define (aload1 p)
917   (let ((x (read p)))   1154   (let ((x (read p)))
918     (if (eof-object? x)   1155     (if (eof-object? x)
919         #t   1156         #t
920         (begin   1157         (begin
921           (arc-eval x)   1158           (arc-eval x)
922           (aload1 p)))))   1159           (aload1 p)))))
923     1160  
924 (define (atests1 p)   1161 (define (atests1 p)
925   (let ((x (read p)))   1162   (let ((x (read p)))
926     (if (eof-object? x)   1163     (if (eof-object? x)
927         #t   1164         #t
928         (begin   1165         (begin
929           (write x)   1166           (write x)
930           (newline)   1167           (newline)
931           (let ((v (arc-eval x)))   1168           (let ((v (arc-eval x)))
932             (if (ar-false? v)   1169             (if (ar-false? v)
933                 (begin   1170                 (begin
934                   (display "  FAILED")   1171                   (display "  FAILED")
935                   (newline))))   1172                   (newline))))
936           (atests1 p)))))   1173           (atests1 p)))))
937     1174  
938 (define (aload filename)   1175 (define (aload filename)
939   (call-with-input-file filename aload1))   1176   (call-with-input-file filename aload1))
940     1177  
941 (define (test filename)   1178 (define (test filename)
942   (call-with-input-file filename atests1))   1179   (call-with-input-file filename atests1))
943     1180  
944 (define (acompile1 ip op)   1181 (define (acompile1 ip op)
945   (let ((x (read ip)))   1182   (let ((x (read ip)))
946     (if (eof-object? x)   1183     (if (eof-object? x)
947         #t   1184         #t
948         (let ((scm (ac x '())))   1185         (let ((scm (ac x '())))
949           (eval scm (interaction-environment)) <> 1186           (eval scm)
950           (pretty-print scm op) = 1187           (pretty-print scm op)
951           (newline op)   1188           (newline op)
952           (newline op)   1189           (newline op)
953           (acompile1 ip op)))))   1190           (acompile1 ip op)))))
954     1191  
955 ; compile xx.arc to xx.arc.scm   1192 ; compile xx.arc to xx.arc.scm
956 ; useful to examine the Arc compiler output   1193 ; useful to examine the Arc compiler output
957 (define (acompile inname)   1194 (define (acompile inname)
958   (let ((outname (string-append inname ".scm")))   1195   (let ((outname (string-append inname ".scm")))
959     (if (file-exists? outname)   1196     (if (file-exists? outname)
960         (delete-file outname))   1197         (delete-file outname))
961     (call-with-input-file inname   1198     (call-with-input-file inname
962       (lambda (ip)   1199       (lambda (ip)
963         (call-with-output-file outname    1200         (call-with-output-file outname 
964           (lambda (op)   1201           (lambda (op)
965             (acompile1 ip op)))))))   1202             (acompile1 ip op)))))))
966     1203  
967 (xdef 'macex (lambda (e) (ac-macex (ac-denil e)))) <> 1204 (xdef macex (lambda (e) (ac-macex (ac-denil e))))
968   = 1205  
969 (xdef 'macex1 (lambda (e) (ac-macex (ac-denil e) 'once))) <> 1206 (xdef macex1 (lambda (e) (ac-macex (ac-denil e) 'once)))
970   = 1207  
971 (xdef 'eval (lambda (e) <> 1208 (xdef eval (lambda (e)
972               (eval (ac (ac-denil e) '()) (interaction-environment))))   1209               (eval (ac (ac-denil e) '()))))
973   = 1210  
974 ; If an err occurs in an on-err expr, no val is returned and code   1211 ; If an err occurs in an on-err expr, no val is returned and code
975 ; after it doesn't get executed.  Not quite what I had in mind.   1212 ; after it doesn't get executed.  Not quite what I had in mind.
976     1213  
977 (define (on-err errfn f)   1214 (define (on-err errfn f)
978   ((call-with-current-continuation    1215   ((call-with-current-continuation 
979      (lambda (k)    1216      (lambda (k) 
980        (lambda ()    1217        (lambda () 
981          (with-handlers ((exn:fail? (lambda (c)    1218          (with-handlers ((exn:fail? (lambda (c) 
982                                       (k (lambda () (errfn c))))))    1219                                       (k (lambda () (errfn c)))))) 
983                         (f)))))))   1220                         (f)))))))
984 (xdef 'on-err on-err) <> 1221 (xdef on-err on-err)
985   = 1222  
986 (define (write-to-string x) <> 1223 (define (disp-to-string x)
987   (let ((o (open-output-string))) = 1224   (let ((o (open-output-string)))
988     (write x o) <> 1225     (display x o)
989     (close-output-port o) = 1226     (close-output-port o)
990     (get-output-string o)))   1227     (get-output-string o)))
991     1228  
992 (xdef 'details (lambda (c) <> 1229 (xdef details (lambda (c)
993                  (write-to-string (exn-message c))))   1230                  (disp-to-string (exn-message c))))
994   = 1231  
995 (xdef 'scar (lambda (x val)  <> 1232 (xdef scar (lambda (x val) 
996               (if (string? x)  = 1233               (if (string? x) 
997                   (string-set! x 0 val)   1234                   (string-set! x 0 val)
998                   (set-car! x val)) <> 1235                   (x-set-car! x val))
999               val)) = 1236               val))
1000     1237  
1001 (xdef 'scdr (lambda (x val)  <> 1238 (xdef scdr (lambda (x val) 
1002               (if (string? x) = 1239               (if (string? x)
1003                   (err "Can't set cdr of a string" x)   1240                   (err "Can't set cdr of a string" x)
1004                   (set-cdr! x val)) <> 1241                   (x-set-cdr! x val))
1005               val)) = 1242               val))
    -+ 1243  
      1244 ; decide at run-time whether the underlying mzscheme supports
      1245 ; set-car! and set-cdr!, since I can't figure out how to do it
      1246 ; at compile time.
      1247  
      1248 (define (x-set-car! p v)
      1249   (let ((fn (namespace-variable-value 'set-car! #t (lambda () #f))))
      1250     (if (procedure? fn)
      1251         (fn p v)
      1252         (n-set-car! p v))))
      1253  
      1254 (define (x-set-cdr! p v)
      1255   (let ((fn (namespace-variable-value 'set-cdr! #t (lambda () #f))))
      1256     (if (procedure? fn)
      1257         (fn p v)
      1258         (n-set-cdr! p v))))
      1259  
      1260 ; Eli's code to modify mzscheme-4's immutable pairs.
      1261  
      1262 ;; to avoid a malloc on every call, reuse a single pointer, but make
      1263 ;; it thread-local to avoid races
      1264 (define ptr (make-thread-cell #f))
      1265 (define (get-ptr)
      1266   (or (thread-cell-ref ptr)
      1267       (let ([p (malloc _scheme 1)]) (thread-cell-set! ptr p) p)))
      1268  
      1269 ;; set a pointer to the cons cell, then dereference it as a pointer,
      1270 ;; and bang the new value in the given offset
      1271 (define (set-ca/dr! offset who p x)
      1272   (if (pair? p)
      1273     (let ([p* (get-ptr)])
      1274       (ptr-set! p* _scheme p)
      1275       (ptr-set! (ptr-ref p* _pointer 0) _scheme offset x))
      1276     (raise-type-error who "pair" p)))
      1277  
      1278 (define (n-set-car! p x) (set-ca/dr! 1 'set-car! p x))
      1279 (define (n-set-cdr! p x) (set-ca/dr! 2 'set-cdr! p x))
1006   = 1280  
1007 ; When and if cdr of a string returned an actual (eq) tail, could   1281 ; When and if cdr of a string returned an actual (eq) tail, could
1008 ; say (if (string? x) (string-replace! x val 1) ...) in scdr, but   1282 ; say (if (string? x) (string-replace! x val 1) ...) in scdr, but
1009 ; for now would be misleading to allow this, because fails for cddr.   1283 ; for now would be misleading to allow this, because fails for cddr.
1010     1284  
1011 (define (string-replace! str val index)   1285 (define (string-replace! str val index)
1012   (if (eqv? (string-length val) (- (string-length str) index))   1286   (if (eqv? (string-length val) (- (string-length str) index))
1013       (do ((i index (+ i 1)))   1287       (do ((i index (+ i 1)))
1014           ((= i (string-length str)) str)   1288           ((= i (string-length str)) str)
1015         (string-set! str i (string-ref val (- i index))))   1289         (string-set! str i (string-ref val (- i index))))
1016       (err "Length mismatch between strings" str val index)))   1290       (err "Length mismatch between strings" str val index)))
1017     1291  
    <> 1292 ; Later may want to have multiple indices.
1018 (xdef 'sref (lambda (com val ind) ; later make ind rest arg   1293  
      1294 (xdef sref 
      1295   (lambda (com val ind)
1019               (cond ((hash-table? com)  (if (eqv? val 'nil)   1296     (cond ((hash-table? com)  (if (eqv? val 'nil)
1020                                             (hash-table-remove! com ind)   1297                                   (hash-table-remove! com ind)
1021                                             (hash-table-put! com ind val)))   1298                                   (hash-table-put! com ind val)))
1022                     ((string? com) (string-set! com ind val))   1299           ((string? com) (string-set! com ind val))
1023                     ((pair? com)   (nth-set! com ind val))   1300           ((pair? com)   (nth-set! com ind val))
1024                     (#t (err "Can't set reference " com ind val)))   1301           (#t (err "Can't set reference " com ind val)))
1025               val))   1302     val))
1026   = 1303  
1027 (define (nth-set! lst n val)   1304 (define (nth-set! lst n val)
1028   (set-car! (list-tail lst n) val)) <> 1305   (x-set-car! (list-tail lst n) val))
1029   = 1306  
1030 ; rewrite to pass a (true) gensym instead of #f in case var bound to #f   1307 ; rewrite to pass a (true) gensym instead of #f in case var bound to #f
1031     1308  
1032 (define (bound? arcname)   1309 (define (bound? arcname)
1033   (namespace-variable-value (ac-global-name arcname)   1310   (namespace-variable-value (ac-global-name arcname)
1034                             #t   1311                             #t
1035                             (lambda () #f)))   1312                             (lambda () #f)))
1036     1313  
1037 (xdef 'bound (lambda (x) (if (bound? x) 't 'nil))) <> 1314 (xdef bound (lambda (x) (tnil (bound? x))))
1038   = 1315  
1039 (xdef 'newstring make-string) <> 1316 (xdef newstring make-string)
1040   = 1317  
1041 (xdef 'truncate (lambda (x) (inexact->exact (truncate x)))) <> 1318 (xdef trunc (lambda (x) (inexact->exact (truncate x))))
1042   = 1319  
    <> 1320 ; bad name
      1321  
1043 (xdef 'exact (lambda (x) (and (integer? x) (exact? x))))   1322 (xdef exact (lambda (x) (tnil (exint? x))))
1044   = 1323  
1045 (xdef 'msec current-milliseconds) <> 1324 (xdef msec                         current-milliseconds)
1046 (xdef 'current-process-milliseconds current-process-milliseconds)   1325 (xdef current-process-milliseconds current-process-milliseconds)
1047 (xdef 'current-gc-milliseconds current-gc-milliseconds)   1326 (xdef current-gc-milliseconds      current-gc-milliseconds)
1048   = 1327  
1049 (xdef 'seconds current-seconds) <> 1328 (xdef seconds current-seconds)
1050   = 1329  
1051 (print-hash-table #t)   1330 (print-hash-table #t)
1052     1331  
1053 (xdef 'client-ip (lambda (port)  <> 1332 (xdef client-ip (lambda (port) 
1054                    (let-values (((x y) (tcp-addresses port))) = 1333                    (let-values (((x y) (tcp-addresses port)))
1055                      y)))   1334                      y)))
1056     1335  
1057 ; make sure only one thread at a time executes anything   1336 ; make sure only one thread at a time executes anything
1058 ; inside an atomic-invoke. atomic-invoke is allowed to   1337 ; inside an atomic-invoke. atomic-invoke is allowed to
1059 ; nest within a thread; the thread-cell keeps track of   1338 ; nest within a thread; the thread-cell keeps track of
1060 ; whether this thread already holds the lock.   1339 ; whether this thread already holds the lock.
1061 ; XXX make sure cell is set #f after an exception? <> 1340  
1062 ; maybe it doesn't matter since thread will die?      
1063 (define ar-the-sema (make-semaphore 1)) = 1341 (define ar-the-sema (make-semaphore 1))
    -+ 1342  
1064 (define ar-sema-cell (make-thread-cell #f)) = 1343 (define ar-sema-cell (make-thread-cell #f))
    <> 1344  
1065 (xdef 'atomic-invoke (lambda (f)   1345 (xdef atomic-invoke (lambda (f)
1066                        (if (thread-cell-ref ar-sema-cell) = 1346                        (if (thread-cell-ref ar-sema-cell)
1067                            (ar-apply f '())   1347                            (ar-apply f '())
1068                            (begin   1348                            (begin
1069                              (thread-cell-set! ar-sema-cell #t)   1349                              (thread-cell-set! ar-sema-cell #t)
1070                              (let ((ret  <> 1350                              (protect
      1351                               (lambda ()
1071                                     (call-with-semaphore   1352                                 (call-with-semaphore
1072                                      ar-the-sema   1353                                  ar-the-sema
1073                                      (lambda () (ar-apply f '())))))   1354                                  (lambda () (ar-apply f '()))))
      1355                               (lambda ()
1074                                (thread-cell-set! ar-sema-cell #f)   1356                                 (thread-cell-set! ar-sema-cell #f)))))))
1075                                ret)))))   1357  
1076     1358 (xdef dead (lambda (x) (tnil (thread-dead? x))))
1077 (xdef 'dead thread-dead?)    1359  
1078     1360 ; Added because Mzscheme buffers output.  Not a permanent part of Arc.
1079 ; Added because Mzscheme buffers output.  Not sure if want as official   1361 ; Only need to use when declare explicit-flush optimization.
1080 ; part of Arc.      
1081   = 1362  
1082 ;(xdef 'flushout (lambda () (flush-output) 't)) <> 1363 (xdef flushout (lambda () (flush-output) 't))
1083   = 1364  
1084 (xdef 'ssyntax (lambda (x) (if (ssyntax? x) 't 'nil))) <> 1365 (xdef ssyntax (lambda (x) (tnil (ssyntax? x))))
1085   = 1366  
1086 (xdef 'ssexpand (lambda (x) <> 1367 (xdef ssexpand (lambda (x)
1087                   (if (symbol? x) (expand-ssyntax x) x))) = 1368                   (if (symbol? x) (expand-ssyntax x) x)))
1088     1369  
1089 (xdef 'quit exit) <> 1370 (xdef quit exit)
1090   = 1371  
    -+ 1372 ; there are two ways to close a TCP output port.
      1373 ; (close o) waits for output to drain, then closes UNIX descriptor.
      1374 ; (force-close o) discards buffered output, then closes UNIX desc.
      1375 ; web servers need the latter to get rid of connections to
      1376 ; clients that are not reading data.
      1377 ; mzscheme close-output-port doesn't work (just raises an error)
      1378 ; if there is buffered output for a non-responsive socket.
      1379 ; must use custodian-shutdown-all instead.
1091   = 1380  
    -+ 1381 (define custodians (make-hash-table 'equal))
      1382  
      1383 (define (associate-custodian c i o)
      1384   (hash-table-put! custodians i c)
      1385   (hash-table-put! custodians o c))
      1386  
      1387 ; if a port has a custodian, use it to close the port forcefully.
      1388 ; also get rid of the reference to the custodian.
      1389 ; sadly doing this to the input port also kills the output port.
      1390  
      1391 (define (try-custodian p)
      1392   (let ((c (hash-table-get custodians p #f)))
      1393     (if c
      1394         (begin
      1395           (custodian-shutdown-all c)
      1396           (hash-table-remove! custodians p)
      1397           #t)
      1398         #f)))
      1399  
      1400 (define (ar-close . args)
      1401   (map (lambda (p)
      1402          (cond ((input-port? p)   (close-input-port p))
      1403                ((output-port? p)  (close-output-port p))
      1404                ((tcp-listener? p) (tcp-close p))
      1405                (#t (err "Can't close " p))))
      1406        args)
      1407   (map (lambda (p) (try-custodian p)) args) ; free any custodian
      1408   'nil)
      1409  
      1410 (xdef close ar-close)
      1411  
      1412 (xdef force-close (lambda args
      1413                        (map (lambda (p)
      1414                               (if (not (try-custodian p))
      1415                                   (ar-close p)))
      1416                             args)
      1417                        'nil))
      1418  
      1419 (xdef memory current-memory-use)
      1420  
      1421 (xdef declare (lambda (key val)
      1422                 (let ((flag (not (ar-false? val))))
      1423                   (case key
      1424                     ((atstrings)      (set! atstrings      flag))
      1425                     ((direct-calls)   (set! direct-calls   flag))
      1426                     ((explicit-flush) (set! explicit-flush flag)))
      1427                   val)))
      1428  
      1429 (putenv "TZ" ":GMT")
      1430  
      1431 (define (gmt-date sec) (seconds->date sec))
      1432  
      1433 (xdef timedate 
      1434   (lambda args
      1435     (let ((d (gmt-date (if (pair? args) (car args) (current-seconds)))))
      1436       (ac-niltree (list (date-second d)
      1437                         (date-minute d)
      1438                         (date-hour d)
      1439                         (date-day d)
      1440                         (date-month d)
      1441                         (date-year d))))))
      1442  
      1443 (xdef sin sin)
      1444 (xdef cos cos)
      1445 (xdef tan tan)
      1446 (xdef asin asin)
      1447 (xdef acos acos)
      1448 (xdef atan atan)
      1449 (xdef log log)
      1450  
      1451 (define (codestring s)
      1452   (let ((i (atpos s 0)))
      1453     (if i
      1454         (cons (substring s 0 i)
      1455               (let* ((rest (substring s (+ i 1)))
      1456                      (in (open-input-string rest))
      1457                      (expr (read in))
      1458                      (i2 (let-values (((x y z) (port-next-location in))) z)))
      1459                 (close-input-port in)
      1460                 (cons expr (codestring (substring rest (- i2 1))))))
      1461         (list s))))
      1462  
      1463 ; First unescaped @ in s, if any.  Escape by doubling.
      1464  
      1465 (define (atpos s i)
      1466   (cond ((eqv? i (string-length s)) 
      1467          #f)
      1468         ((eqv? (string-ref s i) #\@)
      1469          (if (and (< (+ i 1) (string-length s))
      1470                   (not (eqv? (string-ref s (+ i 1)) #\@)))
      1471              i
      1472              (atpos s (+ i 2))))
      1473         (#t                         
      1474          (atpos s (+ i 1)))))
      1475  
      1476 (define (unescape-ats s)
      1477   (list->string (letrec ((unesc (lambda (cs)
      1478                                   (cond 
      1479                                     ((null? cs) 
      1480                                      '())
      1481                                     ((and (eqv? (car cs) #\@) 
      1482                                           (not (null? (cdr cs)))
      1483                                           (eqv? (cadr cs) #\@))
      1484                                      (unesc (cdr cs)))
      1485                                     (#t
      1486                                      (cons (car cs) (unesc (cdr cs))))))))
      1487                   (unesc (string->list s)))))
      1488   
1092 ) = 1489 )
1093     1490  
1094 (require ac) +-    
1095   = 1491  
1096 ./app.arc   1492 ./app.arc
1097 ; Application Server.  Layer inserted 2 Sep 06.   1493 ; Application Server.  Layer inserted 2 Sep 06.
1098     1494  
    <> 1495 ; ideas: 
1099 ; todo: def a general notion of apps of which the programming app is   1496 ; def a general notion of apps of which prompt is one, news another
1100 ;   one and the news site another.      
1101 ; give each user a place to store data?  A home dir? = 1497 ; give each user a place to store data?  A home dir?
1102     1498  
1103 ; A user is simply a string: "pg". Use /whoami to test user cookie.   1499 ; A user is simply a string: "pg". Use /whoami to test user cookie.
1104     1500  
1105 (= hpwfile*   "arc/hpw"   1501 (= hpwfile*   "arc/hpw"
    -+ 1502    oidfile*   "arc/openids"
1106    adminfile* "arc/admins" = 1503    adminfile* "arc/admins"
1107    cookfile*  "arc/cooks")   1504    cookfile*  "arc/cooks")
1108     1505  
1109 (def asv ((o port 8080))   1506 (def asv ((o port 8080))
1110   (load-userinfo)   1507   (load-userinfo)
1111   (serve port))   1508   (serve port))
1112     1509  
1113 (def load-userinfo ()   1510 (def load-userinfo ()
1114   (= hpasswords*   (safe-load-table hpwfile*)   1511   (= hpasswords*   (safe-load-table hpwfile*)
    -+ 1512      openids*      (safe-load-table oidfile*)
1115      admins*       (map string (errsafe (readfile adminfile*))) = 1513      admins*       (map string (errsafe (readfile adminfile*)))
1116      cookie->user* (safe-load-table cookfile*))   1514      cookie->user* (safe-load-table cookfile*))
1117   (maptable (fn (k v) (= (user->cookie* v) k))   1515   (maptable (fn (k v) (= (user->cookie* v) k))
1118             cookie->user*))   1516             cookie->user*))
1119     1517  
1120 ; idea: a bidirectional table, so don't need two vars (and sets)   1518 ; idea: a bidirectional table, so don't need two vars (and sets)
1121     1519  
1122 (= cookie->user* (table) user->cookie* (table) logins* (table))   1520 (= cookie->user* (table) user->cookie* (table) logins* (table))
1123     1521  
1124 (def get-user (req)    1522 (def get-user (req) 
1125   (let u (aand (alref (req 'cooks) "user") (cookie->user* (sym it))) <> 1523   (let u (aand (alref req!cooks "user") (cookie->user* (sym it)))
1126     (when u (= (logins* u) (req 'ip)))   1524     (when u (= (logins* u) req!ip))
1127     u)) = 1525     u))
1128     1526  
1129 (mac when-usermatch (user req . body) <> 1527 (mac when-umatch (user req . body)
1130   `(if (is ,user (get-user ,req)) = 1528   `(if (is ,user (get-user ,req))
1131        (do ,@body)   1529        (do ,@body)
1132        (mismatch-message)))   1530        (mismatch-message)))
1133     1531  
    <> 1532 (def mismatch-message () 
1134 (def mismatch-message () (prn "Dead link: users don't match."))   1533   (prn "Dead link: users don't match."))
1135   = 1534  
1136 (mac when-usermatchr (user req . body) <> 1535 (mac when-umatch/r (user req . body)
1137   `(if (is ,user (get-user ,req)) = 1536   `(if (is ,user (get-user ,req))
1138        (do ,@body)   1537        (do ,@body)
1139        "mismatch"))   1538        "mismatch"))
1140     1539  
1141 (defop mismatch req (mismatch-message))   1540 (defop mismatch req (mismatch-message))
1142     1541  
1143 (mac matchform (user req after . body) <> 1542 (mac uform (user req after . body)
1144   `(aform (fn (,req) = 1543   `(aform (fn (,req)
1145             (when-usermatch ,user ,req <> 1544             (when-umatch ,user ,req
1146               ,after)) = 1545               ,after))
1147      ,@body))   1546      ,@body))
1148     1547  
1149 (mac matchrform (user req after . body) <> 1548 (mac urform (user req after . body)
1150   `(arform (fn (,req) = 1549   `(arform (fn (,req)
1151              (when-usermatchr ,user ,req  <> 1550              (when-umatch/r ,user ,req 
1152                ,after)) = 1551                ,after))
1153      ,@body))   1552      ,@body))
1154     1553  
1155 ; Like onlink, but checks that user submitting the request is the   1554 ; Like onlink, but checks that user submitting the request is the
1156 ; same it was generated for.  Really should log the username and <> 1555 ; same it was generated for.  For extra protection could log the
1157 ; ip addr of every genlink, and check if they match.   1556 ; username and ip addr of every genlink, and check if they match.
1158   = 1557  
1159 (mac userlink (user text . body)   <> 1558 (mac ulink (user text . body)  
1160   (w/uniq req = 1559   (w/uniq req
1161     `(linkf ,text (,req)    1560     `(linkf ,text (,req) 
1162        (when-usermatch ,user ,req ,@body)))) <> 1561        (when-umatch ,user ,req ,@body))))
1163   = 1562  
1164     1563  
1165 (defop admin req (admin-gate (get-user req)))   1564 (defop admin req (admin-gate (get-user req)))
1166     1565  
1167 (def admin-gate (u)   1566 (def admin-gate (u)
1168   (if (admin u)   1567   (if (admin u)
1169       (admin-page u)   1568       (admin-page u)
1170       (login-page 'login nil   1569       (login-page 'login nil
1171                   (fn (u ip)  (admin-gate u)))))   1570                   (fn (u ip)  (admin-gate u)))))
1172     1571  
1173 (def admin (u) (and u (mem u admins*)))   1572 (def admin (u) (and u (mem u admins*)))
1174     1573  
1175 (def user-exists (u) (and u (hpasswords* u) u))   1574 (def user-exists (u) (and u (hpasswords* u) u))
1176     1575  
1177 (def admin-page (user . msg)   1576 (def admin-page (user . msg)
1178   (whitepage    1577   (whitepage 
1179     (prbold "Admin: ")   1578     (prbold "Admin: ")
1180     (hspace 20)   1579     (hspace 20)
1181     (pr user " | ")   1580     (pr user " | ")
1182     (w/link (do (logout-user user)   1581     (w/link (do (logout-user user)
1183                 (whitepage (pr "Bye " user ".")))   1582                 (whitepage (pr "Bye " user ".")))
1184       (pr "logout"))   1583       (pr "logout"))
1185     (when msg (hspace 10) (map pr msg))   1584     (when msg (hspace 10) (map pr msg))
1186     (br2)   1585     (br2)
1187     (aform (fn (req)   1586     (aform (fn (req)
1188              (when-usermatch user req <> 1587              (when-umatch user req
1189                (with (u (arg req "u") p (arg req "p")) = 1588                (with (u (arg req "u") p (arg req "p"))
1190                  (if (or (no u) (no p) (is u "") (is p ""))   1589                  (if (or (no u) (no p) (is u "") (is p ""))
1191                       (pr "Bad data.")   1590                       (pr "Bad data.")
1192                      (user-exists u)   1591                      (user-exists u)
1193                       (admin-page user "User already exists: " u)   1592                       (admin-page user "User already exists: " u)
1194                       (do (create-acct u p)   1593                       (do (create-acct u p)
1195                           (admin-page user))))))   1594                           (admin-page user))))))
1196       (pwfields "create (server) account"))))   1595       (pwfields "create (server) account"))))
1197   +-    
1198 ; need to define a notion of a hashtable that's always written      
1199 ; to a file when modified      
1200   = 1596  
1201 (def cook-user (user)   1597 (def cook-user (user)
1202   (let id (new-user-cookie)   1598   (let id (new-user-cookie)
1203     (= (cookie->user*   id) user   1599     (= (cookie->user*   id) user
1204        (user->cookie* user)   id)   1600        (user->cookie* user)   id)
1205     (save-table cookie->user* cookfile*)   1601     (save-table cookie->user* cookfile*)
1206     id))   1602     id))
1207     1603  
1208 ; Unique-ids are only unique per server invocation.   1604 ; Unique-ids are only unique per server invocation.
1209     1605  
1210 (def new-user-cookie ()   1606 (def new-user-cookie ()
1211   (let id (unique-id)   1607   (let id (unique-id)
1212     (if (cookie->user* id) (new-user-cookie) id)))   1608     (if (cookie->user* id) (new-user-cookie) id)))
1213     1609  
1214 (def logout-user (user)   1610 (def logout-user (user)
1215   (nil! (logins* user)) <> 1611   (wipe (logins* user))
1216   (nil! (cookie->user* (user->cookie* user)) (user->cookie* user))   1612   (wipe (cookie->user* (user->cookie* user)) (user->cookie* user))
1217   (save-table cookie->user* cookfile*)) = 1613   (save-table cookie->user* cookfile*))
1218     1614  
1219 (def create-acct (user pw)   1615 (def create-acct (user pw)
    -+ 1616   (set (dc-usernames* (downcase user)))
1220   (set-pw user pw)) = 1617   (set-pw user pw))
1221     1618  
1222 (def disable-acct (user)   1619 (def disable-acct (user)
1223   (set-pw user (rand-string 20))   1620   (set-pw user (rand-string 20))
1224   (logout-user user))   1621   (logout-user user))
1225      1622   
1226 (def set-pw (user pw)   1623 (def set-pw (user pw)
1227   (= (hpasswords* user) (and pw (shash pw)))   1624   (= (hpasswords* user) (and pw (shash pw)))
1228   (save-table hpasswords* hpwfile*))   1625   (save-table hpasswords* hpwfile*))
1229     1626  
1230 (def hello-page (user ip)   1627 (def hello-page (user ip)
1231   (whitepage (prs "hello" user "at" ip)))   1628   (whitepage (prs "hello" user "at" ip)))
1232     1629  
1233 (defop login req (login-page 'login))   1630 (defop login req (login-page 'login))
1234     1631  
1235 ; switch is one of: register, login, both   1632 ; switch is one of: register, login, both
    <> 1633  
1236 ; afterward is a function on the newly created user, ip addr   1634 ; afterward is either a function on the newly created username and
      1635 ; ip address, in which case it is called to generate the next page 
1237 or can be a list of such a fn and a string, in which case call fn   1636 ; after a successful login, or a pair of (function url), which means
1238 ;  then redirect to string   1637 ; call the function, then redirect to the url.
1239   = 1638  
1240 ; classic example of something that should just "return" a val   1639 ; classic example of something that should just "return" a val
1241 ; via a continuation rather than going to a new page.   1640 ; via a continuation rather than going to a new page.
1242     1641  
1243 ; ugly code-- too much duplication +-    
1244        
1245 (def login-page (switch (o msg nil) (o afterward hello-page)) = 1642 (def login-page (switch (o msg nil) (o afterward hello-page))
1246   (whitepage   1643   (whitepage
1247     (pagemessage msg)   1644     (pagemessage msg)
1248     (when (in switch 'login 'both)   1645     (when (in switch 'login 'both)
1249       (prbold "Login") <>    
1250       (br2)      
1251       (if (acons afterward)      
1252           (let (f url) afterward      
1253             (arformh (fn (req)      
1254                        (logout-user (get-user req))      
1255                        (aif (good-login (arg req "u") (arg req "p") (req 'ip))      
1256                             (do (= (logins* it) (req 'ip))      
1257                                 (prcookie (user->cookie* it))      
1258                                 (f it (req 'ip))      
1259                                 url)      
1260                             (flink (fn ignore (login-page switch   1646       (login-form "Login" switch login-handler afterward)
1261                                                           "Bad login."       
1262                                                           afterward)))))      
1263               (pwfields)))      
1264           (aformh  (fn (req)   1647       (hook 'login-form afterward)
1265                      (logout-user (get-user req))      
1266                      (aif (good-login (arg req "u") (arg req "p") (req 'ip))      
1267                           (do (= (logins* it) (req 'ip))      
1268                               (prcookie (user->cookie* it))      
1269                               (prn)      
1270                               (afterward it (req 'ip)))      
1271                           (do (prn)      
1272                               (login-page switch "Bad login." afterward))))      
1273             (pwfields)))      
1274       (br2)) = 1648       (br2))
1275     (when (in switch 'register 'both)   1649     (when (in switch 'register 'both)
    <> 1650       (login-form "Create Account" switch create-handler afterward))))
1276       (prbold "Create Account")   1651  
      1652 (def login-form (label switch handler afterward)
      1653   (prbold label)
1277       (br2)   1654   (br2)
      1655   (fnform (fn (req) (handler req switch afterward))
      1656           (fn () (pwfields (downcase label)))
1278       (if (acons afterward)   1657           (acons afterward)))
      1658  
1279           (let (f url) afterward   1659 (def login-handler (req switch afterward)
      1660   (logout-user (get-user req))
      1661   (aif (good-login (arg req "u") (arg req "p") req!ip)
      1662        (login it req!ip (user->cookie* it) afterward)
      1663        (failed-login switch "Bad login." afterward)))
1280             (arformh (fn (req)   1664  
      1665 (def create-handler (req switch afterward)
1281                        (logout-user (get-user req))   1666   (logout-user (get-user req))
1282                        (with (user (arg req "u") pw (arg req "p"))   1667   (with (user (arg req "u") pw (arg req "p"))
1283                          (aif (bad-newacct user pw)   1668     (aif (bad-newacct user pw)
1284                               (flink (fn ignore      
1285                                        (login-page switch it afterward)))   1669          (failed-login switch it afterward)
1286                               (do (create-acct user pw)   1670          (do (create-acct user pw)
      1671              (login user req!ip (cook-user user) afterward)))))
      1672  
      1673 (def login (user ip cookie afterward)
1287                                   (= (logins* user) (req 'ip))   1674   (= (logins* user) ip)
1288                                   (prcookie (cook-user user))   1675   (prcookie cookie)
      1676   (if (acons afterward)
      1677       (let (f url) afterward
1289                                   (f user (req 'ip))   1678         (f user ip)
1290                                   url))))   1679         url)
1291               (pwfields "create account")))      
1292           (aformh (fn (req)      
1293                     (logout-user (get-user req))      
1294                     (with (user (arg req "u") pw (arg req "p"))      
1295                       (aif (bad-newacct user pw)      
1296                            (do (prn)   1680       (do (prn)
      1681           (afterward user ip))))
      1682  
1297                                (login-page switch it afterward))   1683 (def failed-login (switch msg afterward)
1298                            (do (create-acct user pw)   1684   (if (acons afterward)
1299                                (= (logins* user) (req 'ip))   1685       (flink (fn ignore (login-page switch msg afterward)))
1300                                (prcookie (cook-user user))      
1301                                (prn)   1686       (do (prn)
1302                                (afterward user (req 'ip))))))      
1303             (pwfields "create account"))))))   1687           (login-page switch msg afterward))))
1304      1688  
1305 (def prcookie (cook) = 1689 (def prcookie (cook)
1306   (prn "Set-Cookie: user=" cook "; expires=Sun, 17-Jan-2038 19:14:07 GMT"))   1690   (prn "Set-Cookie: user=" cook "; expires=Sun, 17-Jan-2038 19:14:07 GMT"))
1307     1691  
1308 (def pwfields ((o label "login"))   1692 (def pwfields ((o label "login"))
1309   (inputs u username 20 nil   1693   (inputs u username 20 nil
1310           p password 20 nil)   1694           p password 20 nil)
1311   (br)   1695   (br)
1312   (submit label))   1696   (submit label))
1313     1697  
1314 (= good-logins* (queue) bad-logins* (queue))   1698 (= good-logins* (queue) bad-logins* (queue))
1315     1699  
1316 (def good-login (user pw ip)   1700 (def good-login (user pw ip)
1317   (let record (list (seconds) ip user pw) <> 1701   (let record (list (seconds) ip user)
1318     (if (and user pw (aand (shash pw) (is it (hpasswords* user)))) = 1702     (if (and user pw (aand (shash pw) (is it (hpasswords* user))))
1319         (do (unless (user->cookie* user) (cook-user user))   1703         (do (unless (user->cookie* user) (cook-user user))
1320             (enq-limit record good-logins*)   1704             (enq-limit record good-logins*)
1321             user)   1705             user)
1322         (do (enq-limit record bad-logins*)   1706         (do (enq-limit record bad-logins*)
1323             nil))))   1707             nil))))
1324     1708  
1325 ; can remove this once sha1 installed on pi +-    
1326        
1327 ; Create a file in case people have quote chars in their pws.  I can't  = 1709 ; Create a file in case people have quote chars in their pws.  I can't 
1328 ; believe there's no way to just send the chars.   1710 ; believe there's no way to just send the chars.
1329     1711  
1330 (def shash (str)   1712 (def shash (str)
1331   (let fname (+ "/tmp/shash" (rand-string 10))   1713   (let fname (+ "/tmp/shash" (rand-string 10))
1332     (w/outfile f fname (disp str f))   1714     (w/outfile f fname (disp str f))
1333     (let res (tostring (system (+ "openssl dgst -sha1 <" fname)))   1715     (let res (tostring (system (+ "openssl dgst -sha1 <" fname)))
1334       (do1 (subseq res 0 (- (len res) 1)) <> 1716       (do1 (cut res 0 (- (len res) 1))
1335            (rmfile fname))))) = 1717            (rmfile fname)))))
1336     1718  
    -+ 1719 (= dc-usernames* (table))
      1720  
      1721 (def username-taken (user)
      1722   (when (empty dc-usernames*)
      1723     (each (k v) hpasswords*
      1724       (set (dc-usernames* (downcase k)))))
      1725   (dc-usernames* (downcase user)))
      1726  
1337 (def bad-newacct (user pw) = 1727 (def bad-newacct (user pw)
1338   (if (no (goodname user 2 15))   1728   (if (no (goodname user 2 15))
1339        "Usernames can only contain letters, digits, dashes and    1729        "Usernames can only contain letters, digits, dashes and 
1340         underscores, and should be between 2 and 15 characters long.     1730         underscores, and should be between 2 and 15 characters long.  
1341         Please choose another."   1731         Please choose another."
1342       (let dcuser (downcase user) <> 1732       (username-taken user)
1343         (some [is dcuser (downcase _)] (keys hpasswords*)))      
1344        "That username is taken. Please choose another." = 1733        "That username is taken. Please choose another."
1345       (or (no pw) (< (len pw) 4))   1734       (or (no pw) (< (len pw) 4))
1346        "Passwords should be a least 4 characters long.  Please    1735        "Passwords should be a least 4 characters long.  Please 
1347         choose another."   1736         choose another."
1348        nil))   1737        nil))
1349     1738  
1350 (def goodname (str (o min 1) (o max nil))   1739 (def goodname (str (o min 1) (o max nil))
1351   (and (isa str 'string)   1740   (and (isa str 'string)
1352        (>= (len str) min)   1741        (>= (len str) min)
1353        (~find (fn (c) (no (or (alphadig c) (in c #\- #\_))))   1742        (~find (fn (c) (no (or (alphadig c) (in c #\- #\_))))
1354               str)   1743               str)
1355        (isnt (str 0) #\-)   1744        (isnt (str 0) #\-)
1356        (or (no max) (<= (len str) max))   1745        (or (no max) (<= (len str) max))
1357        str))   1746        str))
1358     1747  
1359   +-    
1360 (defop logout req = 1748 (defop logout req
1361   (aif (get-user req)   1749   (aif (get-user req)
1362        (do (logout-user it)   1750        (do (logout-user it)
1363            (pr "Logged out."))   1751            (pr "Logged out."))
1364        (pr "You were not logged in.")))   1752        (pr "You were not logged in.")))
1365     1753  
1366 (defop whoami req   1754 (defop whoami req
1367   (aif (get-user req)   1755   (aif (get-user req)
1368        (prs it 'at (req 'ip)) <> 1756        (prs it 'at req!ip)
1369        (do (pr "You are not logged in. ") = 1757        (do (pr "You are not logged in. ")
1370            (w/link (login-page 'both) (pr "Log in"))   1758            (w/link (login-page 'both) (pr "Log in"))
1371            (pr "."))))   1759            (pr "."))))
1372     1760  
1373     1761  
1374   <>    
1375 (= formwid* 60 bigformwid* 80 numwid* 8 formatdoc-url* nil)   1762 (= formwid* 60 bigformwid* 80 numwid* 16 formatdoc-url* nil)
1376   = 1763  
1377 ; Eventually figure out a way to separate type name from format of    1764 ; Eventually figure out a way to separate type name from format of 
1378 ; input field, instead of having e.g. toks and bigtoks   1765 ; input field, instead of having e.g. toks and bigtoks
1379     1766  
1380 (def varfield (typ id val)   1767 (def varfield (typ id val)
1381   (if (in typ 'string 'string1 'url)   1768   (if (in typ 'string 'string1 'url)
1382        (gentag input type 'text name id value val size formwid*)   1769        (gentag input type 'text name id value val size formwid*)
1383       (in typ 'num 'int 'posint) <> 1770       (in typ 'num 'int 'posint 'sym)
1384        (gentag input type 'text name id value val size numwid*) = 1771        (gentag input type 'text name id value val size numwid*)
1385       (in typ 'users 'toks)   1772       (in typ 'users 'toks)
1386        (gentag input type 'text name id value (tostring (apply prs val))   1773        (gentag input type 'text name id value (tostring (apply prs val))
1387                      size formwid*)       1774                      size formwid*)    
1388       (is typ 'sexpr)   1775       (is typ 'sexpr)
1389        (gentag input type 'text name id    1776        (gentag input type 'text name id 
1390                      value (tostring (map [do (write _) (sp)] val))   1777                      value (tostring (map [do (write _) (sp)] val))
1391                      size formwid*)   1778                      size formwid*)
1392       (in typ 'syms 'text 'doc 'mdtext 'mdtext2 'lines 'bigtoks)   1779       (in typ 'syms 'text 'doc 'mdtext 'mdtext2 'lines 'bigtoks)
1393        (let text (if (in typ 'syms 'bigtoks)   1780        (let text (if (in typ 'syms 'bigtoks)
1394                       (tostring (apply prs val))   1781                       (tostring (apply prs val))
    -+ 1782                      (is typ 'lines)
      1783                       (tostring (apply pr (intersperse #\newline val)))
1395                      (in typ 'mdtext 'mdtext2) = 1784                      (in typ 'mdtext 'mdtext2)
1396                       (unmarkdown val)   1785                       (unmarkdown val)
1397                      (no val)   1786                      (no val)
1398                       ""   1787                       ""
1399                      val)   1788                      val)
1400          (tag (textarea cols (if (is typ 'doc) bigformwid* formwid*)    1789          (tag (textarea cols (if (is typ 'doc) bigformwid* formwid*) 
1401                         rows (needrows text formwid* 4)   1790                         rows (needrows text formwid* 4)
1402                         wrap 'virtual    1791                         wrap 'virtual 
1403                         style (if (is typ 'doc) "font-size:8.5pt")   1792                         style (if (is typ 'doc) "font-size:8.5pt")
1404                         name id)   1793                         name id)
1405            (prn) ; needed or 1 initial newline gets chopped off   1794            (prn) ; needed or 1 initial newline gets chopped off
1406            (pr text))   1795            (pr text))
1407          (when (and formatdoc-url* (in typ 'mdtext 'mdtext2))   1796          (when (and formatdoc-url* (in typ 'mdtext 'mdtext2))
1408            (pr " ")   1797            (pr " ")
1409            (tag (font size -2)   1798            (tag (font size -2)
1410              (link "help" formatdoc-url* (gray 175)))))   1799              (link "help" formatdoc-url* (gray 175)))))
1411       (and (acons typ) (is (car typ) 'choice)) <> 1800       (caris typ 'choice)
1412        (menu id (cddr typ) val) = 1801        (menu id (cddr typ) val)
1413       (is typ 'yesno)   1802       (is typ 'yesno)
1414        (menu id '("yes" "no") (if val "yes" "no"))   1803        (menu id '("yes" "no") (if val "yes" "no"))
1415       (is typ 'hexcol)   1804       (is typ 'hexcol)
1416        (gentag input type 'text name id value val); was (hexrep val) <> 1805        (gentag input type 'text name id value val)
      1806       (is typ 'time)
      1807        (gentag input type 'text name id value (if val (english-time val) ""))
      1808       (is typ 'date)
      1809        (gentag input type 'text name id value (if val (english-date val) ""))
1417        (err "unknown varfield type" typ))) = 1810        (err "unknown varfield type" typ)))
1418     1811  
1419 (def text-rows (text wid (o pad 3))   1812 (def text-rows (text wid (o pad 3))
1420   (+ (truncate (/ (len text) (* wid .8))) pad)) <> 1813   (+ (trunc (/ (len text) (* wid .8))) pad))
1421   = 1814  
1422 (def needrows (text cols (o pad 0))   1815 (def needrows (text cols (o pad 0))
1423   (+ pad (max (+ 1 (count #\newline text))   1816   (+ pad (max (+ 1 (count #\newline text))
1424               (roundup (/ (len text) (- cols 5))))))   1817               (roundup (/ (len text) (- cols 5))))))
1425     1818  
1426 (def varline (typ id val) <> 1819 (def varline (typ id val (o liveurls))
1427   (if (in typ 'users 'syms 'toks 'bigtoks)  (apply prs val) = 1820   (if (in typ 'users 'syms 'toks 'bigtoks)  (apply prs val)
1428       (is typ 'lines)                       (map prn val)   1821       (is typ 'lines)                       (map prn val)
1429       (is typ 'yesno)                       (pr (if val 'yes 'no))   1822       (is typ 'yesno)                       (pr (if val 'yes 'no))
1430       (is typ 'choice)                      (varline (cadr typ) nil val) <> 1823       (caris typ 'choice)                   (varline (cadr typ) nil val)
      1824       (is typ 'url)                         (if (and liveurls (valid-url val))
      1825                                                 (link val val)
      1826                                                 (pr val))
1431       (text-type typ)                       (pr (or val "")) = 1827       (text-type typ)                       (pr (or val ""))
1432                                             (pr val)))   1828                                             (pr val)))
1433     1829  
1434 (def text-type (typ) (in typ 'string 'string1 'url 'text 'mdtext 'mdtext2))   1830 (def text-type (typ) (in typ 'string 'string1 'url 'text 'mdtext 'mdtext2))
1435     1831  
1436 ; Newlines in forms come back as /r/n.  Only want the /ns. Currently   1832 ; Newlines in forms come back as /r/n.  Only want the /ns. Currently
1437 ; remove the /rs in individual cases below.  Could do it in aform or   1833 ; remove the /rs in individual cases below.  Could do it in aform or
1438 ; even in the parsing of http requests, in the server.   1834 ; even in the parsing of http requests, in the server.
1439     1835  
1440 ; Need the calls to striptags so that news users can't get html   1836 ; Need the calls to striptags so that news users can't get html
1441 ; into a title or comment by editing it.  If want a form that    1837 ; into a title or comment by editing it.  If want a form that 
1442 ; can take html, just create another typ for it.   1838 ; can take html, just create another typ for it.
1443     1839  
1444 (def readvar (typ str (o fail nil))   1840 (def readvar (typ str (o fail nil))
1445   (case (carif typ)   1841   (case (carif typ)
1446     string  (striptags str)   1842     string  (striptags str)
1447     string1 (if (is str "") fail (striptags str)) <> 1843     string1 (if (blank str) fail (striptags str))
1448     url     (if (is str "") str (valid-url str) (striptags str) fail)   1844     url     (if (blank str) "" (valid-url str) (clean-url str) fail)
1449     num     (let n (saferead str) (if (number n) n fail)) = 1845     num     (let n (saferead str) (if (number n) n fail))
1450     int     (let n (saferead str)   1846     int     (let n (saferead str)
1451               (if (number n) (round n) fail))   1847               (if (number n) (round n) fail))
1452     posint  (let n (saferead str)   1848     posint  (let n (saferead str)
1453               (if (and (number n) (> n 0)) (round n) fail))   1849               (if (and (number n) (> n 0)) (round n) fail))
1454     text    (striptags str)   1850     text    (striptags str)
1455     doc     (striptags str)   1851     doc     (striptags str)
1456     mdtext  (md-from-form str)   1852     mdtext  (md-from-form str)
1457     mdtext2 (md-from-form str t)                      ; for md with no links   1853     mdtext2 (md-from-form str t)                      ; for md with no links
1458  ;  sym     (aif (tokens str) (sym (car it)) fail) <> 1854     sym     (or (sym:car:tokens str) fail)
1459  ;  syms    (map sym (tokens str))   1855     syms    (map sym (tokens str))
1460     sexpr   (errsafe (readall str)) = 1856     sexpr   (errsafe (readall str))
1461     users   (rem [no (goodname _)] (tokens str))   1857     users   (rem [no (goodname _)] (tokens str))
1462     toks    (tokens str)   1858     toks    (tokens str)
1463     bigtoks (tokens str)   1859     bigtoks (tokens str)
1464  ;  lines   (or (splitlines (= sss str)) fail) <> 1860     lines   (lines str)
1465     choice  (readvar (cadr typ) str) = 1861     choice  (readvar (cadr typ) str)
1466     yesno   (is str "yes")   1862     yesno   (is str "yes")
1467     hexcol  (if (hex>color str) str fail) ; was (or (hex>color str) fail) <> 1863     hexcol  (if (hex>color str) str fail)
      1864     time    (or (errsafe (parse-time str)) fail)
      1865     date    (or (errsafe (parse-date str)) fail)
1468             (err "unknown readvar type" typ))) = 1866             (err "unknown readvar type" typ)))
1469     1867  
    <> 1868 ; dates should be tagged date, and just redefine <
1470 (def splitlines (str)   1869  
      1870 (def varcompare (typ)
      1871   (if (in typ 'syms 'sexpr 'users 'toks 'bigtoks 'lines 'hexcol)
      1872        (fn (x y) (> (len x) (len y)))
      1873       (is typ 'date)
      1874        (fn (x y)
      1875          (or (no y) (and x (date< x y))))
      1876        (fn (x y)
      1877          (or (empty y) (and (~empty x) (< x y))))))
1471   (map [rem #\return _] (split (cons #\newline "") str)))   1878  
1472   = 1879  
1473 (= fail* (uniq)) <> 1880 ; (= fail* (uniq))
      1881  
      1882 (def fail* ()) ; coudn't possibly come back from a form
1474    = 1883   
1475 ; Takes a list of fields of the form (type label value view modify) and    1884 ; Takes a list of fields of the form (type label value view modify) and 
1476 ; a fn f and generates a form such that when submitted (f label newval)    1885 ; a fn f and generates a form such that when submitted (f label newval) 
1477 ; will be called for each valid value.  Finally done is called.   1886 ; will be called for each valid value.  Finally done is called.
1478     1887  
1479 (def vars-form (user fields f done (o button "update") (o lasts))   1888 (def vars-form (user fields f done (o button "update") (o lasts))
1480   (timed-aform lasts <> 1889   (taform lasts
      1890           (if (all [no (_ 4)] fields)
      1891               (fn (req))
1481                (fn (req)   1892               (fn (req)
1482                  (when-usermatch user req   1893                 (when-umatch user req
1483                    (each (k v) (req 'args)   1894                   (each (k v) req!args
1484                      (let name (sym k)   1895                     (let name (sym k)
1485                        (awhen (find [is (cadr _) name] fields)   1896                       (awhen (find [is (cadr _) name] fields)
      1897                         ; added sho to fix bug
1486                          (let (typ id val mod) it   1898                         (let (typ id val sho mod) it
1487                            (when (and mod v)   1899                           (when (and mod v)
1488                              (let newval (readvar typ v fail*)   1900                             (let newval (readvar typ v fail*)
1489                                (unless (is newval fail*)   1901                               (unless (is newval fail*)
1490                                  (f name newval))))))))   1902                                 (f name newval))))))))
1491                    (done)))   1903                   (done))))
1492      (tab = 1904      (tab
1493        (showvars fields))   1905        (showvars fields))
1494      (unless (all [no (_ 4)] fields)  ; no modifiable fields   1906      (unless (all [no (_ 4)] fields)  ; no modifiable fields
1495        (br)   1907        (br)
1496        (submit button))))   1908        (submit button))))
1497                    1909                 
1498 (def showvars (fields) <> 1910 (def showvars (fields (o liveurls))
1499   (each (typ id val view mod question) fields = 1911   (each (typ id val view mod question) fields
1500     (when view   1912     (when view
1501       (when question   1913       (when question
1502         (tr (td (prn question))))   1914         (tr (td (prn question))))
1503       (tr (unless question (tag (td valign 'top)  (pr id ":")))   1915       (tr (unless question (tag (td valign 'top)  (pr id ":")))
    <> 1916           (td (if mod 
      1917                   (varfield typ id val)
1504           (td ((if mod varfield varline) typ id val)))   1918                   (varline  typ id val liveurls))))
1505       (prn)))) = 1919       (prn))))
1506     1920  
1507 ; http://daringfireball.net/projects/markdown/syntax   1921 ; http://daringfireball.net/projects/markdown/syntax
1508     1922  
1509 (def md-from-form (str (o nolinks))   1923 (def md-from-form (str (o nolinks))
1510   (markdown (trim (rem #\return (esc<>& str)) 'end) 60 nolinks)) <> 1924   (markdown (trim (rem #\return (esc-tags str)) 'end) 60 nolinks))
1511   = 1925  
1512 (def markdown (s (o maxurl) (o nolinks))   1926 (def markdown (s (o maxurl) (o nolinks))
1513   (let ital nil   1927   (let ital nil
1514     (tostring   1928     (tostring
1515       (forlen i s   1929       (forlen i s
1516         (iflet (newi spaces) (indented-code s i (if (is i 0) 2 0))   1930         (iflet (newi spaces) (indented-code s i (if (is i 0) 2 0))
1517                (do (pr  "<p><pre><code>")   1931                (do (pr  "<p><pre><code>")
1518                  (let cb (code-block s (- newi spaces 1))   1932                  (let cb (code-block s (- newi spaces 1))
1519                    (pr cb)   1933                    (pr cb)
1520                    (= i (+ (- newi spaces 1) (len cb))))   1934                    (= i (+ (- newi spaces 1) (len cb))))
1521                  (pr "</code></pre>"))   1935                  (pr "</code></pre>"))
1522                (iflet newi (parabreak s i (if (is i 0) 1 0))   1936                (iflet newi (parabreak s i (if (is i 0) 1 0))
1523                       (do (unless (is i 0) (pr "<p>"))   1937                       (do (unless (is i 0) (pr "<p>"))
1524                           (= i (- newi 1)))   1938                           (= i (- newi 1)))
1525                       (and (is (s i) #\*)   1939                       (and (is (s i) #\*)
1526                            (or ital    1940                            (or ital 
1527                                (atend i s)    1941                                (atend i s) 
1528                                (and (~whitec (s (+ i 1)))   1942                                (and (~whitec (s (+ i 1)))
1529                                     (pos #\* s (+ i 1)))))   1943                                     (pos #\* s (+ i 1)))))
1530                        (do (pr (if ital "</i>" "<i>"))   1944                        (do (pr (if ital "</i>" "<i>"))
1531                            (= ital (no ital)))   1945                            (= ital (no ital)))
1532                       (and (no nolinks)   1946                       (and (no nolinks)
1533                            (t! gotthere) +-    
1534                            (or (litmatch "http://" s i)  = 1947                            (or (litmatch "http://" s i) 
1535                                (litmatch "https://" s i)))   1948                                (litmatch "https://" s i)))
1536                        (withs (n   (urlend s i)   1949                        (withs (n   (urlend s i)
1537                                url (subseq s i n)) <> 1950                                url (clean-url (cut s i n)))
1538                          (tag (a href url rel 'nofollow) = 1951                          (tag (a href url rel 'nofollow)
1539                            (pr (if (no maxurl) url (ellipsize url maxurl))))   1952                            (pr (if (no maxurl) url (ellipsize url maxurl))))
1540                          (= i (- n 1)))   1953                          (= i (- n 1)))
1541                        (writec (s i))))))))   1954                        (writec (s i))))))))
1542     1955  
1543 (def indented-code (s i (o newlines 0) (o spaces 0))   1956 (def indented-code (s i (o newlines 0) (o spaces 0))
1544   (let c (s i)   1957   (let c (s i)
1545     (if (nonwhite c)   1958     (if (nonwhite c)
1546          (if (and (> newlines 1) (> spaces 1))   1959          (if (and (> newlines 1) (> spaces 1))
1547              (list i spaces)   1960              (list i spaces)
1548              nil)   1961              nil)
1549         (atend i s)   1962         (atend i s)
1550          nil   1963          nil
1551         (is c #\newline)   1964         (is c #\newline)
1552          (indented-code s (+ i 1) (+ newlines 1) 0)   1965          (indented-code s (+ i 1) (+ newlines 1) 0)
1553          (indented-code s (+ i 1) newlines       (+ spaces 1)))))   1966          (indented-code s (+ i 1) newlines       (+ spaces 1)))))
1554     1967  
    -+ 1968 ; If i is start a paragraph break, returns index of start of next para.
      1969  
1555 (def parabreak (s i (o newlines 0)) = 1970 (def parabreak (s i (o newlines 0))
1556   (let c (s i)   1971   (let c (s i)
1557     (if (or (nonwhite c) (atend i s))   1972     (if (or (nonwhite c) (atend i s))
1558         (if (> newlines 1) i nil)   1973         (if (> newlines 1) i nil)
1559         (parabreak s (+ i 1) (+ newlines (if (is c #\newline) 1 0))))))   1974         (parabreak s (+ i 1) (+ newlines (if (is c #\newline) 1 0))))))
1560             <> 1975  
      1976 ; Returns the indices of the next paragraph break in s, if any.
1561   = 1977  
    -+ 1978 (def next-parabreak (s i)
      1979   (unless (atend i s)
      1980     (aif (parabreak s i) 
      1981          (list i it)
      1982          (next-parabreak s (+ i 1)))))
      1983  
      1984 (def paras (s (o i 0))
      1985   (if (atend i s)
      1986       nil
      1987       (iflet (endthis startnext) (next-parabreak s i)
      1988              (cons (cut s i endthis)
      1989                    (paras s startnext))
      1990              (list (trim (cut s i) 'end)))))
      1991  
      1992  
1562 ; Returns the index of the first char not part of the url beginning = 1993 ; Returns the index of the first char not part of the url beginning
1563 ; at i, or len of string if url goes all the way to the end.   1994 ; at i, or len of string if url goes all the way to the end.
1564     1995  
1565 ; Note that > immediately after a url (http://foo.com>) will cause   1996 ; Note that > immediately after a url (http://foo.com>) will cause
1566 ; an odd result, because the > gets escaped to something beginning   1997 ; an odd result, because the > gets escaped to something beginning
1567 ; with &, which is treated as part of the url.  Perhaps the answer   1998 ; with &, which is treated as part of the url.  Perhaps the answer
1568 ; is just to esc<>& after markdown instead of before. <> 1999 ; is just to esc-tags after markdown instead of before.
1569   = 2000  
    <> 2001 ; Treats a delimiter as part of a url if it is (a) an open delimiter
      2002 ; not followed by whitespace or eos, or (b) a close delimiter 
      2003 ; balancing a previous open delimiter.
      2004  
1570 (def urlend (s i)   2005 (def urlend (s i (o indelim))
1571   (let c (s i) = 2006   (let c (s i)
1572     (if (atend i s)   2007     (if (atend i s)
1573          (if ((orf punc delimc whitec) c) i (+ i 1)) <> 2008          (if ((orf punc whitec opendelim) c)
      2009               i 
      2010              (closedelim c)
      2011               (if indelim (+ i 1) i)
      2012              (+ i 1))
1574         (if (or (whitec c) = 2013         (if (or (whitec c)
    <> 2014                 (and (punc c) (whitec (s (+ i 1))))
      2015                 (and ((orf whitec punc) (s (+ i 1)))
1575                 (delimc c)    2016                      (or (opendelim c)
1576                 (and (punc c)      
1577                      ((orf whitec delimc) (s (+ i 1)))))   2017                          (and (closedelim c) (no indelim)))))
1578             i = 2018             i
1579             (urlend s (+ i 1)))))) <> 2019             (urlend s (+ i 1) (or (opendelim c)
1580     2020                                   (and indelim (no (closedelim c)))))))))
1581 (def delimc (c)   2021  
1582   (in c #\( #\) #\[ #\] #\{ #\} #\"))   2022 (def opendelim (c)  (in c #\< #\( #\[ #\{))
      2023  
      2024 (def closedelim (c) (in c #\> #\) #\] #\}))
1583   = 2025  
1584     2026  
1585 (def code-block (s i)   2027 (def code-block (s i)
1586   (tostring   2028   (tostring
1587     (until (let left (- (len s) i 1)   2029     (until (let left (- (len s) i 1)
1588              (or (is left 0)   2030              (or (is left 0)
1589                  (and (> left 2)   2031                  (and (> left 2)
1590                       (is (s (+ i 1)) #\newline)   2032                       (is (s (+ i 1)) #\newline)
1591                       (nonwhite (s (+ i 2))))))   2033                       (nonwhite (s (+ i 2))))))
1592      (writec (s (++ i))))))   2034      (writec (s (++ i))))))
1593     2035  
1594 (def unmarkdown (s)   2036 (def unmarkdown (s)
1595   (tostring   2037   (tostring
1596     (forlen i s   2038     (forlen i s
1597       (if (litmatch "<p>" s i)   2039       (if (litmatch "<p>" s i)
1598            (do (++ i 2)    2040            (do (++ i 2) 
1599                (unless (is i 2) (pr "\n\n")))   2041                (unless (is i 2) (pr "\n\n")))
1600           (litmatch "<i>" s i)   2042           (litmatch "<i>" s i)
1601            (do (++ i 2) (pr #\*))   2043            (do (++ i 2) (pr #\*))
1602           (litmatch "</i>" s i)   2044           (litmatch "</i>" s i)
1603            (do (++ i 3) (pr #\*))   2045            (do (++ i 3) (pr #\*))
1604           (litmatch "<a href=" s i)   2046           (litmatch "<a href=" s i)
1605            (let endurl (posmatch [in _ #\> #\space] s (+ i 9))   2047            (let endurl (posmatch [in _ #\> #\space] s (+ i 9))
1606              (if endurl   2048              (if endurl
1607                  (do (pr (subseq s (+ i 9) (- endurl 1))) <> 2049                  (do (pr (cut s (+ i 9) (- endurl 1)))
1608                      (= i (aif (posmatch "</a>" s endurl) = 2050                      (= i (aif (posmatch "</a>" s endurl)
1609                                (+ it 3)   2051                                (+ it 3)
1610                                endurl)))   2052                                endurl)))
1611                  (writec (s i))))   2053                  (writec (s i))))
1612           (litmatch "<pre><code>" s i)   2054           (litmatch "<pre><code>" s i)
1613            (awhen (findsubseq "</code></pre>" s (+ i 12))   2055            (awhen (findsubseq "</code></pre>" s (+ i 12))
1614              (pr (subseq s (+ i 11) it)) <> 2056              (pr (cut s (+ i 11) it))
1615              (= i (+ it 12))) = 2057              (= i (+ it 12)))
1616           (litmatch "<pre><code>" s i) +-    
1617            (awhen (findsubseq "</code></pre>" s (+ i 12))      
1618              (pr (subseq s (+ i 11) it))      
1619              (= i (+ it 12)))      
1620           (writec (s i)))))) = 2058           (writec (s i))))))
1621     2059  
    -+ 2060  
      2061 (def english-time (min)
      2062   (let n (mod min 720)
      2063     (string (let h (trunc (/ n 60)) (if (is h 0) "12" h))
      2064             ":"
      2065             (let m (mod n 60)
      2066               (if (is m 0) "00"
      2067                   (< m 10) (string "0" m)
      2068                            m))
      2069             (if (is min 0)   " midnight"
      2070                 (is min 720) " noon"
      2071                 (>= min 720) " pm"
      2072                              " am"))))
      2073  
      2074 (def parse-time (s)
      2075   (let (nums (o label "")) (halve s letter)
      2076     (with ((h (o m 0)) (map int (tokens nums ~digit))
      2077            cleanlabel  (downcase (rem ~alphadig label)))
      2078       (+ (* (if (is h 12)
      2079                  (if (in cleanlabel "am" "midnight")
      2080                      0
      2081                      12)
      2082                 (is cleanlabel "am")
      2083                  h
      2084                  (+ h 12))
      2085             60)
      2086           m))))
      2087  
      2088  
      2089 (= months* '("January" "February" "March" "April" "May" "June" "July"
      2090              "August" "September" "October" "November" "December"))
      2091  
      2092 (def english-date ((y m d))
      2093   (string d " " (months* (- m 1)) " " y))
      2094  
      2095 (= month-names* (obj "january"    1  "jan"        1
      2096                      "february"   2  "feb"        2
      2097                      "march"      3  "mar"        3
      2098                      "april"      4  "apr"        4
      2099                      "may"        5
      2100                      "june"       6  "jun"        6
      2101                      "july"       7  "jul"        7
      2102                      "august"     8  "aug"        8
      2103                      "september"  9  "sept"       9  "sep"      9
      2104                      "october"   10  "oct"       10
      2105                      "november"  11  "nov"       11
      2106                      "december"  12  "dec"       12))
      2107  
      2108 (def monthnum (s) (month-names* (downcase s)))
      2109  
      2110 ; Doesn't work for BC dates.
      2111  
      2112 (def parse-date (s)
      2113   (let nums (date-nums s)
      2114     (if (valid-date nums)
      2115         nums
      2116         (err (string "Invalid date: " s)))))
      2117  
      2118 (def date-nums (s)
      2119   (with ((ynow mnow dnow) (date)
      2120          toks             (tokens s ~alphadig))
      2121     (if (all [all digit _] toks)
      2122          (let nums (map int toks)
      2123            (case (len nums)
      2124              1 (list ynow mnow (car nums))
      2125              2 (iflet d (find [> _ 12] nums)
      2126                         (list ynow (find [isnt _ d] nums) d)
      2127                         (cons ynow nums))
      2128                (if (> (car nums) 31)
      2129                    (firstn 3 nums)
      2130                    (rev (firstn 3 nums)))))
      2131         ([all digit _] (car toks))
      2132          (withs ((ds ms ys) toks
      2133                  d          (int ds))
      2134            (aif (monthnum ms)
      2135                 (list (or (errsafe (int ys)) ynow) 
      2136                       it
      2137                       d)
      2138                 nil))
      2139         (monthnum (car toks))
      2140          (let (ms ds ys) toks
      2141            (aif (errsafe (int ds))
      2142                 (list (or (errsafe (int ys)) ynow) 
      2143                       (monthnum (car toks))
      2144                       it)
      2145                 nil))
      2146           nil)))
      2147  
      2148 ; To be correct needs to know days per month, and about leap years
      2149  
      2150 (def valid-date ((y m d))
      2151   (and y m d
      2152        (< 0 m 13)
      2153        (< 0 d 32)))
1622   = 2154  
1623 (mac defopl (name parm . body)   2155 (mac defopl (name parm . body)
1624   `(defop ,name ,parm   2156   `(defop ,name ,parm
1625      (if (get-user ,parm)   2157      (if (get-user ,parm)
1626          (do ,@body)    2158          (do ,@body) 
1627          (login-page 'both   2159          (login-page 'both
1628                      "You need to be logged in to do that."   2160                      "You need to be logged in to do that."
1629                      (list (fn (u ip))   2161                      (list (fn (u ip))
1630                            (string ',name (reassemble-args ,parm)))))))   2162                            (string ',name (reassemble-args ,parm)))))))
1631     2163  
1632     2164  
1633 ./arc.arc   2165 ./arc.arc
1634 ; Main Arc lib.  Ported to Scheme version Jul 06.   2166 ; Main Arc lib.  Ported to Scheme version Jul 06.
1635     2167  
1636 ; optimize ~foo in functional position in ac, like compose <> 2168 ; don't like names of conswhen and consif
      2169  
1637 ; rename: string, into-string (shorter).  could call intos string,   2170 ; need better way of generating strings; too many calls to string
1638 but then what to call string?   2171 maybe strings with escape char for evaluation
      2172 ; make foo~bar equiv of foo:~bar (in expand-ssyntax)
      2173 ; add sigs of ops defined in ac.scm
1639 ; get hold of error types within arc = 2174 ; get hold of error types within arc
1640 ; why is macex defined in scheme instead of using def below? <> 2175 ; does macex have to be defined in scheme instead of using def below?
1641 ; write disp, read, write in arc = 2176 ; write disp, read, write in arc
1642 ; could prob write rmfile and dir in terms of system <>    
1643 ; could I get all of macros up into lib.arc?   2177 ; could I get all of macros up into arc.arc?
1644     2178 ; warn when shadow a global name
1645 ; any logical reason I can't say (push x (if foo y z)) ?   2179 ; some simple regexp/parsing plan
1646 ;   eval would have to always ret 2 things, the val and where it came from      
1647 ; idea: implicit tables of tables; setf empty field, becomes table      
1648 ;   or should setf on a table just take n args?      
1649 ; idea: permanent objs that live on disk and are updated when modified      
1650   = 2180  
1651 ; compromises in this implementation:    2181 ; compromises in this implementation: 
1652 ; no objs in code   2182 ; no objs in code
1653 ;  (mac testlit args (listtab args)) breaks when called   2183 ;  (mac testlit args (listtab args)) breaks when called
1654 ; separate string type   2184 ; separate string type
1655 ;  (= (cdr (cdr str)) "foo") couldn't work because no way to get str tail   2185 ;  (= (cdr (cdr str)) "foo") couldn't work because no way to get str tail
    -+ 2186 ;  not sure this is a mistake; strings may be subtly different from 
      2187 ;  lists of chars
1656   = 2188  
1657     2189  
1658 (set do (annotate 'mac <> 2190 (assign do (annotate 'mac
1659           (fn args `((fn () ,@args)))))   2191              (fn args `((fn () ,@args)))))
1660   = 2192  
1661 (set safeset (annotate 'mac <> 2193 (assign safeset (annotate 'mac
1662                (fn (var val)   2194                   (fn (var val)
1663                  `(do (if (bound ',var)   2195                     `(do (if (bound ',var)
1664                           (do (disp "*** redefining ")   2196                              (do (disp "*** redefining " (stderr))
1665                               (disp ',var)   2197                                  (disp ',var (stderr))
1666                               (writec #\newline)))   2198                                  (disp #\newline (stderr))))
1667                       (set ,var ,val)))))   2199                          (assign ,var ,val)))))
1668   = 2200  
1669 (set def (annotate 'mac <> 2201 (assign def (annotate 'mac
1670             (fn (name parms . body)   2202                (fn (name parms . body)
1671               `(do (sref sig ',parms ',name)   2203                  `(do (sref sig ',parms ',name)
1672                    (safeset ,name (fn ,parms ,@body))))))   2204                       (safeset ,name (fn ,parms ,@body))))))
1673   = 2205  
1674 (def caar (xs) (car (car xs)))   2206 (def caar (xs) (car (car xs)))
1675 (def cadr (xs) (car (cdr xs)))   2207 (def cadr (xs) (car (cdr xs)))
1676 (def cddr (xs) (cdr (cdr xs)))   2208 (def cddr (xs) (cdr (cdr xs)))
1677     2209  
1678 (def no (x) (is x nil))   2210 (def no (x) (is x nil))
1679     2211  
1680 (def acons (x) (is (type x) 'cons))   2212 (def acons (x) (is (type x) 'cons))
1681     2213  
1682 (def atom (x) (no (acons x)))   2214 (def atom (x) (no (acons x)))
1683     2215  
    <> 2216 ; Can return to this def once Rtm gets ac to make all rest args
      2217 ; nil-terminated lists.
      2218  
1684 (def list args args)   2219 ; (def list args args)
1685   = 2220  
    -+ 2221 (def copylist (xs)
      2222   (if (no xs) 
      2223       nil 
      2224       (cons (car xs) (copylist (cdr xs)))))
      2225  
      2226 (def list args (copylist args))
      2227  
1686 (def idfn (x) x) = 2228 (def idfn (x) x)
1687     2229  
1688 ; Maybe later make this internal. <> 2230 ; Maybe later make this internal.  Useful to let xs be a fn?
1689   = 2231  
1690 (def map1 (f xs)   2232 (def map1 (f xs)
1691   (if (no xs)    2233   (if (no xs) 
1692       nil   2234       nil
1693       (cons (f (car xs)) (map1 f (cdr xs)))))   2235       (cons (f (car xs)) (map1 f (cdr xs)))))
1694     2236  
1695 (def pair (xs (o f list))   2237 (def pair (xs (o f list))
1696   (if (no xs)   2238   (if (no xs)
1697        nil   2239        nil
1698       (no (cdr xs))   2240       (no (cdr xs))
1699        (list (list (car xs)))   2241        (list (list (car xs)))
1700       (cons (f (car xs) (cadr xs))   2242       (cons (f (car xs) (cadr xs))
1701             (pair (cddr xs) f))))   2243             (pair (cddr xs) f))))
1702     2244  
1703 (set mac (annotate 'mac <> 2245 (assign mac (annotate 'mac
1704            (fn (name parms . body)   2246               (fn (name parms . body)
1705              `(do (sref sig ',parms ',name)   2247                 `(do (sref sig ',parms ',name)
1706                   (safeset ,name (annotate 'mac (fn ,parms ,@body)))))))   2248                      (safeset ,name (annotate 'mac (fn ,parms ,@body)))))))
1707   = 2249  
1708 (mac and args   2250 (mac and args
1709   (if args   2251   (if args
1710       (if (cdr args)   2252       (if (cdr args)
1711           `(if ,(car args) (and ,@(cdr args)))   2253           `(if ,(car args) (and ,@(cdr args)))
1712           (car args))   2254           (car args))
1713       't))   2255       't))
1714     2256  
1715 (def assoc (key al)   2257 (def assoc (key al)
1716   (if (atom al)   2258   (if (atom al)
1717        nil   2259        nil
1718       (and (acons (car al)) (is (caar al) key))   2260       (and (acons (car al)) (is (caar al) key))
1719        (car al)   2261        (car al)
1720       (assoc key (cdr al))))   2262       (assoc key (cdr al))))
1721     2263  
1722 (def alref (al key) (cadr (assoc key al)))   2264 (def alref (al key) (cadr (assoc key al)))
1723     2265  
1724 (mac with (parms . body)   2266 (mac with (parms . body)
1725   `((fn ,(map1 car (pair parms))   2267   `((fn ,(map1 car (pair parms))
1726      ,@body)   2268      ,@body)
1727     ,@(map1 cadr (pair parms))))   2269     ,@(map1 cadr (pair parms))))
1728     2270  
1729 (mac let (var val . body)   2271 (mac let (var val . body)
1730   `(with (,var ,val) ,@body))   2272   `(with (,var ,val) ,@body))
1731     2273  
1732 (mac withs (parms . body)   2274 (mac withs (parms . body)
1733   (if (no parms)    2275   (if (no parms) 
1734       `(do ,@body)   2276       `(do ,@body)
1735       `(let ,(car parms) ,(cadr parms)    2277       `(let ,(car parms) ,(cadr parms) 
1736          (withs ,(cddr parms) ,@body))))   2278          (withs ,(cddr parms) ,@body))))
1737     2279  
1738 ; Rtm prefers to overload + to do this   2280 ; Rtm prefers to overload + to do this
1739     2281  
1740 (def join args   2282 (def join args
1741   (if (no args)   2283   (if (no args)
1742       nil   2284       nil
1743       (let a (car args)    2285       (let a (car args) 
1744         (if (no a)    2286         (if (no a) 
1745             (apply join (cdr args))   2287             (apply join (cdr args))
1746             (cons (car a) (apply join (cdr a) (cdr args)))))))   2288             (cons (car a) (apply join (cdr a) (cdr args)))))))
1747     2289  
1748 ; Need rfn for use in macro expansions.   2290 ; Need rfn for use in macro expansions.
1749     2291  
1750 (mac rfn (name parms . body)   2292 (mac rfn (name parms . body)
1751   `(let ,name nil   2293   `(let ,name nil
1752      (set ,name (fn ,parms ,@body)))) <> 2294      (assign ,name (fn ,parms ,@body))))
1753   = 2295  
1754 (mac afn (parms . body)   2296 (mac afn (parms . body)
1755   `(let self nil   2297   `(let self nil
1756      (set self (fn ,parms ,@body)))) <> 2298      (assign self (fn ,parms ,@body))))
1757   = 2299  
1758 ; Ac expands x:y:z into (compose x y z), ~x into (complement x)   2300 ; Ac expands x:y:z into (compose x y z), ~x into (complement x)
1759     2301  
1760 ; Only used when the call to compose doesn't occur in functional position.     2302 ; Only used when the call to compose doesn't occur in functional position.  
1761 ; Composes in functional position are transformed away by ac.   2303 ; Composes in functional position are transformed away by ac.
1762     2304  
1763 (mac compose args   2305 (mac compose args
1764   (let g (uniq)   2306   (let g (uniq)
1765     `(fn ,g   2307     `(fn ,g
1766        ,((afn (fs)   2308        ,((afn (fs)
1767            (if (cdr fs)   2309            (if (cdr fs)
1768                (list (car fs) (self (cdr fs)))   2310                (list (car fs) (self (cdr fs)))
1769                `(apply ,(if (car fs) (car fs) 'idfn) ,g)))   2311                `(apply ,(if (car fs) (car fs) 'idfn) ,g)))
1770          args))))   2312          args))))
    -+ 2313  
      2314 ; Ditto: complement in functional position optimized by ac.
1771   = 2315  
1772 (mac complement (f)   2316 (mac complement (f)
1773   (let g (uniq)   2317   (let g (uniq)
1774     `(fn ,g (no (apply ,f ,g)))))   2318     `(fn ,g (no (apply ,f ,g)))))
1775     2319  
1776 (def rev (xs)    2320 (def rev (xs) 
1777   ((afn (xs acc)   2321   ((afn (xs acc)
1778      (if (no xs)   2322      (if (no xs)
1779          acc   2323          acc
1780          (self (cdr xs) (cons (car xs) acc))))   2324          (self (cdr xs) (cons (car xs) acc))))
1781    xs nil))   2325    xs nil))
1782     2326  
1783 (def isnt (x y) (no (is x y)))   2327 (def isnt (x y) (no (is x y)))
1784     2328  
1785 (mac w/uniq (names . body)   2329 (mac w/uniq (names . body)
1786   (if (acons names)   2330   (if (acons names)
1787       `(with ,(apply + nil (map1 (fn (n) (list n '(uniq)))   2331       `(with ,(apply + nil (map1 (fn (n) (list n '(uniq)))
1788                              names))   2332                              names))
1789          ,@body)   2333          ,@body)
1790       `(let ,names (uniq) ,@body)))   2334       `(let ,names (uniq) ,@body)))
1791     2335  
1792 (mac or args   2336 (mac or args
1793   (and args   2337   (and args
1794        (w/uniq g   2338        (w/uniq g
1795          `(let ,g ,(car args)   2339          `(let ,g ,(car args)
1796             (if ,g ,g (or ,@(cdr args)))))))   2340             (if ,g ,g (or ,@(cdr args)))))))
1797     2341  
1798 (def alist (x) (or (no x) (is (type x) 'cons)))   2342 (def alist (x) (or (no x) (is (type x) 'cons)))
1799     2343  
1800 (mac in (x . choices)   2344 (mac in (x . choices)
1801   (w/uniq g   2345   (w/uniq g
1802     `(let ,g ,x   2346     `(let ,g ,x
1803        (or ,@(map1 (fn (c) `(is ,g ,c)) choices)))))   2347        (or ,@(map1 (fn (c) `(is ,g ,c)) choices)))))
1804     2348  
1805 ; should take n args <> 2349 ; Could take n args, but have never once needed that.
1806   = 2350  
1807 (def iso (x y)   2351 (def iso (x y)
1808   (or (is x y)   2352   (or (is x y)
1809       (and (acons x)    2353       (and (acons x) 
1810            (acons y)    2354            (acons y) 
1811            (iso (car x) (car y))    2355            (iso (car x) (car y)) 
1812            (iso (cdr x) (cdr y)))))   2356            (iso (cdr x) (cdr y)))))
1813     2357  
1814 (mac when (test . body)   2358 (mac when (test . body)
1815   `(if ,test (do ,@body)))   2359   `(if ,test (do ,@body)))
1816     2360  
1817 (mac unless (test . body)   2361 (mac unless (test . body)
1818   `(if (no ,test) (do ,@body)))   2362   `(if (no ,test) (do ,@body)))
1819     2363  
1820 (mac while (test . body)   2364 (mac while (test . body)
1821   (w/uniq (gf gp)   2365   (w/uniq (gf gp)
1822     `((rfn ,gf (,gp)   2366     `((rfn ,gf (,gp)
1823         (when ,gp ,@body (,gf ,test)))   2367         (when ,gp ,@body (,gf ,test)))
1824       ,test)))   2368       ,test)))
1825     2369  
1826 (def empty (seq)    2370 (def empty (seq) 
1827   (or (no seq)    2371   (or (no seq) 
1828       (and (no (acons seq)) (is (len seq) 0)))) <> 2372       (and (or (is (type seq) 'string) (is (type seq) 'table))
      2373            (is (len seq) 0))))
1829   = 2374  
1830 (def reclist (f xs)   2375 (def reclist (f xs)
1831   (and xs (or (f xs) (reclist f (cdr xs)))))   2376   (and xs (or (f xs) (reclist f (cdr xs)))))
1832     2377  
1833 (def recstring (test s (o start 0))   2378 (def recstring (test s (o start 0))
1834   (let n (len s) <>    
1835     ((afn (i)   2379   ((afn (i)
1836        (and (< i (len s))   2380      (and (< i (len s))
1837             (or (test i)   2381           (or (test i)
1838                 (self (+ i 1)))))   2382               (self (+ i 1)))))
1839      start)))   2383    start))
1840   = 2384  
1841 (def testify (x)   2385 (def testify (x)
1842   (if (isa x 'fn) x [is _ x]))   2386   (if (isa x 'fn) x [is _ x]))
    -+ 2387  
      2388 ; Like keep, seems like some shouldn't testify.  But find should,
      2389 ; and all probably should.
1843   = 2390  
1844 (def some (test seq)   2391 (def some (test seq)
1845   (let f (testify test)   2392   (let f (testify test)
1846     (if (alist seq)   2393     (if (alist seq)
1847         (reclist f:car seq)   2394         (reclist f:car seq)
1848         (recstring f:seq seq))))   2395         (recstring f:seq seq))))
1849     2396  
1850 (def all (test seq)    2397 (def all (test seq) 
1851   (~some (complement (testify test)) seq))   2398   (~some (complement (testify test)) seq))
1852           2399        
1853 (def mem (test seq)   2400 (def mem (test seq)
1854   (let f (testify test)   2401   (let f (testify test)
1855     (reclist [if (f:car _) _] seq)))   2402     (reclist [if (f:car _) _] seq)))
1856     2403  
1857 (def find (test seq)   2404 (def find (test seq)
1858   (let f (testify test)   2405   (let f (testify test)
1859     (if (alist seq)   2406     (if (alist seq)
1860         (reclist   [if (f:car _) (car _)] seq)   2407         (reclist   [if (f:car _) (car _)] seq)
1861         (recstring [if (f:seq _) (seq _)] seq))))   2408         (recstring [if (f:seq _) (seq _)] seq))))
1862     2409  
1863 (def isa (x y) (is (type x) y))   2410 (def isa (x y) (is (type x) y))
1864     2411  
1865 ; Possible to write map without map1, but makes News 3x slower.   2412 ; Possible to write map without map1, but makes News 3x slower.
1866     2413  
1867 ;(def map (f . seqs)   2414 ;(def map (f . seqs)
1868 ;  (if (some1 no seqs)   2415 ;  (if (some1 no seqs)
1869 ;       nil   2416 ;       nil
1870 ;      (no (cdr seqs))   2417 ;      (no (cdr seqs))
1871 ;       (let s1 (car seqs)   2418 ;       (let s1 (car seqs)
1872 ;         (cons (f (car s1))   2419 ;         (cons (f (car s1))
1873 ;               (map f (cdr s1))))   2420 ;               (map f (cdr s1))))
1874 ;      (cons (apply f (map car seqs))   2421 ;      (cons (apply f (map car seqs))
1875 ;            (apply map f (map cdr seqs)))))   2422 ;            (apply map f (map cdr seqs)))))
1876     2423  
1877     2424  
1878 (def map (f . seqs)   2425 (def map (f . seqs)
1879   (if (some [isa _ 'string] seqs)    2426   (if (some [isa _ 'string] seqs) 
1880        (withs (n   (apply min (map len seqs))   2427        (withs (n   (apply min (map len seqs))
1881                new (newstring n))   2428                new (newstring n))
1882          ((afn (i)   2429          ((afn (i)
1883             (if (is i n)   2430             (if (is i n)
1884                 new   2431                 new
1885                 (do (sref new (apply f (map [_ i] seqs)) i)   2432                 (do (sref new (apply f (map [_ i] seqs)) i)
1886                     (self (+ i 1)))))   2433                     (self (+ i 1)))))
1887           0))   2434           0))
1888       (no (cdr seqs))    2435       (no (cdr seqs)) 
1889        (map1 f (car seqs))   2436        (map1 f (car seqs))
1890       ((afn (seqs)   2437       ((afn (seqs)
1891         (if (some no seqs)     2438         (if (some no seqs)  
1892             nil   2439             nil
1893             (cons (apply f (map1 car seqs))   2440             (cons (apply f (map1 car seqs))
1894                   (self (map1 cdr seqs)))))   2441                   (self (map1 cdr seqs)))))
1895        seqs)))   2442        seqs)))
1896     2443  
1897 (def mappend (f . args)   2444 (def mappend (f . args)
1898   (apply + nil (apply map f args)))   2445   (apply + nil (apply map f args)))
1899     2446  
1900 (def firstn (n xs)   2447 (def firstn (n xs)
1901   (if (and (> n 0) xs) <> 2448   (if (no n)            xs
1902       (cons (car xs) (firstn (- n 1) (cdr xs)))   2449       (and (> n 0) xs)  (cons (car xs) (firstn (- n 1) (cdr xs)))
1903       nil))   2450                         nil))
1904   = 2451  
1905 (def nthcdr (n xs)   2452 (def nthcdr (n xs)
1906   (if (> n 0) <> 2453   (if (no n)  xs
1907       (nthcdr (- n 1) (cdr xs))   2454       (> n 0) (nthcdr (- n 1) (cdr xs))
1908       xs))   2455               xs))
1909   = 2456  
1910 ; Generalization of pair: (tuples x) = (pair x)   2457 ; Generalization of pair: (tuples x) = (pair x)
1911     2458  
1912 (def tuples (xs (o n 2))   2459 (def tuples (xs (o n 2))
1913   (if (no xs)   2460   (if (no xs)
1914       nil   2461       nil
1915       (cons (firstn n xs)   2462       (cons (firstn n xs)
1916             (tuples (nthcdr n xs) n))))   2463             (tuples (nthcdr n xs) n))))
1917     2464  
    <> 2465 ; If ok to do with =, why not with def?  But see if use it.
      2466  
      2467 (mac defs args
      2468   `(do ,@(map [cons 'def _] (tuples args 3))))
      2469  
      2470 (def caris (x val) 
1918 (def caris (x val) (and (acons x) (is (car x) val)))   2471   (and (acons x) (is (car x) val)))
1919   = 2472  
1920 (def warn (msg . args)   2473 (def warn (msg . args)
1921   (disp (+ "Warning: " msg ". "))   2474   (disp (+ "Warning: " msg ". "))
1922   (map [do (write _) (disp " ")] args)   2475   (map [do (write _) (disp " ")] args)
1923   (disp #\newline))   2476   (disp #\newline))
1924     2477  
1925 (mac atomic body   2478 (mac atomic body
1926   `(atomic-invoke (fn () ,@body)))   2479   `(atomic-invoke (fn () ,@body)))
1927     2480  
1928 (mac atlet args   2481 (mac atlet args
1929   `(atomic (let ,@args)))   2482   `(atomic (let ,@args)))
1930      2483   
1931 (mac atwith args   2484 (mac atwith args
1932   `(atomic (with ,@args)))   2485   `(atomic (with ,@args)))
1933     2486  
1934 (mac atwiths args   2487 (mac atwiths args
1935   `(atomic (withs ,@args)))   2488   `(atomic (withs ,@args)))
1936     2489  
    -+ 2490  
1937 ; setforms returns (vars get set) for a place based on car of an expr = 2491 ; setforms returns (vars get set) for a place based on car of an expr
1938 ;  vars is a list of gensyms alternating with expressions whose vals they   2492 ;  vars is a list of gensyms alternating with expressions whose vals they
1939 ;   should be bound to, suitable for use as first arg to withs   2493 ;   should be bound to, suitable for use as first arg to withs
1940 ;  get is an expression returning the current value in the place   2494 ;  get is an expression returning the current value in the place
1941 ;  set is an expression representing a function of one argument   2495 ;  set is an expression representing a function of one argument
1942 ;   that stores a new value in the place   2496 ;   that stores a new value in the place
1943     2497  
1944 ; A bit gross that it works based on the *name* in the car, but maybe   2498 ; A bit gross that it works based on the *name* in the car, but maybe
1945 ; wrong to worry.  Macros live in expression land.   2499 ; wrong to worry.  Macros live in expression land.
1946     2500  
1947 ; seems meaningful to e.g. (push 1 (pop x)) if (car x) is a cons.   2501 ; seems meaningful to e.g. (push 1 (pop x)) if (car x) is a cons.
1948 ; can't in cl though.  could I define a setter for push or pop?   2502 ; can't in cl though.  could I define a setter for push or pop?
1949     2503  
1950 (set setter (table)) <> 2504 (assign setter (table))
1951   = 2505  
1952 (mac defset (name parms . body)   2506 (mac defset (name parms . body)
1953   (w/uniq gexpr   2507   (w/uniq gexpr
1954     `(sref setter    2508     `(sref setter 
1955            (fn (,gexpr)   2509            (fn (,gexpr)
1956              (let ,parms (cdr ,gexpr)   2510              (let ,parms (cdr ,gexpr)
1957                ,@body))   2511                ,@body))
1958            ',name)))   2512            ',name)))
1959     2513  
1960 (defset car (x)   2514 (defset car (x)
1961   (w/uniq g   2515   (w/uniq g
1962     (list (list g x)   2516     (list (list g x)
1963           `(car ,g)   2517           `(car ,g)
1964           `(fn (val) (scar ,g val)))))   2518           `(fn (val) (scar ,g val)))))
1965     2519  
1966 (defset cdr (x)   2520 (defset cdr (x)
1967   (w/uniq g   2521   (w/uniq g
1968     (list (list g x)   2522     (list (list g x)
1969           `(cdr ,g)   2523           `(cdr ,g)
1970           `(fn (val) (scdr ,g val)))))   2524           `(fn (val) (scdr ,g val)))))
1971     2525  
1972 (defset caar (x)   2526 (defset caar (x)
1973   (w/uniq g   2527   (w/uniq g
1974     (list (list g x)   2528     (list (list g x)
1975           `(caar ,g)   2529           `(caar ,g)
1976           `(fn (val) (scar (car ,g) val)))))   2530           `(fn (val) (scar (car ,g) val)))))
1977     2531  
1978 (defset cadr (x)   2532 (defset cadr (x)
1979   (w/uniq g   2533   (w/uniq g
1980     (list (list g x)   2534     (list (list g x)
1981           `(cadr ,g)   2535           `(cadr ,g)
1982           `(fn (val) (scar (cdr ,g) val)))))   2536           `(fn (val) (scar (cdr ,g) val)))))
1983     2537  
1984 (defset cddr (x)   2538 (defset cddr (x)
1985   (w/uniq g   2539   (w/uniq g
1986     (list (list g x)   2540     (list (list g x)
1987           `(cddr ,g)   2541           `(cddr ,g)
1988           `(fn (val) (scdr (cdr ,g) val)))))   2542           `(fn (val) (scdr (cdr ,g) val)))))
1989     2543  
1990 ; Note: if expr0 macroexpands into any expression whose car doesn't   2544 ; Note: if expr0 macroexpands into any expression whose car doesn't
1991 ; have a setter, setforms assumes it's a data structure in functional    2545 ; have a setter, setforms assumes it's a data structure in functional 
1992 ; position.  Such bugs will be seen only when the code is executed, when    2546 ; position.  Such bugs will be seen only when the code is executed, when 
1993 ; sref complains it can't set a reference to a function.   2547 ; sref complains it can't set a reference to a function.
1994     2548  
1995 (def setforms (expr0)   2549 (def setforms (expr0)
1996   (let expr (macex expr0)   2550   (let expr (macex expr0)
1997     (if (isa expr 'sym)   2551     (if (isa expr 'sym)
    <> 2552          (if (ssyntax expr)
      2553              (setforms (ssexpand expr))
1998          (w/uniq (g h)   2554              (w/uniq (g h)
1999            (list (list g expr)    2555                (list (list g expr)
2000                  g   2556                      g
2001                  `(fn (,h) (set ,expr ,h))))   2557                      `(fn (,h) (assign ,expr ,h)))))
2002         ; make it also work for uncompressed calls to compose = 2558         ; make it also work for uncompressed calls to compose
2003         (and (acons expr) (metafn (car expr)))   2559         (and (acons expr) (metafn (car expr)))
2004          (setforms (expand-metafn-call (ssexpand (car expr)) (cdr expr)))   2560          (setforms (expand-metafn-call (ssexpand (car expr)) (cdr expr)))
    -+ 2561         (and (acons expr) (acons (car expr)) (is (caar expr) 'get))
      2562          (setforms (list (cadr expr) (cadr (car expr))))
2005          (let f (setter (car expr)) = 2563          (let f (setter (car expr))
2006            (if f   2564            (if f
2007                (f expr)   2565                (f expr)
2008                ; assumed to be data structure in fn position   2566                ; assumed to be data structure in fn position
2009                (do (when (caris (car expr) 'fn)   2567                (do (when (caris (car expr) 'fn)
2010                      (warn "Inverting what looks like a function call"  <> 2568                      (warn "Inverting what looks like a function call"
2011                            expr0 expr)) = 2569                            expr0 expr))
2012                    (w/uniq (g h)   2570                    (w/uniq (g h)
2013                      (let argsyms (map [uniq] (cdr expr))   2571                      (let argsyms (map [uniq] (cdr expr))
2014                         (list (+ (list g (car expr))   2572                         (list (+ (list g (car expr))
2015                                  (mappend list argsyms (cdr expr)))   2573                                  (mappend list argsyms (cdr expr)))
2016                               `(,g ,@argsyms)   2574                               `(,g ,@argsyms)
2017                               `(fn (,h) (sref ,g ,h ,@argsyms))))))))))) <> 2575                               `(fn (,h) (sref ,g ,h ,(car argsyms))))))))))))
2018   = 2576  
2019 (def metafn (x)   2577 (def metafn (x)
2020   (or (ssyntax x)   2578   (or (ssyntax x)
2021       (and (acons x) (in (car x) 'compose 'complement))))   2579       (and (acons x) (in (car x) 'compose 'complement))))
2022     2580  
2023 (def expand-metafn-call (f args)   2581 (def expand-metafn-call (f args)
2024   (if (is (car f) 'compose)   2582   (if (is (car f) 'compose)
2025       ((afn (fs) <> 2583        ((afn (fs)
2026          (if (caris (car fs) 'compose)            ; nested compose   2584           (if (caris (car fs) 'compose)            ; nested compose
2027               (self (join (cdr (car fs)) (cdr fs)))   2585                (self (join (cdr (car fs)) (cdr fs)))
2028              (cdr fs)   2586               (cdr fs)
2029               (list (car fs) (self (cdr fs)))   2587                (list (car fs) (self (cdr fs)))
2030              (cons (car fs) args)))   2588               (cons (car fs) args)))
2031        (cdr f))   2589         (cdr f))
      2590       (is (car f) 'no)
2032       (err "Can't invert " (cons f args))))   2591        (err "Can't invert " (cons f args))
      2592        (cons f args)))
2033   = 2593  
2034 (def expand= (place val)   2594 (def expand= (place val)
2035   (if (isa place 'sym) <> 2595   (if (and (isa place 'sym) (~ssyntax place))
2036       `(set ,place ,val)   2596       `(assign ,place ,val)
2037       (let (vars prev setter) (setforms place) = 2597       (let (vars prev setter) (setforms place)
2038         (w/uniq g   2598         (w/uniq g
2039           `(atwith ,(+ vars (list g val))   2599           `(atwith ,(+ vars (list g val))
2040              (,setter ,g))))))   2600              (,setter ,g))))))
2041     2601  
2042 (def expand=list (terms)   2602 (def expand=list (terms)
2043   `(do ,@(map (fn ((p v)) (expand= p v))  ; [apply expand= _]   2603   `(do ,@(map (fn ((p v)) (expand= p v))  ; [apply expand= _]
2044                   (pair terms))))   2604                   (pair terms))))
2045     2605  
2046 (mac = args   2606 (mac = args
2047   (expand=list args))   2607   (expand=list args))
2048     2608  
2049 (mac loop (start test update . body)   2609 (mac loop (start test update . body)
2050   (w/uniq (gfn gparm)   2610   (w/uniq (gfn gparm)
2051     `(do ,start   2611     `(do ,start
2052          ((rfn ,gfn (,gparm)    2612          ((rfn ,gfn (,gparm) 
2053             (if ,gparm   2613             (if ,gparm
2054                 (do ,@body ,update (,gfn ,test))))   2614                 (do ,@body ,update (,gfn ,test))))
2055           ,test))))   2615           ,test))))
2056     2616  
2057 (mac for (v init max . body)   2617 (mac for (v init max . body)
2058   (w/uniq (gi gm)   2618   (w/uniq (gi gm)
2059     `(with (,v nil ,gi ,init ,gm (+ ,max 1))   2619     `(with (,v nil ,gi ,init ,gm (+ ,max 1))
2060        (loop (set ,v ,gi) (< ,v ,gm) (set ,v (+ ,v 1)) <> 2620        (loop (assign ,v ,gi) (< ,v ,gm) (assign ,v (+ ,v 1))
2061          ,@body)))) = 2621          ,@body))))
2062     2622  
    -+ 2623 (mac down (v init min . body)
      2624   (w/uniq (gi gm)
      2625     `(with (,v nil ,gi ,init ,gm (- ,min 1))
      2626        (loop (assign ,v ,gi) (> ,v ,gm) (assign ,v (- ,v 1))
      2627          ,@body))))
      2628  
2063 (mac repeat (n . body) = 2629 (mac repeat (n . body)
2064   `(for ,(uniq) 1 ,n ,@body))   2630   `(for ,(uniq) 1 ,n ,@body))
2065     2631  
2066 ; could bind index instead of gensym   2632 ; could bind index instead of gensym
2067     2633  
2068 (mac each (var expr . body)   2634 (mac each (var expr . body)
2069   (w/uniq (gseq g) <> 2635   (w/uniq (gseq gf gv)
2070     `(let ,gseq ,expr = 2636     `(let ,gseq ,expr
2071        (if (alist ,gseq)   2637        (if (alist ,gseq)
2072             ((afn (,g) <> 2638             ((rfn ,gf (,gv)
2073                (when (acons ,g)   2639                (when (acons ,gv)
2074                  (let ,var (car ,g) ,@body)   2640                  (let ,var (car ,gv) ,@body)
2075                  (self (cdr ,g))))   2641                  (,gf (cdr ,gv))))
2076              ,gseq) = 2642              ,gseq)
2077            (isa ,gseq 'table)   2643            (isa ,gseq 'table)
2078             (maptable (fn (,g ,var) ,@body) <> 2644             (maptable (fn ,var ,@body)
2079                       ,gseq) = 2645                       ,gseq)
2080             (for ,g 0 (- (len ,gseq) 1) <> 2646             (for ,gv 0 (- (len ,gseq) 1)
2081               (let ,var (,gseq ,g) ,@body))))))   2647               (let ,var (,gseq ,gv) ,@body))))))
2082   = 2648  
2083 ; (nthcdr x y) = (subseq y x). <> 2649 ; (nthcdr x y) = (cut y x).
2084   = 2650  
2085 (def subseq (seq start (o end (len seq))) <> 2651 (def cut (seq start (o end))
      2652   (let end (if (no end)   (len seq)
      2653                (< end 0)  (+ (len seq) end) 
      2654                           end)
2086   (if (isa seq 'string)   2655     (if (isa seq 'string)
2087       (let s2 (newstring (- end start))   2656         (let s2 (newstring (- end start))
2088         (for i 0 (- end start 1)   2657           (for i 0 (- end start 1)
2089           (= (s2 i) (seq (+ start i))))   2658             (= (s2 i) (seq (+ start i))))
2090         s2)   2659           s2)
2091       (firstn (- end start) (nthcdr start seq))))   2660         (firstn (- end start) (nthcdr start seq)))))
2092        = 2661       
2093 (mac ontable (k v h . body) +-    
2094   `(maptable (fn (,k ,v) ,@body) ,h))      
2095        
2096 (mac whilet (var test . body) = 2662 (mac whilet (var test . body)
2097   (w/uniq (gf gp)   2663   (w/uniq (gf gp)
2098     `((rfn ,gf (,gp)   2664     `((rfn ,gf (,gp)
2099         (let ,var ,gp   2665         (let ,var ,gp
2100           (when ,var ,@body (,gf ,test))))   2666           (when ,var ,@body (,gf ,test))))
2101       ,test)))   2667       ,test)))
2102     2668  
2103 (def last (seq) <> 2669 (def last (xs)
2104   (if (no (cdr seq))   2670   (if (cdr xs)
2105       (car seq)   2671       (last (cdr xs))
2106       (last (cdr seq))))   2672       (car xs)))
2107   = 2673  
2108 (def rem (test seq)   2674 (def rem (test seq)
2109   (let f (testify test)   2675   (let f (testify test)
2110     (if (alist seq)   2676     (if (alist seq)
2111         ((afn (s)   2677         ((afn (s)
2112            (if (no s)       nil   2678            (if (no s)       nil
2113                (f (car s))  (self (cdr s))   2679                (f (car s))  (self (cdr s))
2114                             (cons (car s) (self (cdr s)))))   2680                             (cons (car s) (self (cdr s)))))
2115           seq)   2681           seq)
2116         (coerce (rem test (coerce seq 'cons)) 'string))))   2682         (coerce (rem test (coerce seq 'cons)) 'string))))
2117     2683  
    -+ 2684 ; Seems like keep doesn't need to testify-- would be better to
      2685 ; be able to use tables as fns.  But rem does need to, because
      2686 ; often want to rem a table from a list.  So maybe the right answer
      2687 ; is to make keep the more primitive, not rem.
      2688  
2118 (def keep (test seq)  = 2689 (def keep (test seq) 
2119   (rem (complement (testify test)) seq))   2690   (rem (complement (testify test)) seq))
2120     2691  
    <> 2692 ;(def trues (f seq) 
2121 (def trues (f seq) (rem nil (map f seq)))   2693 ;  (rem nil (map f seq)))
      2694  
      2695 (def trues (f xs)
      2696   (and xs
      2697       (let fx (f (car xs))
      2698         (if fx
      2699             (cons fx (trues f (cdr xs)))
      2700             (trues f (cdr xs))))))
2122   = 2701  
2123 (mac do1 args   2702 (mac do1 args
2124   (w/uniq g   2703   (w/uniq g
2125     `(let ,g ,(car args)   2704     `(let ,g ,(car args)
2126        ,@(cdr args)   2705        ,@(cdr args)
2127        ,g)))   2706        ,g)))
2128     2707  
2129 ; Would like to write a faster case based on table generated by a macro,   2708 ; Would like to write a faster case based on table generated by a macro,
2130 ; but can't insert objects into expansions in Mzscheme.   2709 ; but can't insert objects into expansions in Mzscheme.
2131     2710  
2132 (mac caselet (var expr . args)   2711 (mac caselet (var expr . args)
2133   (let ex (afn (args)   2712   (let ex (afn (args)
2134             (if (no (cdr args))    2713             (if (no (cdr args)) 
2135                 (car args)   2714                 (car args)
2136                 `(if (is ,var ',(car args))   2715                 `(if (is ,var ',(car args))
2137                      ,(cadr args)   2716                      ,(cadr args)
2138                      ,(self (cddr args)))))   2717                      ,(self (cddr args)))))
2139     `(let ,var ,expr ,(ex args))))   2718     `(let ,var ,expr ,(ex args))))
2140     2719  
2141 (mac case (expr . args)   2720 (mac case (expr . args)
2142   `(caselet ,(uniq) ,expr ,@args))   2721   `(caselet ,(uniq) ,expr ,@args))
2143     2722  
2144 (mac push (x place)   2723 (mac push (x place)
2145   (w/uniq gx   2724   (w/uniq gx
2146     (let (binds val setter) (setforms place)   2725     (let (binds val setter) (setforms place)
2147       `(let ,gx ,x   2726       `(let ,gx ,x
2148          (atwiths ,binds   2727          (atwiths ,binds
2149            (,setter (cons ,gx ,val)))))))   2728            (,setter (cons ,gx ,val)))))))
2150     2729  
2151 (mac swap (place1 place2)   2730 (mac swap (place1 place2)
2152   (w/uniq (g1 g2)   2731   (w/uniq (g1 g2)
2153     (with ((binds1 val1 setter1) (setforms place1)   2732     (with ((binds1 val1 setter1) (setforms place1)
2154            (binds2 val2 setter2) (setforms place2))   2733            (binds2 val2 setter2) (setforms place2))
2155       `(atwiths ,(+ binds1 (list g1 val1) binds2 (list g2 val2))   2734       `(atwiths ,(+ binds1 (list g1 val1) binds2 (list g2 val2))
2156          (,setter1 ,g2)   2735          (,setter1 ,g2)
2157          (,setter2 ,g1)))))   2736          (,setter2 ,g1)))))
2158     2737  
2159 (mac rotate places   2738 (mac rotate places
2160   (with (vars (map [uniq] places)   2739   (with (vars (map [uniq] places)
2161          forms (map setforms places))   2740          forms (map setforms places))
2162     `(atwiths ,(mappend (fn (g (binds val setter))   2741     `(atwiths ,(mappend (fn (g (binds val setter))
2163                           (+ binds (list g val)))   2742                           (+ binds (list g val)))
2164                         vars   2743                         vars
2165                         forms)   2744                         forms)
2166        ,@(map (fn (g (binds val setter))   2745        ,@(map (fn (g (binds val setter))
2167                 (list setter g))   2746                 (list setter g))
2168               (+ (cdr vars) (list (car vars)))   2747               (+ (cdr vars) (list (car vars)))
2169               forms))))   2748               forms))))
2170     2749  
2171 (mac pop (place)   2750 (mac pop (place)
2172   (w/uniq g   2751   (w/uniq g
2173     (let (binds val setter) (setforms place)   2752     (let (binds val setter) (setforms place)
2174       `(atwiths ,(+ binds (list g val))   2753       `(atwiths ,(+ binds (list g val))
2175          (do1 (car ,g)    2754          (do1 (car ,g) 
2176               (,setter (cdr ,g)))))))   2755               (,setter (cdr ,g)))))))
2177     2756  
2178 (def adjoin (x xs (o test iso))   2757 (def adjoin (x xs (o test iso))
2179   (if (some [test x _] xs)   2758   (if (some [test x _] xs)
2180       xs   2759       xs
2181       (cons x xs)))   2760       (cons x xs)))
2182     2761  
2183 (mac pushnew (x place . args)   2762 (mac pushnew (x place . args)
2184   (w/uniq gx   2763   (w/uniq gx
2185     (let (binds val setter) (setforms place)   2764     (let (binds val setter) (setforms place)
2186       `(atwiths ,(+ (list gx x) binds)   2765       `(atwiths ,(+ (list gx x) binds)
2187          (,setter (adjoin ,gx ,val ,@args))))))   2766          (,setter (adjoin ,gx ,val ,@args))))))
2188     2767  
2189 (mac pull (test place)   2768 (mac pull (test place)
2190   (w/uniq g   2769   (w/uniq g
2191     (let (binds val setter) (setforms place)   2770     (let (binds val setter) (setforms place)
2192       `(atwiths ,(+ (list g test) binds)   2771       `(atwiths ,(+ (list g test) binds)
2193          (,setter (rem ,g ,val))))))   2772          (,setter (rem ,g ,val))))))
2194     2773  
    -+ 2774 (mac togglemem (x place . args)
      2775   (w/uniq gx
      2776     (let (binds val setter) (setforms place)
      2777       `(atwiths ,(+ (list gx x) binds)
      2778          (,setter (if (mem ,gx ,val)
      2779                       (rem ,gx ,val)
      2780                       (adjoin ,gx ,val ,@args)))))))
      2781  
2195 (mac ++ (place (o i 1)) = 2782 (mac ++ (place (o i 1))
2196   (if (isa place 'sym)   2783   (if (isa place 'sym)
2197       `(= ,place (+ ,place ,i))   2784       `(= ,place (+ ,place ,i))
2198       (w/uniq gi   2785       (w/uniq gi
2199         (let (binds val setter) (setforms place)   2786         (let (binds val setter) (setforms place)
2200           `(atwiths ,(+ binds (list gi i))   2787           `(atwiths ,(+ binds (list gi i))
2201              (,setter (+ ,val ,gi)))))))   2788              (,setter (+ ,val ,gi)))))))
2202     2789  
2203 (mac -- (place (o i 1))   2790 (mac -- (place (o i 1))
2204   (if (isa place 'sym)   2791   (if (isa place 'sym)
2205       `(= ,place (- ,place ,i))   2792       `(= ,place (- ,place ,i))
2206       (w/uniq gi   2793       (w/uniq gi
2207         (let (binds val setter) (setforms place)   2794         (let (binds val setter) (setforms place)
2208           `(atwiths ,(+ binds (list gi i))   2795           `(atwiths ,(+ binds (list gi i))
2209              (,setter (- ,val ,gi)))))))   2796              (,setter (- ,val ,gi)))))))
2210     2797  
2211 ; E.g. (inc x) equiv to (zap + x 1) <> 2798 ; E.g. (++ x) equiv to (zap + x 1)
2212   = 2799  
2213 (mac zap (op place . args)   2800 (mac zap (op place . args)
2214   (with (gop    (uniq)   2801   (with (gop    (uniq)
2215          gargs  (map [uniq] args)   2802          gargs  (map [uniq] args)
2216          mix    (afn seqs    2803          mix    (afn seqs 
2217                   (if (some no seqs)   2804                   (if (some no seqs)
2218                       nil   2805                       nil
2219                       (+ (map car seqs)   2806                       (+ (map car seqs)
2220                          (apply self (map cdr seqs))))))   2807                          (apply self (map cdr seqs))))))
2221     (let (binds val setter) (setforms place)   2808     (let (binds val setter) (setforms place)
2222       `(atwiths ,(+ binds (list gop op) (mix gargs args))   2809       `(atwiths ,(+ binds (list gop op) (mix gargs args))
2223          (,setter (,gop ,val ,@gargs))))))   2810          (,setter (,gop ,val ,@gargs))))))
2224     2811  
2225 ; Can't simply mod pr to print strings represented as lists of chars,   2812 ; Can't simply mod pr to print strings represented as lists of chars,
2226 ; because empty string will get printed as nil.  Would need to rep strings   2813 ; because empty string will get printed as nil.  Would need to rep strings
2227 ; as lists of chars annotated with 'string, and modify car and cdr to get   2814 ; as lists of chars annotated with 'string, and modify car and cdr to get
2228 ; the rep of these.  That would also require hacking the reader.     2815 ; the rep of these.  That would also require hacking the reader.  
2229     2816  
2230 ;(def pr args +-    
2231 ;  (if (isa (car args) 'output)      
2232 ;      (do (error "stream arg!" args)      
2233 ;          (map1 [disp _ (car args)] (cdr args))      
2234 ;          (cadr args))      
2235 ;      (do (map1 disp args)      
2236 ;          (car args))))      
2237        
2238 (def pr args = 2817 (def pr args
2239   (map1 disp args)   2818   (map1 disp args)
2240   (car args))   2819   (car args))
2241     2820  
2242 ; Rtm says this version should make the server 20% faster because map1 <>    
2243 ; generates so much garbage; in fact makes slower; maybe rewrite map1?      
2244        
2245 ;(def newpr args   2821 (def prt args
2246 ;  (if (isa (car args) 'output)   2822   (map1 [if _ (disp _)] args)
2247 ;      (do (each a (cdr args) (disp a (car args)))      
2248 ;          (cadr args))   2823   (car args))
2249 ;      (do (each a args (disp a))      
2250 ;          (car args))))      
2251   = 2824  
2252 (def prn args   2825 (def prn args
2253   (do1 (apply pr args)   2826   (do1 (apply pr args)
2254        (writec #\newline <> 2827        (writec #\newline)))
2255                (if (isa (car args) 'output) (car args) (stdout)))))      
2256   = 2828  
2257 (mac nil! args <> 2829 (mac wipe args
2258   `(do ,@(map (fn (a) `(= ,a nil)) args))) = 2830   `(do ,@(map (fn (a) `(= ,a nil)) args)))
2259     2831  
2260 (mac t! args <> 2832 (mac set args
2261   `(do ,@(map (fn (a) `(= ,a t)) args))) = 2833   `(do ,@(map (fn (a) `(= ,a t)) args)))
2262     2834  
2263 ; Destructing means ambiguity: are pat vars bound in else? (no) <> 2835 ; Destructuring means ambiguity: are pat vars bound in else? (no)
2264   = 2836  
2265 (mac iflet (var expr then . rest)   2837 (mac iflet (var expr then . rest)
2266   (w/uniq gv   2838   (w/uniq gv
2267     `(let ,gv ,expr   2839     `(let ,gv ,expr
2268        (if ,gv (let ,var ,gv ,then) ,@rest))))   2840        (if ,gv (let ,var ,gv ,then) ,@rest))))
2269     2841  
2270 (mac whenlet (var expr . body)   2842 (mac whenlet (var expr . body)
2271   `(iflet ,var ,expr (do ,@body)))   2843   `(iflet ,var ,expr (do ,@body)))
2272     2844  
2273 (mac aif (expr . body)   2845 (mac aif (expr . body)
2274   `(let it ,expr (if it ,@body))) <> 2846   `(let it ,expr
      2847      (if it
      2848          ,@(if (cddr body)
      2849                `(,(car body) (aif ,@(cdr body)))
      2850                body))))
2275   = 2851  
2276 (mac awhen (expr . body)   2852 (mac awhen (expr . body)
2277   `(let it ,expr (if it (do ,@body))))   2853   `(let it ,expr (if it (do ,@body))))
2278     2854  
2279 (mac aand args   2855 (mac aand args
2280   (if (no args)   2856   (if (no args)
2281       't    2857       't 
2282       (no (cdr args))   2858       (no (cdr args))
2283        (car args)   2859        (car args)
2284       `(let it ,(car args) (and it (aand ,@(cdr args))))))   2860       `(let it ,(car args) (and it (aand ,@(cdr args))))))
2285     2861  
2286 (mac accum (accfn . body)   2862 (mac accum (accfn . body)
2287   (w/uniq gacc   2863   (w/uniq gacc
2288     `(withs (,gacc nil ,accfn [push _ ,gacc])   2864     `(withs (,gacc nil ,accfn [push _ ,gacc])
2289        ,@body   2865        ,@body
2290        ,gacc))) <> 2866        (rev ,gacc))))
2291   = 2867  
2292 ; Repeatedly evaluates its body till it returns nil, then returns vals.   2868 ; Repeatedly evaluates its body till it returns nil, then returns vals.
2293     2869  
2294 (mac drain (expr (o eof nil))   2870 (mac drain (expr (o eof nil))
2295   (w/uniq (gacc gdone gres)   2871   (w/uniq (gacc gdone gres)
2296     `(with (,gacc nil ,gdone nil)   2872     `(with (,gacc nil ,gdone nil)
2297        (while (no ,gdone)   2873        (while (no ,gdone)
2298          (let ,gres ,expr   2874          (let ,gres ,expr
2299            (if (is ,gres ,eof)   2875            (if (is ,gres ,eof)
2300                (= ,gdone t)   2876                (= ,gdone t)
2301                (push ,gres ,gacc))))   2877                (push ,gres ,gacc))))
2302        (rev ,gacc))))   2878        (rev ,gacc))))
2303     2879  
2304 ; For the common C idiom while x = snarfdata != stopval.   2880 ; For the common C idiom while x = snarfdata != stopval.
2305 ; Rename this if use it often.   2881 ; Rename this if use it often.
2306     2882  
2307 (mac whiler (var expr endval . body)   2883 (mac whiler (var expr endval . body)
2308   (w/uniq gf   2884   (w/uniq gf
2309     `((rfn ,gf (,var) <> 2885     `(withs (,var nil ,gf (testify ,endval))
2310         (when (and ,var (no (is ,var ,endval)))   2886        (while (no (,gf (= ,var ,expr)))
2311           ,@body   2887          ,@body))))
2312           (,gf ,expr)))      
2313       ,expr)))      
2314    = 2888   
2315 ;(def macex (e)   2889 ;(def macex (e)
2316 ;  (if (atom e)   2890 ;  (if (atom e)
2317 ;      e   2891 ;      e
2318 ;      (let op (and (atom (car e)) (eval (car e)))   2892 ;      (let op (and (atom (car e)) (eval (car e)))
2319 ;        (if (isa op 'mac)   2893 ;        (if (isa op 'mac)
2320 ;            (apply (rep op) (cdr e))   2894 ;            (apply (rep op) (cdr e))
2321 ;            e))))   2895 ;            e))))
2322     2896  
2323 (def consif (x y) (if x (cons x y) y))   2897 (def consif (x y) (if x (cons x y) y))
2324     2898  
2325 (def string args   2899 (def string args
2326   (apply + "" (map [coerce _ 'string] args)))   2900   (apply + "" (map [coerce _ 'string] args)))
2327     2901  
2328 (def flat (x (o stringstoo)) <> 2902 (def flat x
2329   ((rfn f (x acc)   2903   ((afn (x acc)
2330      (if (or (no x) (and stringstoo (is x "")))      
2331          acc   2904      (if (no x)   acc
2332          (and (atom x) (no (and stringstoo (isa x 'string))))      
2333           (cons x acc)   2905          (atom x) (cons x acc)
2334          (f (car x) (f (cdr x) acc))))   2906                   (self (car x) (self (cdr x) acc))))
2335    x nil)) = 2907    x nil))
2336     2908  
2337 ; Perhaps not the final idea, or at least final name <>    
2338               
2339 (mac default (x test alt)   2909 (mac check (x test (o alt))
2340   (w/uniq gx = 2910   (w/uniq gx
2341     `(let ,gx ,x   2911     `(let ,gx ,x
2342        (if (,test ,gx) ,gx ,alt))))   2912        (if (,test ,gx) ,gx ,alt))))
2343     2913  
2344 (def pos (test seq (o start 0))   2914 (def pos (test seq (o start 0))
2345   (let f (testify test)   2915   (let f (testify test)
2346     (if (alist seq)   2916     (if (alist seq)
2347         ((afn (seq n)   2917         ((afn (seq n)
2348            (if (no seq)      2918            (if (no seq)   
2349                 nil   2919                 nil
2350                (f (car seq))    2920                (f (car seq)) 
2351                 n   2921                 n
2352                (self (cdr seq) (+ n 1))))   2922                (self (cdr seq) (+ n 1))))
2353          (nthcdr start seq)    2923          (nthcdr start seq) 
2354          start)   2924          start)
2355         (recstring [if (f (seq _)) _] seq start))))   2925         (recstring [if (f (seq _)) _] seq start))))
2356     2926  
2357 (def even (n) (is (mod n 2) 0))   2927 (def even (n) (is (mod n 2) 0))
2358     2928  
2359 (def odd (n) (no (even n)))   2929 (def odd (n) (no (even n)))
2360     2930  
2361 (mac after (x . ys)   2931 (mac after (x . ys)
2362   `(protect (fn () ,x) (fn () ,@ys)))   2932   `(protect (fn () ,x) (fn () ,@ys)))
2363     2933  
2364 (let expander    2934 (let expander 
2365      (fn (f var name body)   2935      (fn (f var name body)
2366        `(let ,var (,f ,name)   2936        `(let ,var (,f ,name)
2367           (after (do ,@body) (close ,var))))   2937           (after (do ,@body) (close ,var))))
2368     2938  
2369   (mac w/infile (var name . body)   2939   (mac w/infile (var name . body)
2370     (expander 'infile var name body))   2940     (expander 'infile var name body))
2371     2941  
2372   (mac w/outfile (var name . body)   2942   (mac w/outfile (var name . body)
2373     (expander 'outfile var name body))   2943     (expander 'outfile var name body))
2374     2944  
2375   (mac w/instring (var str . body)   2945   (mac w/instring (var str . body)
2376     (expander 'instring var str body))   2946     (expander 'instring var str body))
    -+ 2947  
      2948   (mac w/socket (var port . body)
      2949     (expander 'open-socket var port body))
2377   ) = 2950   )
2378     2951  
2379 (mac w/outstring (var . body)   2952 (mac w/outstring (var . body)
2380   `(let ,var (outstring) ,@body))   2953   `(let ,var (outstring) ,@body))
    -+ 2954  
      2955 ; what happens to a file opened for append if arc is killed in
      2956 ; the middle of a write?
2381   = 2957  
2382 (mac w/appendfile (var name . body)   2958 (mac w/appendfile (var name . body)
2383   `(let ,var (outfile ,name 'append)   2959   `(let ,var (outfile ,name 'append)
2384      (after (do ,@body) (close ,var))))   2960      (after (do ,@body) (close ,var))))
2385     2961  
2386 ; rename this simply "to"?  - prob not; rarely use   2962 ; rename this simply "to"?  - prob not; rarely use
2387     2963  
2388 (mac w/stdout (str . body)   2964 (mac w/stdout (str . body)
2389   `(call-w/stdout ,str (fn () ,@body)))   2965   `(call-w/stdout ,str (fn () ,@body)))
2390     2966  
2391 (mac w/stdin (str . body)   2967 (mac w/stdin (str . body)
2392   `(call-w/stdin ,str (fn () ,@body)))   2968   `(call-w/stdin ,str (fn () ,@body)))
2393     2969  
2394 (mac tostring body   2970 (mac tostring body
2395   (w/uniq gv   2971   (w/uniq gv
2396    `(w/outstring ,gv   2972    `(w/outstring ,gv
2397       (w/stdout ,gv ,@body)   2973       (w/stdout ,gv ,@body)
2398       (inside ,gv))))   2974       (inside ,gv))))
2399     2975  
2400 (mac fromstring (str . body)   2976 (mac fromstring (str . body)
2401   (w/uniq gv   2977   (w/uniq gv
2402    `(w/instring ,gv ,str   2978    `(w/instring ,gv ,str
2403       (w/stdin ,gv ,@body))))   2979       (w/stdin ,gv ,@body))))
2404     2980  
2405 (def readstring1 (s (o eof nil)) (w/instring i s (read i eof)))   2981 (def readstring1 (s (o eof nil)) (w/instring i s (read i eof)))
2406     2982  
2407 (def read ((o x (stdin)) (o eof nil))   2983 (def read ((o x (stdin)) (o eof nil))
2408   (if (isa x 'string) (readstring1 x eof) (sread x eof)))   2984   (if (isa x 'string) (readstring1 x eof) (sread x eof)))
2409     2985  
    -+ 2986 ; inconsistency between names of readfile[1] and writefile
      2987  
2410 (def readfile (name) (w/infile s name (drain (read s)))) = 2988 (def readfile (name) (w/infile s name (drain (read s))))
2411     2989  
2412 (def readfile1 (name) (w/infile s name (read s)))   2990 (def readfile1 (name) (w/infile s name (read s)))
2413     2991  
2414 (def writefile1 (val name) (w/outfile s name (write val s)) val) +-    
2415        
2416 (def readall (src (o eof nil)) = 2992 (def readall (src (o eof nil))
2417   ((afn (i)   2993   ((afn (i)
2418     (let x (read i eof)   2994     (let x (read i eof)
2419       (if (is x eof)   2995       (if (is x eof)
2420           nil   2996           nil
2421           (cons x (self i)))))   2997           (cons x (self i)))))
2422    (if (isa src 'string) (instring src) src)))   2998    (if (isa src 'string) (instring src) src)))
2423     2999  
    -+ 3000 (def allchars (str)
      3001   (tostring (whiler c (readc str nil) no
      3002               (writec c))))
      3003  
      3004 (def filechars (name)
      3005   (w/infile s name (allchars s)))
      3006  
      3007 (def writefile (val file)
      3008   (let tmpfile (+ file ".tmp")
      3009     (w/outfile o tmpfile (write val o))
      3010     (mvfile tmpfile file))
      3011   val)
      3012  
2424 (def sym (x) (coerce x 'sym)) = 3013 (def sym (x) (coerce x 'sym))
    -+ 3014  
      3015 (def int (x (o b 10)) (coerce x 'int b))
2425   = 3016  
2426 (mac rand-choice exprs   3017 (mac rand-choice exprs
2427   `(case (rand ,(len exprs))   3018   `(case (rand ,(len exprs))
2428      ,@(let key -1    3019      ,@(let key -1 
2429          (mappend [list (++ key) _]   3020          (mappend [list (++ key) _]
2430                   exprs))))   3021                   exprs))))
2431     3022  
2432 (mac n-of (n expr)   3023 (mac n-of (n expr)
2433   (w/uniq ga   3024   (w/uniq ga
2434     `(let ,ga nil        3025     `(let ,ga nil     
2435        (repeat ,n (push ,expr ,ga))   3026        (repeat ,n (push ,expr ,ga))
2436        (rev ,ga))))   3027        (rev ,ga))))
2437     3028  
    -+ 3029 ; rejects bytes >= 248 lest digits be overrepresented
      3030  
2438 (def rand-string (n) = 3031 (def rand-string (n)
    <> 3032   (let c "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
2439   (with (cap (fn () (+ 65 (rand 26)))   3033     (with (nc 62 s (newstring n) i 0)
2440          sm  (fn () (+ 97 (rand 26)))   3034       (w/infile str "/dev/urandom"
2441          dig (fn () (+ 48 (rand 10))))   3035         (while (< i n)
2442     (coerce (map [coerce _ 'char]   3036           (let x (readb str)
      3037              (unless (> x 247)
2443                  (cons (rand-choice (cap) (sm))   3038                (= (s i) (c (mod x nc)))
2444                        (n-of (- n 1) (rand-choice (cap) (sm) (dig)))))   3039                (++ i)))))
2445             'string)))   3040       s)))
2446   = 3041  
2447 (mac forlen (var s . body)   3042 (mac forlen (var s . body)
2448   `(for ,var 0 (- (len ,s) 1) ,@body))   3043   `(for ,var 0 (- (len ,s) 1) ,@body))
2449     3044  
2450 (mac on (var s . body)   3045 (mac on (var s . body)
2451   (if (is var 'index)   3046   (if (is var 'index)
2452       (err "Can't use index as first arg to on.")   3047       (err "Can't use index as first arg to on.")
2453       (w/uniq gs   3048       (w/uniq gs
2454         `(let ,gs ,s   3049         `(let ,gs ,s
2455            (forlen index ,gs   3050            (forlen index ,gs
2456              (let ,var (,gs index)   3051              (let ,var (,gs index)
2457                ,@body))))))   3052                ,@body))))))
2458     3053  
2459 (def best (f seq)   3054 (def best (f seq)
2460   (if (no seq)   3055   (if (no seq)
2461       nil   3056       nil
2462       (let wins (car seq)   3057       (let wins (car seq)
2463         (each elt (cdr seq)   3058         (each elt (cdr seq)
2464           (if (f elt wins) (= wins elt)))   3059           (if (f elt wins) (= wins elt)))
2465         wins)))   3060         wins)))
2466                  3061               
2467 (def max args (best > args))   3062 (def max args (best > args))
2468 (def min args (best < args))   3063 (def min args (best < args))
2469     3064  
2470 ; (mac max2 (x y)   3065 ; (mac max2 (x y)
2471 ;   (w/uniq (a b)   3066 ;   (w/uniq (a b)
2472 ;     `(with (,a ,x ,b ,y) (if (> ,a ,b) ,a ,b))))   3067 ;     `(with (,a ,x ,b ,y) (if (> ,a ,b) ,a ,b))))
2473     3068  
2474 (def most (f seq)    3069 (def most (f seq) 
2475   (unless (no seq)   3070   (unless (no seq)
2476     (withs (wins (car seq) topscore (f wins))   3071     (withs (wins (car seq) topscore (f wins))
2477       (each elt (cdr seq)   3072       (each elt (cdr seq)
2478         (let score (f elt)   3073         (let score (f elt)
2479           (if (> score topscore) (= wins elt topscore score))))   3074           (if (> score topscore) (= wins elt topscore score))))
2480       wins)))   3075       wins)))
2481     3076  
2482 ; Insert so that list remains sorted.  Don't really want to expose   3077 ; Insert so that list remains sorted.  Don't really want to expose
2483 ; these but seem to have to because can't include a fn obj in a    3078 ; these but seem to have to because can't include a fn obj in a 
2484 ; macroexpansion.   3079 ; macroexpansion.
2485      3080   
2486 (def insert-sorted (test elt seq)   3081 (def insert-sorted (test elt seq)
2487   (if (no seq)   3082   (if (no seq)
2488        (list elt)    3083        (list elt) 
2489       (test elt (car seq))    3084       (test elt (car seq)) 
2490        (cons elt seq)   3085        (cons elt seq)
2491       (cons (car seq) (insert-sorted test elt (cdr seq)))))   3086       (cons (car seq) (insert-sorted test elt (cdr seq)))))
2492     3087  
2493 (mac insort (test elt seq)   3088 (mac insort (test elt seq)
2494   `(zap [insert-sorted ,test ,elt _] ,seq))   3089   `(zap [insert-sorted ,test ,elt _] ,seq))
2495     3090  
2496 (def reinsert-sorted (test elt seq)   3091 (def reinsert-sorted (test elt seq)
2497   (if (no seq)    3092   (if (no seq) 
2498        (list elt)    3093        (list elt) 
2499       (is elt (car seq))   3094       (is elt (car seq))
2500        (reinsert-sorted test elt (cdr seq))   3095        (reinsert-sorted test elt (cdr seq))
2501       (test elt (car seq))    3096       (test elt (car seq)) 
2502        (cons elt (rem elt seq))   3097        (cons elt (rem elt seq))
2503       (cons (car seq) (reinsert-sorted test elt (cdr seq)))))   3098       (cons (car seq) (reinsert-sorted test elt (cdr seq)))))
2504     3099  
2505 (mac insortnew (test elt seq)   3100 (mac insortnew (test elt seq)
2506   `(zap [reinsert-sorted ,test ,elt _] ,seq))   3101   `(zap [reinsert-sorted ,test ,elt _] ,seq))
2507     3102  
2508 ; Could make this look at the sig of f and return a fn that took the    3103 ; Could make this look at the sig of f and return a fn that took the 
2509 ; right no of args and didn't have to call apply (or list if 1 arg).   3104 ; right no of args and didn't have to call apply (or list if 1 arg).
2510     3105  
2511 (def memo (f)   3106 (def memo (f)
2512   (let cache (table) <> 3107   (with (cache (table) nilcache (table))
2513     (fn args = 3108     (fn args
2514       (or (cache args)   3109       (or (cache args)
    <> 3110           (and (no (nilcache args))
      3111                (aif (apply f args)
      3112                     (= (cache args) it)
      3113                     (do (set (nilcache args))
      3114                         nil)))))))
2515           (= (cache args) (apply f args))))))   3115  
2516   = 3116  
2517 (mac defmemo (name parms . body)   3117 (mac defmemo (name parms . body)
2518   `(safeset ,name (memo (fn ,parms ,@body))))   3118   `(safeset ,name (memo (fn ,parms ,@body))))
2519     3119  
2520 (def <= args   3120 (def <= args
2521   (or (no args)  <> 3121   (or (no args)
2522       (no (cdr args)) = 3122       (no (cdr args))
2523       (and (no (> (car args) (cadr args)))   3123       (and (no (> (car args) (cadr args)))
2524            (apply <= (cdr args)))))   3124            (apply <= (cdr args)))))
2525     3125  
2526 (def >= args   3126 (def >= args
2527   (or (no args)  <> 3127   (or (no args)
2528       (no (cdr args)) = 3128       (no (cdr args))
2529       (and (no (< (car args) (cadr args)))   3129       (and (no (< (car args) (cadr args)))
2530            (apply >= (cdr args)))))   3130            (apply >= (cdr args)))))
2531                <> 3131  
2532 (def whitec (c) = 3132 (def whitec (c)
2533   (in c #\space #\newline #\tab #\return))   3133   (in c #\space #\newline #\tab #\return))
2534     3134  
2535 (def nonwhite (c) (no (whitec c)))   3135 (def nonwhite (c) (no (whitec c)))
2536     3136  
    <> 3137 (def letter (c) (or (<= #\a c #\z) (<= #\A c #\Z)))
2537 (def alphadig (c)   3138  
      3139 (def digit (c) (<= #\0 c #\9))
2538   (or (<= #\a c #\z) (<= #\A c #\Z) (<= #\0 c #\9)))   3140  
      3141 (def alphadig (c) (or (letter c) (digit c)))
2539   = 3142  
2540 (def punc (c)   3143 (def punc (c)
2541   (in c #\. #\, #\; #\: #\! #\?))   3144   (in c #\. #\, #\; #\: #\! #\?))
2542     3145  
2543 (def readline ((o str (stdin)))   3146 (def readline ((o str (stdin)))
2544   (awhen (readc str)   3147   (awhen (readc str)
2545     (tostring    3148     (tostring 
2546       (writec it)   3149       (writec it)
2547       (whiler c (readc str) #\newline <> 3150       (whiler c (readc str) [in _ nil #\newline]
2548         (writec c))))) = 3151         (writec c)))))
2549     3152  
2550 ; Don't currently use this but suspect some code could.   3153 ; Don't currently use this but suspect some code could.
2551     3154  
2552 (mac summing (sumfn . body)   3155 (mac summing (sumfn . body)
2553   (w/uniq (gc gt)   3156   (w/uniq (gc gt)
2554     `(let ,gc 0   3157     `(let ,gc 0
2555        (let ,sumfn (fn (,gt) (if ,gt (++ ,gc)))   3158        (let ,sumfn (fn (,gt) (if ,gt (++ ,gc)))
2556          ,@body)   3159          ,@body)
2557        ,gc)))   3160        ,gc)))
2558     3161  
    <> 3162 (def sum (f xs)
      3163   (let n 0
      3164     (each x xs (++ n (f x)))
      3165     n))
      3166  
2559 (def trav (f base tree)   3167 (def treewise (f base tree)
2560   (if (atom tree) = 3168   (if (atom tree)
2561       (base tree)   3169       (base tree)
    <> 3170       (f (treewise f base (car tree)) 
2562       (f (trav f base (car tree)) (trav f base (cdr tree)))))   3171          (treewise f base (cdr tree)))))
2563   = 3172  
2564 (def carif (x) (if (atom x) x (car x)))   3173 (def carif (x) (if (atom x) x (car x)))
2565     3174  
2566 ; Could prob be generalized beyond printing.   3175 ; Could prob be generalized beyond printing.
2567     3176  
2568 (def prall (elts (o init "") (o sep ", "))   3177 (def prall (elts (o init "") (o sep ", "))
2569   (when elts   3178   (when elts
2570     (pr init (car elts))   3179     (pr init (car elts))
2571     (map [pr sep _] (cdr elts))   3180     (map [pr sep _] (cdr elts))
2572     elts))   3181     elts))
2573                 3182              
2574 (def prs args        3183 (def prs args     
2575   (prall args "" #\space))   3184   (prall args "" #\space))
2576     3185  
2577 (def tree-subst (old new tree)   3186 (def tree-subst (old new tree)
2578   (if (is tree old)   3187   (if (is tree old)
2579        new   3188        new
2580       (atom tree)   3189       (atom tree)
2581        tree   3190        tree
2582       (cons (tree-subst old new (car tree))   3191       (cons (tree-subst old new (car tree))
2583             (tree-subst old new (cdr tree)))))   3192             (tree-subst old new (cdr tree)))))
2584     3193  
2585 (def ontree (f tree)   3194 (def ontree (f tree)
2586   (f tree)   3195   (f tree)
2587   (unless (atom tree)   3196   (unless (atom tree)
2588     (ontree f (car tree))   3197     (ontree f (car tree))
2589     (ontree f (cdr tree))))   3198     (ontree f (cdr tree))))
2590     3199  
2591 (def dotted (x)   3200 (def dotted (x)
2592   (if (atom x)   3201   (if (atom x)
2593       nil   3202       nil
2594       (and (cdr x) (or (atom (cdr x))   3203       (and (cdr x) (or (atom (cdr x))
2595                        (dotted (cdr x))))))   3204                        (dotted (cdr x))))))
2596     3205  
2597 (def fill-table (table data)   3206 (def fill-table (table data)
2598   (each (k v) (pair data) (= (table k) v))   3207   (each (k v) (pair data) (= (table k) v))
2599   table)   3208   table)
2600     3209  
2601 (mac obj args +-    
2602   (w/uniq g      
2603     `(let ,g (table)      
2604        ,@(map (fn ((k v)) `(= (,g ',k) ,v))      
2605               (pair args))      
2606        ,g)))      
2607        
2608 (def keys (h)  = 3210 (def keys (h) 
2609   (accum a (ontable k v h (a k)))) <> 3211   (accum a (each (k v) h (a k))))
2610   = 3212  
2611 (def vals (h)    3213 (def vals (h) 
2612   (accum a (ontable k v h (a v)))) <> 3214   (accum a (each (k v) h (a v))))
2613   = 3215  
2614 ; These two should really be done by coerce.  Wrap coerce?   3216 ; These two should really be done by coerce.  Wrap coerce?
2615     3217  
2616 (def tablist (h)   3218 (def tablist (h)
2617   (accum a (maptable (fn args (a args)) h)))   3219   (accum a (maptable (fn args (a args)) h)))
2618     3220  
2619 (def listtab (al)   3221 (def listtab (al)
2620   (let h (table)   3222   (let h (table)
2621     (map (fn ((k v)) (= (h k) v))   3223     (map (fn ((k v)) (= (h k) v))
2622          al)   3224          al)
2623     h))   3225     h))
    -+ 3226  
      3227 (mac obj args
      3228   `(listtab (list ,@(map (fn ((k v))
      3229                            `(list ',k ,v))
      3230                          (pair args)))))
2624   = 3231  
2625 (def load-table (file (o eof))   3232 (def load-table (file (o eof))
2626   (w/infile i file (read-table i eof)))   3233   (w/infile i file (read-table i eof)))
2627     3234  
2628 (def read-table ((o i (stdin)) (o eof))   3235 (def read-table ((o i (stdin)) (o eof))
2629   (let e (read i eof)   3236   (let e (read i eof)
2630     (if (alist e) (listtab e) e)))   3237     (if (alist e) (listtab e) e)))
2631     3238  
2632 (def load-tables (file)   3239 (def load-tables (file)
2633   (w/infile i file   3240   (w/infile i file
2634     (w/uniq eof   3241     (w/uniq eof
2635       (drain (read-table i eof) eof))))   3242       (drain (read-table i eof) eof))))
2636     3243  
2637 (def save-table (h file)   3244 (def save-table (h file)
2638   (w/outfile o file (write-table h o))) <> 3245   (writefile (tablist h) file))
2639   = 3246  
2640 (def write-table (h (o o (stdout)))   3247 (def write-table (h (o o (stdout)))
2641   (write (tablist h) o))   3248   (write (tablist h) o))
2642     3249  
2643 (def copy (x) <> 3250 (def copy (x . args)
2644   (case (type x)   3251   (let x2 (case (type x)
2645     sym    x   3252             sym    x
2646     cons   (apply (fn args args) x)   3253             cons   (copylist x) ; (apply (fn args args) x)
2647     string (let new (newstring (len x))   3254             string (let new (newstring (len x))
2648              (forlen i x   3255                      (forlen i x
2649                (= (new i) (x i)))   3256                        (= (new i) (x i)))
2650              new)   3257                      new)
2651     table  (let new (table)   3258             table  (let new (table)
2652              (ontable k v x    3259                      (each (k v)
2653                (= (new k) v))   3260                        (= (new k) v))
2654              new)   3261                      new)
2655            (err "Can't copy " x)))   3262                    (err "Can't copy " x))
      3263     (map (fn ((k v)) (= (x2 k) v))
      3264          (pair args))
      3265     x2))
2656   = 3266  
2657 (def abs (n)   3267 (def abs (n)
2658   (if (< n 0) (- n) n))   3268   (if (< n 0) (- n) n))
2659     3269  
2660 ; The problem with returning a list instead of multiple values is that   3270 ; The problem with returning a list instead of multiple values is that
2661 ; you can't act as if the fn didn't return multiple vals in cases where   3271 ; you can't act as if the fn didn't return multiple vals in cases where
2662 ; you only want the first.  Not a big problem.   3272 ; you only want the first.  Not a big problem.
2663     3273  
2664 (def round (n)   3274 (def round (n)
2665   (withs (base (truncate n) rem (abs (- n base))) <> 3275   (withs (base (trunc n) rem (abs (- n base)))
2666     (if (> rem 1/2) ((if (> n 0) + -) base 1) = 3276     (if (> rem 1/2) ((if (> n 0) + -) base 1)
2667         (< rem 1/2) base   3277         (< rem 1/2) base
2668         (odd base)  ((if (> n 0) + -) base 1)   3278         (odd base)  ((if (> n 0) + -) base 1)
2669                     base)))   3279                     base)))
2670     3280  
2671 (def roundup (n)   3281 (def roundup (n)
2672   (withs (base (truncate n) rem (abs (- n base))) <> 3282   (withs (base (trunc n) rem (abs (- n base)))
2673     (if (>= rem 1/2)  = 3283     (if (>= rem 1/2) 
2674         ((if (> n 0) + -) base 1)   3284         ((if (> n 0) + -) base 1)
2675         base)))   3285         base)))
2676     3286  
2677 (def to-nearest (n quantum) <> 3287 (def nearest (n quantum)
2678   (* (roundup (/ n quantum)) quantum)) = 3288   (* (roundup (/ n quantum)) quantum))
2679     3289  
2680 (def avg (ns) (/ (apply + ns) (len ns)))   3290 (def avg (ns) (/ (apply + ns) (len ns)))
    -+ 3291  
      3292 (def med (ns (o test >))
      3293   ((sort test ns) (round (/ (len ns) 2))))
2681   = 3294  
2682 ; Use mergesort on assumption that mostly sorting mostly sorted lists   3295 ; Use mergesort on assumption that mostly sorting mostly sorted lists
2683 ; benchmark: (let td (n-of 10000 (rand 100)) (time (sort < td)) 1)    3296 ; benchmark: (let td (n-of 10000 (rand 100)) (time (sort < td)) 1) 
2684     3297  
2685 (def sort (test seq)   3298 (def sort (test seq)
2686   (if (alist seq)   3299   (if (alist seq)
2687       (mergesort test (copy seq))   3300       (mergesort test (copy seq))
2688       (coerce (mergesort test (coerce seq 'cons)) (type seq))))   3301       (coerce (mergesort test (coerce seq 'cons)) (type seq))))
2689     3302  
2690 ; Destructive stable merge-sort, adapted from slib and improved    3303 ; Destructive stable merge-sort, adapted from slib and improved 
2691 ; by Eli Barzilay for MzLib; re-written in Arc.   3304 ; by Eli Barzilay for MzLib; re-written in Arc.
2692     3305  
2693 (def mergesort (less? lst)   3306 (def mergesort (less? lst)
2694   (with (n (len lst))   3307   (with (n (len lst))
2695     (if (<= n 1) lst   3308     (if (<= n 1) lst
2696         ; ; check if the list is already sorted   3309         ; ; check if the list is already sorted
2697         ; ; (which can be a common case, eg, directory lists).   3310         ; ; (which can be a common case, eg, directory lists).
2698         ; (let loop ([last (car lst)] [next (cdr lst)])   3311         ; (let loop ([last (car lst)] [next (cdr lst)])
2699         ;   (or (null? next)   3312         ;   (or (null? next)
2700         ;       (and (not (less? (car next) last))   3313         ;       (and (not (less? (car next) last))
2701         ;            (loop (car next) (cdr next)))))   3314         ;            (loop (car next) (cdr next)))))
2702         ; lst   3315         ; lst
2703         ((afn (n)   3316         ((afn (n)
2704            (if (> n 2)   3317            (if (> n 2)
2705                 ; needs to evaluate L->R   3318                 ; needs to evaluate L->R
2706                 (withs (j (/ (if (even n) n (- n 1)) 2) ; faster than round   3319                 (withs (j (/ (if (even n) n (- n 1)) 2) ; faster than round
2707                         a (self j)   3320                         a (self j)
2708                         b (self (- n j)))   3321                         b (self (- n j)))
2709                   (merge less? a b))   3322                   (merge less? a b))
2710                ; the following case just inlines the length 2 case,   3323                ; the following case just inlines the length 2 case,
2711                ; it can be removed (and use the above case for n>1)   3324                ; it can be removed (and use the above case for n>1)
2712                ; and the code still works, except a little slower   3325                ; and the code still works, except a little slower
2713                (is n 2)   3326                (is n 2)
2714                 (with (x (car lst) y (cadr lst) p lst)   3327                 (with (x (car lst) y (cadr lst) p lst)
2715                   (= lst (cddr lst))   3328                   (= lst (cddr lst))
2716                   (when (less? y x) (scar p y) (scar (cdr p) x))   3329                   (when (less? y x) (scar p y) (scar (cdr p) x))
2717                   (scdr (cdr p) nil)   3330                   (scdr (cdr p) nil)
2718                   p)   3331                   p)
2719                (is n 1)   3332                (is n 1)
2720                 (with (p lst)   3333                 (with (p lst)
2721                   (= lst (cdr lst))   3334                   (= lst (cdr lst))
2722                   (scdr p nil)   3335                   (scdr p nil)
2723                   p)   3336                   p)
2724                nil))   3337                nil))
2725          n))))   3338          n))))
2726     3339  
2727 ; Also by Eli.   3340 ; Also by Eli.
2728     3341  
2729 (def merge (less? x y)   3342 (def merge (less? x y)
2730   (if (no x) y   3343   (if (no x) y
2731       (no y) x   3344       (no y) x
2732       (let lup nil   3345       (let lup nil
2733         (set lup <> 3346         (assign lup
2734              (fn (r x y r-x?) ; r-x? for optimization -- is r connected to x?   3347                 (fn (r x y r-x?) ; r-x? for optimization -- is r connected to x?
2735                (if (less? (car y) (car x))   3348                   (if (less? (car y) (car x))
2736                  (do (if r-x? (scdr r y))   3349                     (do (if r-x? (scdr r y))
2737                      (if (cdr y) (lup y x (cdr y) nil) (scdr y x)))   3350                         (if (cdr y) (lup y x (cdr y) nil) (scdr y x)))
2738                  ; (car x) <= (car y)   3351                     ; (car x) <= (car y)
2739                  (do (if (no r-x?) (scdr r x))   3352                     (do (if (no r-x?) (scdr r x))
2740                      (if (cdr x) (lup x (cdr x) y t) (scdr x y))))))   3353                         (if (cdr x) (lup x (cdr x) y t) (scdr x y))))))
2741         (if (less? (car y) (car x)) = 3354         (if (less? (car y) (car x))
2742           (do (if (cdr y) (lup y x (cdr y) nil) (scdr y x))   3355           (do (if (cdr y) (lup y x (cdr y) nil) (scdr y x))
2743               y)   3356               y)
2744           ; (car x) <= (car y)   3357           ; (car x) <= (car y)
2745           (do (if (cdr x) (lup x (cdr x) y t) (scdr x y))   3358           (do (if (cdr x) (lup x (cdr x) y t) (scdr x y))
2746               x)))))   3359               x)))))
2747     3360  
2748 (def bestn (n f seq)   3361 (def bestn (n f seq)
2749   (firstn n (sort f seq)))   3362   (firstn n (sort f seq)))
2750     3363  
2751 (def split (seq pos)   3364 (def split (seq pos)
2752   (withs (mid (nthcdr (- pos 1) seq)  <>    
2753           s2  (cdr mid))      
2754     (nil! (cdr mid))      
2755     (list seq s2)))   3365   (list (cut seq 0 pos) (cut seq pos)))
2756   = 3366  
2757 (mac time (expr)   3367 (mac time (expr)
2758   (w/uniq (t1 t2)   3368   (w/uniq (t1 t2)
2759     `(let ,t1 (msec)   3369     `(let ,t1 (msec)
2760        (do1 ,expr   3370        (do1 ,expr
2761             (let ,t2 (msec)   3371             (let ,t2 (msec)
2762               (prn "time: " (- ,t2 ,t1) " msec."))))))   3372               (prn "time: " (- ,t2 ,t1) " msec."))))))
2763     3373  
2764 (mac jtime (expr)   3374 (mac jtime (expr)
2765   `(do1 'ok (time ,expr)))   3375   `(do1 'ok (time ,expr)))
2766     3376  
2767 (mac time10 (expr)   3377 (mac time10 (expr)
2768   `(time (repeat 10 ,expr)))   3378   `(time (repeat 10 ,expr)))
2769     3379  
    -+ 3380 (def union (f xs ys)
      3381   (+ xs (rem (fn (y) (some [f _ y] xs))
      3382              ys)))
      3383  
2770 (= templates* (table)) = 3384 (= templates* (table))
2771     3385  
2772 (def maps (fn . args) +-    
2773   (apply join (apply map fn args)))      
2774        
2775 (mac deftem (tem . fields) = 3386 (mac deftem (tem . fields)
2776   (withs (name (carif tem) includes (if (acons tem) (cdr tem)))   3387   (withs (name (carif tem) includes (if (acons tem) (cdr tem)))
2777     `(= (templates* ',name)    3388     `(= (templates* ',name) 
2778         (+ (maps templates* ',(rev includes)) <> 3389         (+ (mappend templates* ',(rev includes))
2779            (list ,@(map (fn ((k v)) `(list ',k (fn () ,v))) = 3390            (list ,@(map (fn ((k v)) `(list ',k (fn () ,v)))
2780                         (pair fields)))))))   3391                         (pair fields)))))))
2781     3392  
    -+ 3393 (mac addtem (name . fields)
      3394   `(= (templates* ',name) 
      3395       (union (fn (x y) (is (car x) (car y)))
      3396              (list ,@(map (fn ((k v)) `(list ',k (fn () ,v)))
      3397                           (pair fields)))
      3398              (templates* ',name))))
      3399  
2782 (def inst (tem . args) = 3400 (def inst (tem . args)
2783   (let x (table)   3401   (let x (table)
2784     (each (k v) (templates* tem) <> 3402     (each (k v) (if (acons tem) tem (templates* tem))
2785       (unless (no v) (= (x k) (v)))) = 3403       (unless (no v) (= (x k) (v))))
2786     (each (k v) (pair args)   3404     (each (k v) (pair args)
2787       (= (x k) v))   3405       (= (x k) v))
2788     x))   3406     x))
2789     3407  
2790 ; To write something to be read by temread, (write (tablist x))   3408 ; To write something to be read by temread, (write (tablist x))
2791     3409  
2792 (def temread (tem (o str (stdin)))   3410 (def temread (tem (o str (stdin)))
2793   (templatize tem (read str)))   3411   (templatize tem (read str)))
2794     3412  
2795 ; Converts alist to inst; ugly; maybe should make this part of coerce.   3413 ; Converts alist to inst; ugly; maybe should make this part of coerce.
2796 ; Note: discards fields not defined by the template.   3414 ; Note: discards fields not defined by the template.
2797     3415  
2798 (def templatize (tem raw)   3416 (def templatize (tem raw)
2799   (with (x (inst tem) fields (templates* tem)) <> 3417   (with (x (inst tem) fields (if (acons tem) tem (templates* tem)))
2800     (each (k v) raw = 3418     (each (k v) raw
2801       (when (assoc k fields)   3419       (when (assoc k fields)
2802         (= (x k) v)))   3420         (= (x k) v)))
2803     x))   3421     x))
2804     3422  
2805 (def temload (tem file)   3423 (def temload (tem file)
2806   (w/infile i file (temread tem i)))   3424   (w/infile i file (temread tem i)))
2807     3425  
2808 (def temloadall (tem file)   3426 (def temloadall (tem file)
2809   (map (fn (pairs) (templatize tem pairs))          3427   (map (fn (pairs) (templatize tem pairs))       
2810        (w/infile in file (readall in))))   3428        (w/infile in file (readall in))))
2811     3429  
2812     3430  
2813 (def number (n) (in (type n) 'int 'num))   3431 (def number (n) (in (type n) 'int 'num))
2814     3432  
    -+ 3433 (def since (t1) (- (seconds) t1))
      3434  
      3435 (def minutes-since (t1) (/ (since t1) 60))
      3436 (def hours-since (t1)   (/ (since t1) 3600))
      3437 (def days-since (t1)    (/ (since t1) 86400))
      3438  
      3439 ; could use a version for fns of 1 arg at least
      3440  
2815 (def cache (timef valf) = 3441 (def cache (timef valf)
2816   (with (cached nil gentime nil)   3442   (with (cached nil gentime nil)
2817     (fn ()   3443     (fn ()
2818       (unless (and cached (< (- (seconds) gentime) (timef))) <> 3444       (unless (and cached (< (since gentime) (timef)))
2819         (= cached  (valf) = 3445         (= cached  (valf)
2820            gentime (seconds)))   3446            gentime (seconds)))
2821       cached)))   3447       cached)))
2822     3448  
    -+ 3449 (mac defcache (name lasts . body)
      3450   `(safeset ,name (cache (fn () ,lasts)
      3451                          (fn () ,@body))))
      3452  
2823 (mac errsafe (expr) = 3453 (mac errsafe (expr)
2824   `(on-err (fn (c) nil)   3454   `(on-err (fn (c) nil)
2825            (fn () ,expr)))   3455            (fn () ,expr)))
2826     3456  
2827 (def saferead (arg) (errsafe (read arg))) <> 3457 (def saferead (arg) (errsafe:read arg))
2828   = 3458  
2829 (def safe-load-table (filename)    3459 (def safe-load-table (filename) 
2830   (or (errsafe (load-table filename)) <> 3460   (or (errsafe:load-table filename)
2831       (table))) = 3461       (table)))
2832     3462  
2833 (def ensure-dir (path)   3463 (def ensure-dir (path)
2834   (unless (dir-exists path)   3464   (unless (dir-exists path)
2835     (system (string "mkdir " path)))) <> 3465     (system (string "mkdir -p " path))))
2836   = 3466  
2837 (def date ((o time (seconds))) <> 3467 (def date ((o s (seconds)))
2838   (let val (tostring (system (string "date -u -r " time " \"+%Y-%m-%d\"")))   3468   (rev (nthcdr 3 (timedate s))))
2839     (subseq val 0 (- (len val) 1))))      
2840   = 3469  
2841 (def since (t1) (- (seconds) t1)) <> 3470 (def datestring ((o s (seconds)))
      3471   (let (y m d) (date s)
      3472     (string y "-" (if (< m 10) "0") m "-" (if (< d 10) "0") d)))
2842   = 3473  
2843 (def count (test x)   3474 (def count (test x)
2844   (with (n 0 testf (testify test))   3475   (with (n 0 testf (testify test))
2845     (each elt x   3476     (each elt x
2846       (if (testf elt) (++ n)))   3477       (if (testf elt) (++ n)))
2847     n))   3478     n))
2848     3479  
2849 (def ellipsize (str (o limit 80))   3480 (def ellipsize (str (o limit 80))
2850   (if (<= (len str) limit)   3481   (if (<= (len str) limit)
2851       str   3482       str
2852       (+ (subseq str 0 limit) "..."))) <> 3483       (+ (cut str 0 limit) "...")))
2853   = 3484  
    <> 3485 (def rand-elt (seq) 
2854 (def random-elt (seq) (seq (rand (len seq))))   3486   (seq (rand (len seq))))
2855   = 3487  
2856 (mac until (test . body)   3488 (mac until (test . body)
2857   `(while (no ,test) ,@body))   3489   `(while (no ,test) ,@body))
2858     3490  
2859 (def before (x y seq (o i 0))   3491 (def before (x y seq (o i 0))
2860   (with (xp (pos x seq i) yp (pos y seq i))   3492   (with (xp (pos x seq i) yp (pos y seq i))
2861     (and xp (or (no yp) (< xp yp)))))   3493     (and xp (or (no yp) (< xp yp)))))
2862     3494  
2863 (def orf fns   3495 (def orf fns
    <> 3496   (fn args
2864   (fn (x) (some [_ x] fns)))   3497     ((afn (fs)
      3498        (and fs (or (apply (car fs) args) (self (cdr fs)))))
      3499      fns)))
2865   = 3500  
2866 (def andf fns   3501 (def andf fns
    <> 3502   (fn args
2867   (fn (x) (all [_ x] fns)))   3503     ((afn (fs)
      3504        (if (no fs)       t
      3505            (no (cdr fs)) (apply (car fs) args)
      3506                          (and (apply (car fs) args) (self (cdr fs)))))
      3507      fns)))
2868   = 3508  
2869 (def atend (i s)   3509 (def atend (i s)
2870   (>= i (- (len s) 1))) <> 3510   (> i (- (len s) 2)))
2871   = 3511  
2872 (def multiple (x y)   3512 (def multiple (x y)
2873   (is 0 (mod x y)))   3513   (is 0 (mod x y)))
2874     3514  
2875 (mac nor args `(no (or ,@args)))    3515 (mac nor args `(no (or ,@args))) 
2876     3516  
2877 ; Consider making the default sort fn take compare's two args (when do    3517 ; Consider making the default sort fn take compare's two args (when do 
2878 ; you ever have to sort mere lists of numbers?) and rename current sort   3518 ; you ever have to sort mere lists of numbers?) and rename current sort
2879 ; as prim-sort or something.   3519 ; as prim-sort or something.
2880     3520  
2881 ; Could simply modify e.g. > so that (> len) returned the same thing   3521 ; Could simply modify e.g. > so that (> len) returned the same thing
2882 ; as (compare > len).   3522 ; as (compare > len).
2883     3523  
2884 (def compare (comparer scorer)   3524 (def compare (comparer scorer)
2885   (fn (x y) (comparer (scorer x) (scorer y))))   3525   (fn (x y) (comparer (scorer x) (scorer y))))
2886     3526  
2887 ; Cleaner thus, but may only ever need in 2 arg case.   3527 ; Cleaner thus, but may only ever need in 2 arg case.
2888     3528  
2889 ;(def compare (comparer scorer)   3529 ;(def compare (comparer scorer)
2890 ;  (fn args (apply comparer map scorer args)))   3530 ;  (fn args (apply comparer map scorer args)))
2891     3531  
    <> 3532 ; (def only (f g . args) (aif (apply g args) (f it)))
      3533  
2892 (def only (f g . args)   3534 (def only (f)
2893   (aif (apply g args) (f it)))   3535   (fn args (if (car args) (apply f args))))
2894   = 3536  
2895 (mac conswhen (f x y)   3537 (mac conswhen (f x y)
2896   (w/uniq (gf gx)   3538   (w/uniq (gf gx)
2897    `(with (,gf ,f ,gx ,x)   3539    `(with (,gf ,f ,gx ,x)
2898       (if (,gf ,gx) (cons ,gx ,y) ,y))))   3540       (if (,gf ,gx) (cons ,gx ,y) ,y))))
2899     3541  
2900 ; Could rename this get, but don't unless it's frequently used. <> 3542 ; Could combine with firstn if put f arg last, default to (fn (x) t).
2901   = 3543  
2902 (def firstn-that (n f xs) <> 3544 (def retrieve (n f xs)
      3545   (if (no n)                 (keep f xs)
2903   (if (or (<= n 0) (no xs))   3546       (or (<= n 0) (no xs))  nil
2904        nil      
2905       (f (car xs))      
2906        (cons (car xs) (firstn-that (- n 1) f (cdr xs)))   3547       (f (car xs))           (cons (car xs) (retrieve (- n 1) f (cdr xs)))
2907        (firstn-that n f (cdr xs))))   3548                              (retrieve n f (cdr xs))))
2908   = 3549  
2909 (def dedup (xs)   3550 (def dedup (xs)
2910   (with (h (table) acc nil)   3551   (with (h (table) acc nil)
2911     (each x xs   3552     (each x xs
2912       (unless (h x)   3553       (unless (h x)
2913         (push x acc)   3554         (push x acc)
2914         (t! (h x)))) <> 3555         (set (h x))))
2915     (rev acc))) = 3556     (rev acc)))
2916     3557  
2917 (def single (x) (and (acons x) (no (cdr x))))   3558 (def single (x) (and (acons x) (no (cdr x))))
2918     3559  
2919 (def plural (n str) +-    
2920   (if (or (is n 1) (single n))      
2921       str       
2922       (string str "s")))      
2923        
2924 (def intersperse (x ys) = 3560 (def intersperse (x ys)
2925   (cons (car ys) <> 3561   (and ys (cons (car ys)
2926         (mappend [list x _] (cdr ys))))   3562                 (mappend [list x _] (cdr ys)))))
2927   = 3563  
2928 (def counts (seq (o c (table)))   3564 (def counts (seq (o c (table)))
2929   (if (no seq)   3565   (if (no seq)
2930       c   3566       c
2931       (do (zap [if _ (+ _ 1) 1] (c (car seq))) <> 3567       (do (++ (c (car seq) 0))
2932           (counts (cdr seq) c)))) = 3568           (counts (cdr seq) c))))
2933     3569  
2934 (def commonest (seq)   3570 (def commonest (seq)
2935   (with (winner nil n 0)   3571   (with (winner nil n 0)
2936     (ontable k v (counts seq) <> 3572     (each (k v) (counts seq)
2937       (when (> v n) (= winner k n v))) = 3573       (when (> v n) (= winner k n v)))
2938     (list winner n)))   3574     (list winner n)))
2939   +-    
2940 (def splitn (n xs)      
2941   (let acc nil      
2942     ((afn (n xs)      
2943        (if (or (no xs) (<= n 0))      
2944            (list (rev acc) xs)      
2945            (do (push (car xs) acc)      
2946                (self (- n 1) (cdr xs)))))      
2947      n xs)))      
2948   = 3575  
2949 (def reduce (f xs)   3576 (def reduce (f xs)
2950   (if (cddr xs)   3577   (if (cddr xs)
2951       (reduce f (cons (f (car xs) (cadr xs)) (cddr xs)))   3578       (reduce f (cons (f (car xs) (cadr xs)) (cddr xs)))
2952       (apply f xs)))   3579       (apply f xs)))
2953     3580  
2954 (def rreduce (f xs)   3581 (def rreduce (f xs)
2955   (if (cddr xs)   3582   (if (cddr xs)
2956       (f (car xs) (rreduce f (cdr xs)))   3583       (f (car xs) (rreduce f (cdr xs)))
2957       (apply f xs)))   3584       (apply f xs)))
2958     3585  
2959 (let argsym (uniq)   3586 (let argsym (uniq)
2960     3587  
2961   (def parse-format (str)   3588   (def parse-format (str)
2962     (rev (accum a <> 3589     (accum a
2963            (with (chars nil  i -1)   3590       (with (chars nil  i -1)
2964              (w/instring s str   3591         (w/instring s str
2965                (whilet c (readc s)   3592           (whilet c (readc s)
2966                  (case c    3593             (case c 
2967                    #\# (do (a (coerce (rev chars) 'string))   3594               #\# (do (a (coerce (rev chars) 'string))
2968                            (nil! chars)   3595                       (wipe chars)
2969                            (a (read s)))   3596                       (a (read s)))
2970                    #\~ (do (a (coerce (rev chars) 'string))   3597               #\~ (do (a (coerce (rev chars) 'string))
2971                            (nil! chars)   3598                       (wipe chars)
2972                            (readc s)   3599                       (readc s)
2973                            (a (list argsym (++ i))))   3600                       (a (list argsym (++ i))))
2974                        (push c chars))))   3601                   (push c chars))))
2975               (when chars   3602          (when chars
2976                 (a (coerce (rev chars) 'string)))))))   3603            (a (coerce (rev chars) 'string))))))
2977    = 3604   
2978   (mac prf (str . args)   3605   (mac prf (str . args)
2979     `(let ,argsym (list ,@args)   3606     `(let ,argsym (list ,@args)
2980        (pr ,@(parse-format str))))   3607        (pr ,@(parse-format str))))
2981 )   3608 )
2982     3609  
2983 (def load (file)   3610 (def load (file)
2984   (w/infile f file   3611   (w/infile f file
    <> 3612     (w/uniq eof
2985     (whilet e (read f)   3613       (whiler e (read f eof) eof
2986       (eval e))))   3614         (eval e)))))
2987   = 3615  
2988 (def positive (x)   3616 (def positive (x)
2989   (and (number x) (> x 0)))   3617   (and (number x) (> x 0)))
2990     3618  
2991 (mac w/table (var . body)   3619 (mac w/table (var . body)
2992   `(let ,var (table) ,@body ,var))   3620   `(let ,var (table) ,@body ,var))
2993     3621  
2994 (def ero args  <> 3622 (def ero args
      3623   (w/stdout (stderr) 
2995   (each a args    3624     (each a args 
2996     (write a (stderr))    3625       (write a)
2997     (writec #\space (stderr))))   3626       (writec #\space))
      3627     (writec #\newline))
      3628   (car args))
2998   = 3629  
2999 (def queue () (list nil nil 0))   3630 (def queue () (list nil nil 0))
3000     3631  
3001 ; Despite call to atomic, once had some sign this wasn't thread-safe.   3632 ; Despite call to atomic, once had some sign this wasn't thread-safe.
    -+ 3633 ; Keep an eye on it.
3002   = 3634  
3003 (def enq (obj q)   3635 (def enq (obj q)
3004   (atomic   3636   (atomic
3005     (++ (q 2))   3637     (++ (q 2))
3006     (if (no (car q))   3638     (if (no (car q))
3007         (= (cadr q) (= (car q) (list obj)))   3639         (= (cadr q) (= (car q) (list obj)))
3008         (= (cdr (cadr q)) (list obj)   3640         (= (cdr (cadr q)) (list obj)
3009            (cadr q)       (cdr (cadr q))))   3641            (cadr q)       (cdr (cadr q))))
3010     (car q)))   3642     (car q)))
3011     3643  
3012 (def deq (q)   3644 (def deq (q)
3013   (atomic (unless (is (q 2) 0) (-- (q 2)))   3645   (atomic (unless (is (q 2) 0) (-- (q 2)))
3014           (pop (car q))))   3646           (pop (car q))))
3015     3647  
3016 ; Should redef len to do this, and make queues lists annotated queue.   3648 ; Should redef len to do this, and make queues lists annotated queue.
3017     3649  
3018 (def qlen (q) (q 2))   3650 (def qlen (q) (q 2))
3019     3651  
3020 (def qlist (q) (car q))   3652 (def qlist (q) (car q))
3021     3653  
3022 (def enq-limit (val q (o limit 1000))   3654 (def enq-limit (val q (o limit 1000))
3023   (atomic   3655   (atomic
3024      (unless (< (qlen q) limit)   3656      (unless (< (qlen q) limit)
3025        (deq q))   3657        (deq q))
3026      (enq val q)))   3658      (enq val q)))
3027     3659  
3028 (def median (ns)   3660 (def median (ns)
3029   ((sort > ns) (truncate (/ (len ns) 2)))) <> 3661   ((sort > ns) (trunc (/ (len ns) 2))))
3030   = 3662  
3031 (mac noisy-each (n var val . body)   3663 (mac noisy-each (n var val . body)
3032   (w/uniq (gn gc)   3664   (w/uniq (gn gc)
3033     `(with (,gn ,n ,gc 0)   3665     `(with (,gn ,n ,gc 0)
3034        (each ,var ,val   3666        (each ,var ,val
3035          (when (multiple (++ ,gc) ,gn)   3667          (when (multiple (++ ,gc) ,gn)
3036            (pr ".")    3668            (pr ".") 
3037            ;(flushout) <> 3669            (flushout)
3038            ) = 3670            )
3039          ,@body)   3671          ,@body)
3040        (prn)   3672        (prn)
3041        ;(flushout) <> 3673        (flushout))))
3042        )))      
3043   = 3674  
3044 (mac point (name . body)   3675 (mac point (name . body)
3045   (w/uniq g <> 3676   (w/uniq (g p)
3046     `(ccc (fn (,g) = 3677     `(ccc (fn (,g)
3047             (let ,name [,g _] <> 3678             (let ,name (fn ((o ,p)) (,g ,p))
3048               ,@body))))) = 3679               ,@body)))))
3049     3680  
3050 (mac catch body   3681 (mac catch body
3051   `(point throw ,@body))   3682   `(point throw ,@body))
3052     3683  
3053 (def downcase (x)   3684 (def downcase (x)
3054   (let downc (fn (c)   3685   (let downc (fn (c)
3055                (let n (coerce c 'int)   3686                (let n (coerce c 'int)
3056                  (if (or (< 64 n 91) (< 191 n 215) (< 215 n 223))   3687                  (if (or (< 64 n 91) (< 191 n 215) (< 215 n 223))
3057                      (coerce (+ n 32) 'char)   3688                      (coerce (+ n 32) 'char)
3058                      c)))   3689                      c)))
3059     (case (type x)   3690     (case (type x)
3060       string (map downc x)   3691       string (map downc x)
3061       char   (downc x)   3692       char   (downc x)
3062       sym    (sym (map downc (coerce x 'string)))   3693       sym    (sym (map downc (coerce x 'string)))
3063              (err "Can't downcase" x))))   3694              (err "Can't downcase" x))))
3064     3695  
3065 (def upcase (x)   3696 (def upcase (x)
3066   (let upc (fn (c)   3697   (let upc (fn (c)
3067              (let n (coerce c 'int)   3698              (let n (coerce c 'int)
3068                (if (or (< 96 n 123) (< 223 n 247) (< 247 n 255))   3699                (if (or (< 96 n 123) (< 223 n 247) (< 247 n 255))
3069                    (coerce (- n 32) 'char)   3700                    (coerce (- n 32) 'char)
3070                    c)))   3701                    c)))
3071     (case (type x)   3702     (case (type x)
3072       string (map upc x)   3703       string (map upc x)
3073       char   (upc x)   3704       char   (upc x)
3074       sym    (sym (map upc (coerce x 'string)))   3705       sym    (sym (map upc (coerce x 'string)))
3075              (err "Can't upcase" x))))   3706              (err "Can't upcase" x))))
3076     3707  
    -+ 3708 (def inc (x (o n 1))
      3709   (coerce (+ (coerce x 'int) n) (type x)))
      3710  
3077 (def range (start end) = 3711 (def range (start end)
3078   (if (> start end)   3712   (if (> start end)
3079       nil   3713       nil
3080       (cons start (range (+ start 1) end)))) <> 3714       (cons start (range (inc start) end))))
3081   = 3715  
3082 (def mismatch (s1 s2)   3716 (def mismatch (s1 s2)
3083   (catch   3717   (catch
3084     (on c s1   3718     (on c s1
3085       (when (isnt c (s2 index))   3719       (when (isnt c (s2 index))
3086         (throw index)))))   3720         (throw index)))))
3087     3721  
3088 (def memtable (ks)   3722 (def memtable (ks)
3089   (let h (table)   3723   (let h (table)
3090     (each k ks (t! (h k))) <> 3724     (each k ks (set (h k)))
3091     h)) = 3725     h))
3092     3726  
3093 (= bar* " | ")   3727 (= bar* " | ")
3094     3728  
3095 (mac w/bars body   3729 (mac w/bars body
3096   (w/uniq (out needbars)   3730   (w/uniq (out needbars)
3097     `(let ,needbars nil   3731     `(let ,needbars nil
3098        (do ,@(map (fn (e)   3732        (do ,@(map (fn (e)
3099                     `(let ,out (tostring ,e)   3733                     `(let ,out (tostring ,e)
3100                        (unless (is ,out "")   3734                        (unless (is ,out "")
3101                          (if ,needbars   3735                          (if ,needbars
3102                              (pr bar* ,out)   3736                              (pr bar* ,out)
3103                              (do (t! ,needbars) <> 3737                              (do (set ,needbars)
3104                                  (pr ,out)))))) = 3738                                  (pr ,out))))))
3105                   body)))))   3739                   body)))))
3106     3740  
    -+ 3741 (def len< (x n) (< (len x) n))
3107   = 3742  
    <> 3743 (def len> (x n) (> (len x) n))
3108 ; Lower priority ideas   3744  
      3745 (mac thread body 
      3746   `(new-thread (fn () ,@body)))
3109   = 3747  
    -+ 3748 (mac trav (x . fs)
      3749   (w/uniq g
      3750     `((afn (,g)
      3751         (when ,g
      3752           ,@(map [list _ g] fs)))
      3753       ,x)))
      3754  
      3755 (mac or= (place expr)
      3756   (let (binds val setter) (setforms place)
      3757     `(atwiths ,binds
      3758        (or ,val (,setter ,expr)))))
      3759  
      3760 (= hooks* (table))
      3761  
      3762 (def hook (name . args)
      3763   (aif (hooks* name) (apply it args)))
      3764  
      3765 (mac defhook (name . rest)
      3766   `(= (hooks* ',name) (fn ,@rest)))
      3767   
      3768 (mac out (expr) `(pr ,(tostring (eval expr))))
      3769  
      3770 ; if renamed this would be more natural for (map [_ user] pagefns*)
      3771  
      3772 (def get (index) [_ index])
      3773  
      3774 (= savers* (table))
      3775  
      3776 (mac fromdisk (var file init load save)
      3777   (w/uniq (gf gv)
      3778     `(unless (bound ',var)
      3779        (do1 (= ,var (iflet ,gf (file-exists ,file)
      3780                                (,load ,gf)
      3781                                ,init))
      3782             (= (savers* ',var) (fn (,gv) (,save ,gv ,file)))))))
      3783  
      3784 (mac diskvar (var file)
      3785   `(fromdisk ,var ,file nil readfile1 writefile))
      3786  
      3787 (mac disktable (var file)
      3788   `(fromdisk ,var ,file (table) load-table save-table))
      3789  
      3790 (mac todisk (var (o expr var))
      3791   `((savers* ',var) 
      3792     ,(if (is var expr) var `(= ,var ,expr))))
      3793  
      3794  
      3795 (mac evtil (expr test)
      3796   (w/uniq gv
      3797     `(let ,gv ,expr
      3798        (while (no (,test ,gv))
      3799          (= ,gv ,expr))
      3800        ,gv)))
      3801  
      3802 (def rand-key (h)
      3803   (if (empty h)
      3804       nil
      3805       (let n (rand (len h))
      3806         (catch
      3807           (each (k v) h
      3808             (when (is (-- n) -1)
      3809               (throw k)))))))
      3810  
      3811 (def ratio (test xs)
      3812   (if (empty xs)
      3813       0
      3814       (/ (count test xs) (len xs))))
      3815  
      3816  
      3817 ; any logical reason I can't say (push x (if foo y z)) ?
      3818 ;   eval would have to always ret 2 things, the val and where it came from
      3819 ; idea: implicit tables of tables; setf empty field, becomes table
      3820 ;   or should setf on a table just take n args?
      3821  
      3822 ; idea: use constants in functional position for currying?
      3823 ;       (1 foo) would mean (fn args (apply foo 1 args))
      3824 ; another solution would be to declare certain symbols curryable, and 
      3825 ;  if > was, >_10 would mean [> _ 10]
      3826 ;  or just say what the hell and make _ ssyntax for currying
      3827 ; idea: make >10 ssyntax for [> _ 10]
3110 ; solution to the "problem" of improper lists: allow any atom as a list = 3828 ; solution to the "problem" of improper lists: allow any atom as a list
3111 ;  terminator, not just nil.  means list recursion should terminate on    3829 ;  terminator, not just nil.  means list recursion should terminate on 
3112 ;  atom rather than nil, (def empty (x) (or (atom x) (is x "")))   3830 ;  atom rather than nil, (def empty (x) (or (atom x) (is x "")))
3113 ; table should be able to take an optional initial-value.  handle in sref.   3831 ; table should be able to take an optional initial-value.  handle in sref.
3114 ; warn about code of form (if (= )) -- probably mean is   3832 ; warn about code of form (if (= )) -- probably mean is
3115 ; warn when a fn has a parm that's already defined as a macro.   3833 ; warn when a fn has a parm that's already defined as a macro.
3116 ;   (def foo (after) (after))   3834 ;   (def foo (after) (after))
3117 ; idea: a fn (nothing) that returns a special gensym which is ignored   3835 ; idea: a fn (nothing) that returns a special gensym which is ignored
3118 ;  by map, so can use map in cases when don't want all the vals   3836 ;  by map, so can use map in cases when don't want all the vals
3119 ; idea: anaph macro so instead of (aand x y) say (anaph and x y)   3837 ; idea: anaph macro so instead of (aand x y) say (anaph and x y)
3120 ; idea: foo.bar!baz as an abbrev for (foo bar 'baz)   3838 ; idea: foo.bar!baz as an abbrev for (foo bar 'baz)
    -+ 3839 ;  or something a bit more semantic?
3121 ; could uniq be (def uniq () (annotate 'symbol (list 'u))) again? = 3840 ; could uniq be (def uniq () (annotate 'symbol (list 'u))) again?
3122 ; idea: use x- for (car x) and -x for (cdr x)  (but what about math -?)   3841 ; idea: use x- for (car x) and -x for (cdr x)  (but what about math -?)
3123 ; idea: get rid of strings and just use symbols   3842 ; idea: get rid of strings and just use symbols
3124 ; could a string be (#\a #\b . "") ?   3843 ; could a string be (#\a #\b . "") ?
3125 ; better err msg when , outside of a bq   3844 ; better err msg when , outside of a bq
3126 ; idea: parameter (p foo) means in body foo is (pair arg)   3845 ; idea: parameter (p foo) means in body foo is (pair arg)
3127 ; idea: make ('string x) equiv to (coerce x 'string) ?  or isa?   3846 ; idea: make ('string x) equiv to (coerce x 'string) ?  or isa?
3128 ;   quoted atoms in car valuable unused semantic space   3847 ;   quoted atoms in car valuable unused semantic space
    -+ 3848 ; idea: if (defun foo (x y) ...), make (foo 1) return (fn (y) (foo 1 y))
      3849 ;   probably would lead to lots of errors when call with missing args
      3850 ;   but would be really dense with . notation, (foo.1 2)
      3851 ; or use special ssyntax for currying: (foo@1 2)
      3852 ; remember, can also double; could use foo::bar to mean something
      3853 ; wild idea: inline defs for repetitive code
      3854 ;  same args as fn you're in
      3855 ; variant of compose where first fn only applied to first arg?
      3856 ;  (> (len x) y)  means (>+len x y)
      3857 ; use ssyntax underscore for a var?
      3858 ;  foo_bar means [foo _ bar]
      3859 ;  what does foo:_:bar mean?
      3860 ; matchcase
      3861 ; idea: atable that binds it to table, assumes input is a list
      3862 ; crazy that finding the top 100 nos takes so long:
      3863 ;  (let bb (n-of 1000 (rand 50)) (time10 (bestn 100 > bb)))
      3864 ;  time: 2237 msec.  -> now down to 850 msec
3129   = 3865  
3130     3866  
3131 ./as.scm   3867 ./as.scm
3132 ; mzscheme -m -f as.scm   3868 ; mzscheme -m -f as.scm
3133 ; (tl)   3869 ; (tl)
3134 ; (asv)   3870 ; (asv)
3135 ; http://localhost:8080   3871 ; http://localhost:8080
3136     3872  
3137 (require mzscheme) ; promise we won't redefine mzscheme bindings   3873 (require mzscheme) ; promise we won't redefine mzscheme bindings
3138     3874  
3139 (load "ac.scm")  <> 3875 (require "ac.scm") 
3140 (require "brackets.scm") = 3876 (require "brackets.scm")
3141 (use-bracket-readtable)   3877 (use-bracket-readtable)
3142     3878  
3143 (aload "arc.arc")   3879 (aload "arc.arc")
3144 (aload "libs.arc")    3880 (aload "libs.arc") 
3145     3881  
3146 (tl)   3882 (tl)
3147     3883  
3148     3884  
3149 ./blog.arc   3885 ./blog.arc
3150 ; Blog tool example.  20 Jan 08. <> 3886 ; Blog tool example.  20 Jan 08, rev 21 May 09.
3151   = 3887  
3152 ; To run:   3888 ; To run:
3153 ; arc> (load "blog.arc")   3889 ; arc> (load "blog.arc")
3154 ; arc> (bsv)   3890 ; arc> (bsv)
3155 ; go to http://localhost:8080/blog   3891 ; go to http://localhost:8080/blog
3156     3892  
3157 (= postdir* "arc/posts/"  maxid* 0  posts* (table))   3893 (= postdir* "arc/posts/"  maxid* 0  posts* (table))
3158     3894  
3159 (= blogtitle* "A Blog")   3895 (= blogtitle* "A Blog")
3160     3896  
3161 (deftem post <> 3897 (deftem post  id nil  title nil  text nil)
3162   id     nil      
3163   title  nil      
3164   text   nil)      
3165   = 3898  
3166 (def load-posts ()   3899 (def load-posts ()
3167   (each id (map [coerce _ 'int] (dir postdir*)) <> 3900   (each id (map int (dir postdir*))
3168     (= maxid*      (max maxid* id) = 3901     (= maxid*      (max maxid* id)
3169        (posts* id) (temload 'post (string postdir* id)))))   3902        (posts* id) (temload 'post (string postdir* id)))))
3170     3903  
3171 (def save-post (p) <>    
3172   (save-table p (string postdir* (p 'id))))   3904 (def save-post (p) (save-table p (string postdir* p!id)))
3173   = 3905  
3174 (def post (id) (posts* (errsafe (coerce id 'int)))) <> 3906 (def post (id) (posts* (errsafe:int id)))
3175   = 3907  
3176 (mac blogpage body   3908 (mac blogpage body
3177   `(whitepage    3909   `(whitepage 
3178      (center   3910      (center
3179        (widtable 600    3911        (widtable 600 
3180          (tag b (link blogtitle* "blog"))   3912          (tag b (link blogtitle* "blog"))
3181          (br 3)   3913          (br 3)
3182          ,@body   3914          ,@body
3183          (br 3)   3915          (br 3)
3184          (w/bars (link "archive")   3916          (w/bars (link "archive")
3185                  (link "new post" "newpost"))))))   3917                  (link "new post" "newpost"))))))
3186     3918  
    <> 3919 (defop viewpost req (blogop post-page req))
3187 (defop viewpost req   3920  
      3921 (def blogop (f req)
3188   (aif (post (arg req "id"))  = 3922   (aif (post (arg req "id")) 
3189        (post-page (get-user req) it)  <> 3923        (f (get-user req) it) 
3190        (notfound)))   3924        (blogpage (pr "No such post."))))
3191   = 3925  
3192 (def permalink (p) (string "viewpost?id=" (p 'id))) <> 3926 (def permalink (p) (string "viewpost?id=" p!id))
3193   = 3927  
3194 (def post-page (user p) (blogpage (display-post user p)))   3928 (def post-page (user p) (blogpage (display-post user p)))
3195     3929  
3196 (def display-post (user p)   3930 (def display-post (user p)
3197   (tag b (link (p 'title) (permalink p))) <> 3931   (tag b (link p!title (permalink p)))
3198   (when user = 3932   (when user
3199     (sp)   3933     (sp)
3200     (link "[edit]" (string "editpost?id=" (p 'id)))) <> 3934     (link "[edit]" (string "editpost?id=" p!id)))
3201   (br2) = 3935   (br2)
3202   (pr (p 'text))) <> 3936   (pr p!text))
3203   = 3937  
3204 (def notfound () +-    
3205   (blogpage (pr "No such post.")))      
3206        
3207 (defopl newpost req = 3938 (defopl newpost req
3208   (whitepage   3939   (whitepage
3209     (aform (fn (req) <>    
3210              (let user (get-user req)   3940     (aform [let u (get-user _)
3211                (post-page user      
3212                           (addpost user (arg req "t") (arg req "b")))))   3941              (post-page u (addpost u (arg _ "t") (arg _ "b")))]
3213       (tab      
3214         (row "title" (input "t" "" 60))   3942       (tab (row "title" (input "t" "" 60))
3215         (row "text"  (textarea "b" 10 80))   3943            (row "text"  (textarea "b" 10 80))
3216         (row ""      (submit))))))   3944            (row ""      (submit))))))
3217   = 3945  
3218 (def addpost (user title text)   3946 (def addpost (user title text)
3219   (let p (inst 'post 'id (++ maxid*) 'title title 'text text)   3947   (let p (inst 'post 'id (++ maxid*) 'title title 'text text)
3220     (save-post p)   3948     (save-post p)
3221     (= (posts* (p 'id)) p))) <> 3949     (= (posts* p!id) p)))
3222   = 3950  
3223 (defopl editpost req <>    
3224   (aif (post (arg req "id"))      
3225        (edit-page (get-user req) it)   3951 (defopl editpost req (blogop edit-page req))
3226        (notfound)))      
3227   = 3952  
3228 (def edit-page (user p)   3953 (def edit-page (user p)
3229   (whitepage   3954   (whitepage
3230     (vars-form user   3955     (vars-form user
3231                `((string title ,(p 'title) t t) <> 3956                `((string title ,p!title t t) (text text ,p!text t t))
3232                  (text   text  ,(p 'text)  t t))      
3233                (fn (name val) (= (p name) val)) = 3957                (fn (name val) (= (p name) val))
3234                (fn () (save-post p)   3958                (fn () (save-post p)
3235                       (post-page user p)))))   3959                       (post-page user p)))))
3236     3960  
3237 (defop archive req   3961 (defop archive req
3238   (blogpage   3962   (blogpage
3239     (tag ul   3963     (tag ul
3240       (each p (map post (rev (range 1 maxid*)))   3964       (each p (map post (rev (range 1 maxid*)))
3241         (tag li (link (p 'title) (permalink p))))))) <> 3965         (tag li (link p!title (permalink p)))))))
3242   = 3966  
3243 (defop blog req   3967 (defop blog req
3244   (let user (get-user req)   3968   (let user (get-user req)
3245     (blogpage   3969     (blogpage
3246       (for i 0 4   3970       (for i 0 4
3247         (awhen (posts* (- maxid* i))    3971         (awhen (posts* (- maxid* i)) 
3248           (display-post user it)   3972           (display-post user it)
3249           (br 3))))))   3973           (br 3))))))
3250     3974  
3251 (def bsv ()   3975 (def bsv ()
3252   (ensure-dir postdir*)   3976   (ensure-dir postdir*)
3253   (load-posts)   3977   (load-posts)
3254   (asv))   3978   (asv))
3255     3979  
3256     3980  
3257     3981  
3258 ./brackets.scm   3982 ./brackets.scm
3259 ; From Eli Barzilay, eli@barzilay.org   3983 ; From Eli Barzilay, eli@barzilay.org
3260     3984  
3261 ;> (require "brackets.scm")    3985 ;> (require "brackets.scm") 
3262 ;> (use-bracket-readtable)    3986 ;> (use-bracket-readtable) 
3263 ;> ([+ _ 1] 10)    3987 ;> ([+ _ 1] 10) 
3264 ;11   3988 ;11
3265     3989  
3266 (module brackets mzscheme   3990 (module brackets mzscheme
3267      3991   
3268 ; main reader function for []s   3992 ; main reader function for []s
3269 ; recursive read starts with default readtable's [ parser,   3993 ; recursive read starts with default readtable's [ parser,
3270 ; but nested reads still use the curent readtable:   3994 ; but nested reads still use the curent readtable:
3271     3995  
3272 (define (read-square-brackets ch port src line col pos)   3996 (define (read-square-brackets ch port src line col pos)
3273   `(fn (_)   3997   `(fn (_)
3274      ,(read/recursive port #\[ #f)))   3998      ,(read/recursive port #\[ #f)))
3275      3999   
3276 ; a readtable that is just like the builtin except for []s   4000 ; a readtable that is just like the builtin except for []s
3277     4001  
3278 (define bracket-readtable   4002 (define bracket-readtable
3279   (make-readtable #f #\[ 'terminating-macro read-square-brackets))   4003   (make-readtable #f #\[ 'terminating-macro read-square-brackets))
3280      4004   
3281 ; call this to set the global readtable   4005 ; call this to set the global readtable
3282     4006  
3283 (provide use-bracket-readtable)   4007 (provide use-bracket-readtable)
3284     4008  
3285 (define (use-bracket-readtable)   4009 (define (use-bracket-readtable)
3286   (current-readtable bracket-readtable))   4010   (current-readtable bracket-readtable))
3287      4011   
3288 ; these two implement the required functionality for #reader   4012 ; these two implement the required functionality for #reader
3289        4013     
3290 ;(define (*read inp)   4014 ;(define (*read inp)
3291 ;  (parameterize ((current-readtable bracket-readtable))   4015 ;  (parameterize ((current-readtable bracket-readtable))
3292 ;    (read inp)))   4016 ;    (read inp)))
3293     4017  
3294 (define (*read . args)   4018 (define (*read . args)
3295   (parameterize ((current-readtable bracket-readtable))   4019   (parameterize ((current-readtable bracket-readtable))
3296     (read (if (null? args) (current-input-port) (car args)))))   4020     (read (if (null? args) (current-input-port) (car args)))))
3297     4021  
3298 (define (*read-syntax src port)   4022 (define (*read-syntax src port)
3299   (parameterize ((current-readtable bracket-readtable))   4023   (parameterize ((current-readtable bracket-readtable))
3300     (read-syntax src port)))   4024     (read-syntax src port)))
3301     4025  
3302 ; and the need to be provided as `read' and `read-syntax'   4026 ; and the need to be provided as `read' and `read-syntax'
3303     4027  
3304 (provide (rename *read read) (rename *read-syntax read-syntax))   4028 (provide (rename *read read) (rename *read-syntax read-syntax))
3305     4029  
3306 )   4030 )
3307     4031  
3308 ./code.arc   4032 ./code.arc
3309 ; Code analysis. Spun off 21 Dec 07.   4033 ; Code analysis. Spun off 21 Dec 07.
3310     4034  
3311 ; Ought to do more of this in Arc.  One of the biggest advantages   4035 ; Ought to do more of this in Arc.  One of the biggest advantages
3312 ; of Lisp is messing with code.   4036 ; of Lisp is messing with code.
3313     4037  
3314 (def codelines (file)   4038 (def codelines (file)
3315   (w/infile in file   4039   (w/infile in file
3316     (summing test   4040     (summing test
3317       (whilet line (readline in)   4041       (whilet line (readline in)
3318         (test (aand (pos nonwhite line) (isnt it #\;))))))) <> 4042         (test (aand (find nonwhite line) (isnt it #\;)))))))
3319   = 4043  
3320 (def codeflat (file)   4044 (def codeflat (file)
3321   (len (flat (readall (infile file)))))   4045   (len (flat (readall (infile file)))))
3322     4046  
3323 (def codetree (file)   4047 (def codetree (file)
3324   (trav + (fn (x) 1) (readall (infile file)))) <> 4048   (treewise + (fn (x) 1) (readall (infile file))))
3325   = 4049  
3326 (def code-density (file)   4050 (def code-density (file)
3327   (/ (codetree file) (codelines file)))    4051   (/ (codetree file) (codelines file))) 
3328     4052  
3329 (def tokcount (files)   4053 (def tokcount (files)
3330   (let counts (table)   4054   (let counts (table)
3331     (each f files   4055     (each f files
3332       (each token (flat (readall (infile f)))   4056       (each token (flat (readall (infile f)))
3333         (= (counts token) <>    
3334            (+ 1 (or (counts token) 0)))))   4057         (++ (counts token 0))))
3335     counts)) = 4058     counts))
3336     4059  
3337 (def common-tokens (files)   4060 (def common-tokens (files)
3338   (let counts (tokcount files)   4061   (let counts (tokcount files)
3339     (let ranking nil   4062     (let ranking nil
3340       (maptable (fn (k v)   4063       (maptable (fn (k v)
3341                   (unless (nonop k)   4064                   (unless (nonop k)
3342                     (insort (compare > cadr) (list k v) ranking)))   4065                     (insort (compare > cadr) (list k v) ranking)))
3343                 counts)   4066                 counts)
3344       ranking)))   4067       ranking)))
3345     4068  
3346 (def nonop (x)   4069 (def nonop (x)
3347   (in x 'quote 'unquote 'quasiquote 'unquote-splicing))   4070   (in x 'quote 'unquote 'quasiquote 'unquote-splicing))
3348     4071  
3349 (def common-operators (files)   4072 (def common-operators (files)
3350   (keep [and (isa (car _) 'sym) (bound (car _))] (common-tokens files)))   4073   (keep [and (isa (car _) 'sym) (bound (car _))] (common-tokens files)))
3351     4074  
3352 (def top40 (xs)   4075 (def top40 (xs)
3353   (map prn (firstn 40 xs))   4076   (map prn (firstn 40 xs))
3354   t)   4077   t)
3355     4078  
3356 (def space-eaters (files)   4079 (def space-eaters (files)
3357   (let counts (tokcount files)   4080   (let counts (tokcount files)
3358     (let ranking nil   4081     (let ranking nil
3359       (maptable (fn (k v)   4082       (maptable (fn (k v)
3360                   (when (and (isa k 'sym) (bound k))   4083                   (when (and (isa k 'sym) (bound k))
3361                     (insort (compare > [* (len (coerce (car _) 'string)) <> 4084                     (insort (compare > [* (len (string (car _)))
3362                                           (cadr _)]) = 4085                                           (cadr _)])
3363                             (list k v (* (len (string k)) v))   4086                             (list k v (* (len (string k)) v))
3364                             ranking)))   4087                             ranking)))
3365                 counts)   4088                 counts)
3366     ranking)))   4089     ranking)))
3367     4090  
3368 ;(top40 (space-eaters allfiles*))   4091 ;(top40 (space-eaters allfiles*))
3369     4092  
    -+ 4093 (mac flatlen args `(len (flat ',args)))
3370   = 4094  
3371 ./html.arc   4095 ./html.arc
3372 ; HTML Utils.    4096 ; HTML Utils. 
3373     4097  
3374     4098  
3375 (def color (r g b)   4099 (def color (r g b)
3376   (with (c (table)    4100   (with (c (table) 
3377          f (fn (x) (if (< x 0) 0 (> x 255) 255 x)))   4101          f (fn (x) (if (< x 0) 0 (> x 255) 255 x)))
3378     (= (c 'r) (f r) (c 'g) (f g) (c 'b) (f b))   4102     (= (c 'r) (f r) (c 'g) (f g) (c 'b) (f b))
3379     c))   4103     c))
3380     4104  
3381 (def dehex (str) (errsafe (coerce str 'int 16)))   4105 (def dehex (str) (errsafe (coerce str 'int 16)))
3382     4106  
3383 (defmemo hex>color (str)   4107 (defmemo hex>color (str)
3384   (and (is (len str) 6)   4108   (and (is (len str) 6)
3385        (with (r (dehex (subseq str 0 2)) <> 4109        (with (r (dehex (cut str 0 2))
3386               g (dehex (subseq str 2 4))   4110               g (dehex (cut str 2 4))
3387               b (dehex (subseq str 4 6)))   4111               b (dehex (cut str 4 6)))
3388          (and r g b = 4112          (and r g b
3389               (color r g b)))))   4113               (color r g b)))))
3390     4114  
3391 (defmemo gray (n) (color n n n))   4115 (defmemo gray (n) (color n n n))
3392     4116  
3393 (= white    (gray 255)    4117 (= white    (gray 255) 
3394    black    (gray 0)   4118    black    (gray 0)
3395    linkblue (color 0 0 190)) <> 4119    linkblue (color 0 0 190)
      4120    orange   (color 255 102 0)
      4121    darkred  (color 180 0 0)
      4122    darkblue (color 0 0 120)
      4123    )
3396   = 4124  
3397 (= opmeths* (table))   4125 (= opmeths* (table))
3398     4126  
3399 ; hack: intern key pair till have implicit tables of tables <>    
3400        
3401 (mac opmeth (tag opt)   4127 (mac opmeth args
3402   `(opmeths* (sym (+ (coerce ,tag 'string) "."  (coerce ,opt 'string)))))   4128   `(opmeths* (list ,@args)))
3403   = 4129  
3404 (mac attribute (tag opt f)   4130 (mac attribute (tag opt f)
3405 ; `(= (opmeth ',tag ',opt) ,f) <> 4131   `(= (opmeths* (list ',tag ',opt)) ,f))
3406   `(= (opmeths* ',(sym (+ (coerce tag 'string) "."  (coerce opt 'string))))      
3407       ,f))      
3408   = 4132  
3409 (= hexreps (table))   4133 (= hexreps (table))
3410     4134  
3411 (for i 0 255 (= (hexreps i)   4135 (for i 0 255 (= (hexreps i)
3412                 (let s (coerce i 'string 16)   4136                 (let s (coerce i 'string 16)
3413                   (if (is (len s) 1) (+ "0" s) s))))   4137                   (if (is (len s) 1) (+ "0" s) s))))
3414     4138  
3415 (defmemo hexrep (col)   4139 (defmemo hexrep (col)
3416   (+ (hexreps (col 'r)) (hexreps (col 'g)) (hexreps (col 'b))))   4140   (+ (hexreps (col 'r)) (hexreps (col 'g)) (hexreps (col 'b))))
3417     4141  
3418 (def opcolor (key val)    4142 (def opcolor (key val) 
3419   (w/uniq gv   4143   (w/uniq gv
3420     `(whenlet ,gv ,val   4144     `(whenlet ,gv ,val
3421        (pr " " ',key "=#" (hexrep ,gv))))) <> 4145        (pr ,(string " " key "=#") (hexrep ,gv)))))
3422   = 4146  
3423 (def opstring (key val)   4147 (def opstring (key val)
3424   `(aif ,val (pr " " ',key "=\"" it #\"))) <> 4148   `(aif ,val (pr ,(+ " " key "=\"") it #\")))
3425   = 4149  
3426 (def opnum (key val)   4150 (def opnum (key val)
3427   `(aif ,val (pr " " ',key "=" it))) <> 4151   `(aif ,val (pr ,(+ " " key "=") it)))
3428   = 4152  
3429 (def opsym (key val)   4153 (def opsym (key val)
3430   `(pr " " ',key "=" ,val)) <> 4154   `(pr ,(+ " " key "=") ,val))
3431   = 4155  
3432 (def opsel (key val)   4156 (def opsel (key val)
3433   `(if ,val (pr " selected")))   4157   `(if ,val (pr " selected")))
3434     4158  
    -+ 4159 (def opcheck (key val)
      4160   `(if ,val (pr " checked")))
      4161  
3435 (def opesc (key val) = 4162 (def opesc (key val)
3436   `(awhen ,val   4163   `(awhen ,val
3437      (pr " " ',key "=\"") <> 4164      (pr ,(string " " key "=\""))
3438      (if (isa it 'string) (pr-escaped it) (pr it)) = 4165      (if (isa it 'string) (pr-escaped it) (pr it))
3439      (pr  #\")))   4166      (pr  #\")))
3440     4167  
3441 ; need to escape more?  =?   4168 ; need to escape more?  =?
3442     4169  
3443 (def pr-escaped (x)   4170 (def pr-escaped (x)
3444   (each c x    4171   (each c x 
3445     (pr (case c #\<  "&#60;"     4172     (pr (case c #\<  "&#60;"  
3446                 #\>  "&#62;"     4173                 #\>  "&#62;"  
3447                 #\"  "&#34;"     4174                 #\"  "&#34;"  
3448                 #\&  "&#38;"   4175                 #\&  "&#38;"
3449                 c))))   4176                 c))))
3450     4177  
3451 (attribute a          href           opstring)   4178 (attribute a          href           opstring)
3452 (attribute a          rel            opstring)   4179 (attribute a          rel            opstring)
3453 (attribute a          class          opstring)   4180 (attribute a          class          opstring)
3454 (attribute a          id             opsym)   4181 (attribute a          id             opsym)
3455 (attribute a          onclick        opstring)   4182 (attribute a          onclick        opstring)
3456 (attribute body       alink          opcolor)   4183 (attribute body       alink          opcolor)
3457 (attribute body       bgcolor        opcolor)   4184 (attribute body       bgcolor        opcolor)
3458 (attribute body       leftmargin     opnum)   4185 (attribute body       leftmargin     opnum)
3459 (attribute body       link           opcolor)   4186 (attribute body       link           opcolor)
3460 (attribute body       marginheight   opnum)   4187 (attribute body       marginheight   opnum)
3461 (attribute body       marginwidth    opnum)   4188 (attribute body       marginwidth    opnum)
3462 (attribute body       topmargin      opnum)   4189 (attribute body       topmargin      opnum)
3463 (attribute body       vlink          opcolor)   4190 (attribute body       vlink          opcolor)
3464 (attribute font       color          opcolor)   4191 (attribute font       color          opcolor)
3465 (attribute font       face           opstring)   4192 (attribute font       face           opstring)
3466 (attribute font       size           opnum)   4193 (attribute font       size           opnum)
3467 (attribute form       action         opstring)   4194 (attribute form       action         opstring)
3468 (attribute form       method         opsym)   4195 (attribute form       method         opsym)
3469 (attribute img        align          opsym)   4196 (attribute img        align          opsym)
3470 (attribute img        border         opnum)   4197 (attribute img        border         opnum)
3471 (attribute img        height         opnum)   4198 (attribute img        height         opnum)
3472 (attribute img        width          opnum)   4199 (attribute img        width          opnum)
3473 (attribute img        vspace         opnum)   4200 (attribute img        vspace         opnum)
3474 (attribute img        hspace         opnum)   4201 (attribute img        hspace         opnum)
3475 (attribute img        src            opstring)   4202 (attribute img        src            opstring)
3476 (attribute input      name           opstring)   4203 (attribute input      name           opstring)
3477 (attribute input      size           opnum)   4204 (attribute input      size           opnum)
3478 (attribute input      type           opsym)   4205 (attribute input      type           opsym)
3479 (attribute input      value          opesc)   4206 (attribute input      value          opesc)
3480 (attribute option     selected       opsel) <> 4207 (attribute input      checked        opcheck)
3481 (attribute select     name           opstring) = 4208 (attribute select     name           opstring)
    -+ 4209 (attribute option     selected       opsel)
3482 (attribute table      bgcolor        opcolor) = 4210 (attribute table      bgcolor        opcolor)
3483 (attribute table      border         opnum)   4211 (attribute table      border         opnum)
3484 (attribute table      cellpadding    opnum)   4212 (attribute table      cellpadding    opnum)
3485 (attribute table      cellspacing    opnum)   4213 (attribute table      cellspacing    opnum)
3486 (attribute table      width          opstring)   4214 (attribute table      width          opstring)
3487 (attribute textarea   cols           opnum)   4215 (attribute textarea   cols           opnum)
3488 (attribute textarea   name           opstring)   4216 (attribute textarea   name           opstring)
3489 (attribute textarea   rows           opnum)   4217 (attribute textarea   rows           opnum)
3490 (attribute textarea   wrap           opsym)   4218 (attribute textarea   wrap           opsym)
3491 (attribute td         align          opsym)   4219 (attribute td         align          opsym)
3492 (attribute td         bgcolor        opcolor)   4220 (attribute td         bgcolor        opcolor)
3493 (attribute td         colspan        opnum)   4221 (attribute td         colspan        opnum)
3494 (attribute td         width          opnum)   4222 (attribute td         width          opnum)
3495 (attribute td         valign         opsym)   4223 (attribute td         valign         opsym)
3496 (attribute td         class          opstring)   4224 (attribute td         class          opstring)
3497 (attribute tr         bgcolor        opcolor)   4225 (attribute tr         bgcolor        opcolor)
3498 (attribute hr         color          opcolor)   4226 (attribute hr         color          opcolor)
3499 (attribute span       class          opstring)   4227 (attribute span       class          opstring)
3500 (attribute span       align          opstring)   4228 (attribute span       align          opstring)
3501 (attribute span       id             opsym)   4229 (attribute span       id             opsym)
3502 (attribute rss        version        opstring)   4230 (attribute rss        version        opstring)
3503     4231  
3504     4232  
3505 (mac gentag args (start-tag args))   4233 (mac gentag args (start-tag args))
3506         4234      
3507 (mac tag (spec . body)   4235 (mac tag (spec . body)
3508   `(do ,(start-tag spec)   4236   `(do ,(start-tag spec)
3509        ,@body   4237        ,@body
3510        ,(end-tag spec)))   4238        ,(end-tag spec)))
3511         4239      
3512 (mac tag-if (test spec . body)   4240 (mac tag-if (test spec . body)
3513   `(if ,test   4241   `(if ,test
3514        (tag ,spec ,@body)   4242        (tag ,spec ,@body)
3515        (do ,@body)))   4243        (do ,@body)))
3516     4244  
3517 (def start-tag (spec)   4245 (def start-tag (spec)
3518   (if (atom spec)   4246   (if (atom spec)
3519       `(pr "<" ',spec ">") <> 4247       `(pr ,(string "<" spec ">"))
3520       `(do (pr "<" ',(car spec))      
3521            ,@(tag-options (car spec) (pair (cdr spec)))   4248       (let opts (tag-options (car spec) (pair (cdr spec)))
      4249         (if (all [isa _ 'string] opts)
      4250             `(pr ,(string "<" (car spec) (apply string opts) ">"))
      4251             `(do (pr ,(string "<" (car spec)))
      4252                  ,@(map (fn (opt)
      4253                           (if (isa opt 'string)
      4254                               `(pr ,opt)
      4255                               opt))
      4256                         opts)
3522            (pr ">"))))   4257                  (pr ">"))))))
3523            4258  
3524 (def end-tag (spec) = 4259 (def end-tag (spec)
3525   `(pr "</" ',(carif spec) ">")) <> 4260   `(pr ,(string "</" (carif spec) ">")))
3526   = 4261  
    -+ 4262 (def literal (x) 
      4263   (case (type x)
      4264     sym   (in x nil t)
      4265     cons  (caris x 'quote)
      4266           t))
      4267  
      4268 ; Returns a list whose elements are either strings, which can 
      4269 ; simply be printed out, or expressions, which when evaluated
      4270 ; generate output.
      4271  
3527 (def tag-options (spec options) = 4272 (def tag-options (spec options)
3528   (if (no options)   4273   (if (no options)
3529       '()   4274       '()
3530       (let ((opt val) . rest) options   4275       (let ((opt val) . rest) options
3531         (let meth (if (is opt 'style) opstring (opmeth spec opt))   4276         (let meth (if (is opt 'style) opstring (opmeth spec opt))
3532           (if meth   4277           (if meth
3533               (if val   4278               (if val
    <> 4279                   (cons (if (precomputable-tagopt val)
      4280                             (tostring (eval (meth opt val)))
3534                   (cons (meth opt val)   4281                             (meth opt val))
3535                         (tag-options spec rest)) = 4282                         (tag-options spec rest))
3536                   (tag-options spec rest))   4283                   (tag-options spec rest))
3537               (do   4284               (do
3538                 (pr "<!-- ignoring " opt " for " spec "-->")   4285                 (pr "<!-- ignoring " opt " for " spec "-->")
3539                 (tag-options spec rest)))))))   4286                 (tag-options spec rest)))))))
    -+ 4287  
      4288 (def precomputable-tagopt (val)
      4289   (and (literal val) 
      4290        (no (and (is (type val) 'string) (find #\@ val)))))
3540   = 4291  
3541 (def br ((o n 1))    4292 (def br ((o n 1)) 
3542   (repeat n (pr "<br>"))    4293   (repeat n (pr "<br>")) 
3543   (prn))   4294   (prn))
3544     4295  
3545 (def br2 () (prn "<br><br>"))   4296 (def br2 () (prn "<br><br>"))
3546     4297  
3547 (mac center    body         `(tag center ,@body))   4298 (mac center    body         `(tag center ,@body))
3548 (mac underline body         `(tag u ,@body))   4299 (mac underline body         `(tag u ,@body))
3549 (mac tab       body         `(tag (table border 0) ,@body))   4300 (mac tab       body         `(tag (table border 0) ,@body))
3550 (mac tr        body         `(tag tr ,@body))   4301 (mac tr        body         `(tag tr ,@body))
3551     4302  
3552 (let pratoms (fn (body)   4303 (let pratoms (fn (body)
3553                (if (or (no body)    4304                (if (or (no body) 
3554                        (all [and (acons _) (isnt (car _) 'quote)]   4305                        (all [and (acons _) (isnt (car _) 'quote)]
3555                             body))   4306                             body))
3556                    body   4307                    body
3557                    `((pr ,@body))))   4308                    `((pr ,@body))))
3558     4309  
3559   (mac td       body         `(tag td ,@(pratoms body)))   4310   (mac td       body         `(tag td ,@(pratoms body)))
3560   (mac trtd     body         `(tr (td ,@(pratoms body))))   4311   (mac trtd     body         `(tr (td ,@(pratoms body))))
3561   (mac tdright  body         `(tag (td align 'right) ,@(pratoms body))) <> 4312   (mac tdr      body         `(tag (td align 'right) ,@(pratoms body)))
3562   (mac tdcolor  (col . body) `(tag (td bgcolor ,col) ,@(pratoms body))) = 4313   (mac tdcolor  (col . body) `(tag (td bgcolor ,col) ,@(pratoms body)))
3563 )   4314 )
3564     4315  
3565 (mac row args   4316 (mac row args
3566   `(tr ,@(map [list 'td _] args)))   4317   `(tr ,@(map [list 'td _] args)))
3567     4318  
3568 (mac prrow args   4319 (mac prrow args
3569   (w/uniq g   4320   (w/uniq g
3570     `(tr ,@(map (fn (a)    4321     `(tr ,@(map (fn (a) 
3571                   `(let ,g ,a   4322                   `(let ,g ,a
3572                      (if (number ,g)   4323                      (if (number ,g)
3573                          (tdright (pr ,g)) <> 4324                          (tdr (pr ,g))
3574                          (td (pr ,g))))) = 4325                          (td (pr ,g)))))
3575                  args))))   4326                  args))))
3576     4327  
3577 (mac prbold body `(tag b (pr ,@body)))   4328 (mac prbold body `(tag b (pr ,@body)))
3578     4329  
    <> 4330 (def para args 
      4331   (gentag p)
3579 (def para () (gentag p))   4332   (when args (apply pr args)))
3580   = 4333  
3581 (def menu (name items (o sel nil))   4334 (def menu (name items (o sel nil))
3582   (tag (select name name)   4335   (tag (select name name)
3583     (each i items   4336     (each i items
3584       (tag (option selected (is i sel))   4337       (tag (option selected (is i sel))
3585         (pr i)))))   4338         (pr i)))))
3586     4339  
3587 (mac whitepage body   4340 (mac whitepage body
3588   `(tag html    4341   `(tag html 
3589      (tag (body bgcolor white alink linkblue) ,@body)))   4342      (tag (body bgcolor white alink linkblue) ,@body)))
3590     4343  
3591 (def errpage args (whitepage (apply prn args)))   4344 (def errpage args (whitepage (apply prn args)))
3592     4345  
3593 (= local-images* nil)   ; set to t when developing offline <>    
3594        
3595 (def img-url (file)       
3596   (string (unless local-images* "http://ycombinator.com/images/") file))      
3597        
3598 (def blank-url ()   4346 (def blank-url () "s.gif")
3599   (if local-images* "s.gif" "http://ycombinator.com/images/s.gif"))      
3600   = 4347  
3601 ; Could memoize these.   4348 ; Could memoize these.
3602     4349  
3603 ; If h = 0, doesn't affect table column widths in some Netscapes.   4350 ; If h = 0, doesn't affect table column widths in some Netscapes.
3604     4351  
3605 (def hspace (n)    (gentag img src (blank-url) height 1 width n))   4352 (def hspace (n)    (gentag img src (blank-url) height 1 width n))
3606 (def vspace (n)    (gentag img src (blank-url) height n width 0))   4353 (def vspace (n)    (gentag img src (blank-url) height n width 0))
3607 (def vhspace (h w) (gentag img src (blank-url) height h width w))   4354 (def vhspace (h w) (gentag img src (blank-url) height h width w))
3608     4355  
3609 (mac new-hspace (n)       4356 (mac new-hspace (n)    
3610   (if (number n)   4357   (if (number n)
3611       `(pr ,(string "<span style=\"padding-left:" n "px\" />"))   4358       `(pr ,(string "<span style=\"padding-left:" n "px\" />"))
3612       `(pr "<span style=\"padding-left:" ,n "px\" />")))   4359       `(pr "<span style=\"padding-left:" ,n "px\" />")))
3613     4360  
3614 ;(def spacerow (h) (tr (td (vspace h))))   4361 ;(def spacerow (h) (tr (td (vspace h))))
3615     4362  
3616 (def spacerow (h) (pr "<tr style=\"height:" h "px\"></tr>"))   4363 (def spacerow (h) (pr "<tr style=\"height:" h "px\"></tr>"))
3617     4364  
3618 ; For use as nested table.   4365 ; For use as nested table.
3619     4366  
3620 (mac zerotable body   4367 (mac zerotable body
3621   `(tag (table border 0 cellpadding 0 cellspacing 0)   4368   `(tag (table border 0 cellpadding 0 cellspacing 0)
3622      ,@body))   4369      ,@body))
3623     4370  
    <> 4371 ; was `(tag (table border 0 cellpadding 0 cellspacing 7) ,@body)
      4372  
3624 (mac spacetable body   4373 (mac sptab body
3625   `(tag (table border 0 cellpadding 0 cellspacing 7) ,@body))   4374   `(tag (table style "border-spacing: 7px 0px;") ,@body))
3626   = 4375  
3627 (mac widtable (w . body)   4376 (mac widtable (w . body)
3628   `(tag (table width ,w) (tr (td ,@body))))   4377   `(tag (table width ,w) (tr (td ,@body))))
3629     4378  
3630 (def cellpr (x) (pr (or x "&nbsp;")))   4379 (def cellpr (x) (pr (or x "&nbsp;")))
3631     4380  
3632 (def but ((o text "submit") (o name nil))   4381 (def but ((o text "submit") (o name nil))
3633   (gentag input type 'submit name name value text))   4382   (gentag input type 'submit name name value text))
3634     4383  
3635 (def submit ((o val "submit"))   4384 (def submit ((o val "submit"))
3636   (gentag input type 'submit value val))   4385   (gentag input type 'submit value val))
3637     4386  
3638 (def buts (name . texts)   4387 (def buts (name . texts)
3639   (if (no texts)   4388   (if (no texts)
3640       (but)   4389       (but)
3641       (do (but (car texts) name)   4390       (do (but (car texts) name)
3642           (each text (cdr texts)   4391           (each text (cdr texts)
3643             (pr " ")   4392             (pr " ")
3644             (but text name)))))   4393             (but text name)))))
3645     4394  
3646 (mac spanrow (n . body)   4395 (mac spanrow (n . body)
3647   `(tr (tag (td colspan ,n) ,@body)))   4396   `(tr (tag (td colspan ,n) ,@body)))
3648     4397  
3649 (mac form (action . body)   4398 (mac form (action . body)
3650   `(tag (form method "post" action ,action) ,@body))   4399   `(tag (form method "post" action ,action) ,@body))
3651     4400  
3652 (mac textarea (name rows cols . body)   4401 (mac textarea (name rows cols . body)
3653   `(tag (textarea name ,name rows ,rows cols ,cols) ,@body))   4402   `(tag (textarea name ,name rows ,rows cols ,cols) ,@body))
3654     4403  
3655 (def input (name (o val "") (o size 10))   4404 (def input (name (o val "") (o size 10))
3656   (gentag input type 'text name name value val size size))   4405   (gentag input type 'text name name value val size size))
3657     4406  
3658 (mac inputs args   4407 (mac inputs args
3659   `(tag (table border 0)   4408   `(tag (table border 0)
3660      ,@(map (fn ((name label len text))   4409      ,@(map (fn ((name label len text))
3661               (w/uniq (gl gt)   4410               (w/uniq (gl gt)
3662                 `(let ,gl ,len   4411                 `(let ,gl ,len
3663                    (tr (td (pr ',label ":"))   4412                    (tr (td (pr ',label ":"))
3664                        (if (isa ,gl 'cons)   4413                        (if (isa ,gl 'cons)
3665                            (td (textarea ',name (car ,gl) (cadr ,gl)   4414                            (td (textarea ',name (car ,gl) (cadr ,gl)
3666                                  (let ,gt ,text (if ,gt (pr ,gt)))))   4415                                  (let ,gt ,text (if ,gt (pr ,gt)))))
3667                            (td (gentag input type ',(if (is label 'password)    4416                            (td (gentag input type ',(if (is label 'password) 
3668                                                     'password    4417                                                     'password 
3669                                                     'text)   4418                                                     'text)
3670                                          name ',name    4419                                          name ',name 
3671                                          size ,len    4420                                          size ,len 
3672                                          value ,text)))))))   4421                                          value ,text)))))))
3673             (tuples args 4))))   4422             (tuples args 4))))
3674     4423  
    -+ 4424 (def single-input (label name chars btext (o pwd))
      4425   (pr label)
      4426   (gentag input type (if pwd 'password 'text) name name size chars)
      4427   (sp)
      4428   (submit btext))
      4429  
      4430 (mac cdata body
      4431   `(do (pr "<![CDATA[") 
      4432        ,@body
      4433        (pr "]]>")))
      4434  
3675 (def eschtml (str) = 4435 (def eschtml (str)
3676   (tostring    4436   (tostring 
3677     (each c str   4437     (each c str
3678       (pr (case c #\<  "&#60;"    4438       (pr (case c #\<  "&#60;" 
3679                   #\>  "&#62;"   4439                   #\>  "&#62;"
3680                   #\"  "&#34;"   4440                   #\"  "&#34;"
3681                   #\'  "&#39;"   4441                   #\'  "&#39;"
3682                   #\&  "&#38;"   4442                   #\&  "&#38;"
3683                         c)))))   4443                         c)))))
3684     4444  
3685 (def esc<>& (str) <> 4445 (def esc-tags (str)
3686   (tostring  = 4446   (tostring 
3687     (each c str   4447     (each c str
3688       (pr (case c #\<  "&#60;"    4448       (pr (case c #\<  "&#60;" 
3689                   #\>  "&#62;"   4449                   #\>  "&#62;"
3690                   #\&  "&#38;"   4450                   #\&  "&#38;"
3691                         c)))))   4451                         c)))))
3692     4452  
3693 (def nbsp () (pr "&nbsp;"))   4453 (def nbsp () (pr "&nbsp;"))
3694     4454  
3695 (def link (text (o dest text) (o color))   4455 (def link (text (o dest text) (o color))
3696   (tag (a href dest)    4456   (tag (a href dest) 
3697     (tag-if color (font color color)   4457     (tag-if color (font color color)
3698       (pr text))))   4458       (pr text))))
3699     4459  
3700 (def underlink (text (o dest text))   4460 (def underlink (text (o dest text))
3701   (tag (a href dest) (tag u (pr text))))   4461   (tag (a href dest) (tag u (pr text))))
3702     4462  
3703 (def striptags (s)   4463 (def striptags (s)
3704   (let intag nil   4464   (let intag nil
3705     (tostring   4465     (tostring
3706       (each c s   4466       (each c s
3707         (if (is c #\<) (t! intag) <> 4467         (if (is c #\<) (set intag)
3708             (is c #\>) (nil! intag)   4468             (is c #\>) (wipe intag)
3709             (no intag) (pr c)))))) = 4469             (no intag) (pr c))))))
3710     4470  
    -+ 4471 (def clean-url (u)
      4472   (rem [in _ #\" #\' #\< #\>] u))
      4473  
3711 (def shortlink (url) = 4474 (def shortlink (url)
3712   (unless (or (no url) (< (len url) 7))   4475   (unless (or (no url) (< (len url) 7))
3713     (link (subseq url 7) url))) <> 4476     (link (cut url 7) url)))
3714   = 4477  
3715 ; this should be one regexp   4478 ; this should be one regexp
3716     4479  
3717 (def parafy (str)   4480 (def parafy (str)
3718   (let ink nil   4481   (let ink nil
3719     (tostring   4482     (tostring
3720       (each c str   4483       (each c str
3721         (pr c)   4484         (pr c)
3722         (unless (whitec c) (t! ink)) <> 4485         (unless (whitec c) (set ink))
3723         (when (is c #\newline) = 4486         (when (is c #\newline)
3724           (unless ink (pr "<p>"))   4487           (unless ink (pr "<p>"))
3725           (nil! ink)))))) <> 4488           (wipe ink))))))
3726   = 4489  
3727 (mac spanclass (name . body)   4490 (mac spanclass (name . body)
3728   `(tag (span class ',name) ,@body))   4491   `(tag (span class ',name) ,@body))
3729     4492  
3730 (def pagemessage (text)   4493 (def pagemessage (text)
3731   (when text (prn text) (br2)))   4494   (when text (prn text) (br2)))
    -+ 4495  
      4496 ; Could be stricter.  Memoized because looking for chars in Unicode
      4497 ; strings is terribly inefficient in Mzscheme.
      4498  
      4499 (defmemo valid-url (url)
      4500   (and (len> url 10)
      4501        (or (begins url "http://")
      4502            (begins url "https://"))
      4503        (~find [in _ #\< #\> #\" #\'] url)))
      4504  
      4505 (mac fontcolor (c . body)
      4506   (w/uniq g
      4507     `(let ,g ,c
      4508        (if ,g
      4509            (tag (font color ,g) ,@body)
      4510            (do ,@body)))))
3732   = 4511  
3733 ./libs.arc   4512 ./libs.arc
3734 (map load '("strings.arc"   4513 (map load '("strings.arc"
3735             "pprint.arc"   4514             "pprint.arc"
3736             "code.arc"   4515             "code.arc"
3737             "html.arc"   4516             "html.arc"
3738             "srv.arc"   4517             "srv.arc"
3739             "app.arc"   4518             "app.arc"
3740             "prompt.arc"))   4519             "prompt.arc"))
3741     4520  
3742 ./pprint.arc   4521 ./pprint.arc
3743 ; Pretty-Printing.  Spun off 4 Aug 06.   4522 ; Pretty-Printing.  Spun off 4 Aug 06.
3744     4523  
3745 ; todo: indentation of long ifs; quasiquote, unquote, unquote-splicing   4524 ; todo: indentation of long ifs; quasiquote, unquote, unquote-splicing
3746               4525            
3747 (= bodops* (fill-table (table)   4526 (= bodops* (fill-table (table)
3748    '(let 2 with 1 while 1 def 2 fn 1 rfn 2 afn 1   4527    '(let 2 with 1 while 1 def 2 fn 1 rfn 2 afn 1
3749      when 1 unless 1 after 1 whilet 2 for 3 each 2 whenlet 2 awhen 1   4528      when 1 unless 1 after 1 whilet 2 for 3 each 2 whenlet 2 awhen 1
3750      whitepage 0 tag 1 form 1 aform 1 aformh 1 w/link 1 textarea 3   4529      whitepage 0 tag 1 form 1 aform 1 aformh 1 w/link 1 textarea 3
3751    )))   4530    )))
3752     4531  
3753 (= oneline* 35) ; print exprs less than this long on one line   4532 (= oneline* 35) ; print exprs less than this long on one line
3754     4533  
3755 ; If returns nil, can assume it didn't have to break expr.   4534 ; If returns nil, can assume it didn't have to break expr.
3756      4535   
3757 (def ppr (expr (o col 0) (o noindent nil))   4536 (def ppr (expr (o col 0) (o noindent nil))
3758   (if (or (atom expr) (dotted expr))   4537   (if (or (atom expr) (dotted expr))
3759        (do (unless noindent (sp col))   4538        (do (unless noindent (sp col))
3760            (write expr)   4539            (write expr)
3761            nil)   4540            nil)
3762       (is (car expr) 'quote)   4541       (is (car expr) 'quote)
3763        (do (unless noindent (sp col))   4542        (do (unless noindent (sp col))
3764            (pr "'")   4543            (pr "'")
3765            (ppr (cadr expr) (+ col 1) t))   4544            (ppr (cadr expr) (+ col 1) t))
3766       (bodops* (car expr))   4545       (bodops* (car expr))
3767        (do (unless noindent (sp col))   4546        (do (unless noindent (sp col))
3768            (let whole (tostring (write expr))   4547            (let whole (tostring (write expr))
3769              (if (< (len whole) oneline*)   4548              (if (< (len whole) oneline*)
3770                  (do (pr whole) nil)   4549                  (do (pr whole) nil)
3771                  (ppr-progn expr col noindent))))   4550                  (ppr-progn expr col noindent))))
3772       (do (unless noindent (sp col))   4551       (do (unless noindent (sp col))
3773           (let whole (tostring (write expr))   4552           (let whole (tostring (write expr))
3774             (if (< (len whole) oneline*)   4553             (if (< (len whole) oneline*)
3775                 (do (pr whole) nil)   4554                 (do (pr whole) nil)
3776                 (ppr-call expr col noindent))))))   4555                 (ppr-call expr col noindent))))))
3777     4556  
3778 (def ppr-progn (expr col noindent)   4557 (def ppr-progn (expr col noindent)
3779   (lpar)   4558   (lpar)
3780   (let n (bodops* (car expr))   4559   (let n (bodops* (car expr))
3781     (let str (tostring (write-spaced (firstn n expr)))   4560     (let str (tostring (write-spaced (firstn n expr)))
3782       (unless (is n 0) (pr str) (sp))   4561       (unless (is n 0) (pr str) (sp))
3783       (ppr (expr n) (+ col (len str) 2) t))   4562       (ppr (expr n) (+ col (len str) 2) t))
3784     (map (fn (e) (prn) (ppr e (+ col 2)))   4563     (map (fn (e) (prn) (ppr e (+ col 2)))
3785          (nthcdr (+ n 1) expr)))   4564          (nthcdr (+ n 1) expr)))
3786   (rpar)   4565   (rpar)
3787   t)   4566   t)
3788                 4567              
3789 (def ppr-call (expr col noindent)   4568 (def ppr-call (expr col noindent)
3790   (lpar)   4569   (lpar)
3791   (let carstr (tostring (write (car expr)))   4570   (let carstr (tostring (write (car expr)))
3792     (pr carstr)   4571     (pr carstr)
3793     (if (cdr expr)   4572     (if (cdr expr)
3794         (do (sp)   4573         (do (sp)
3795             (let broke (ppr (cadr expr) (+ col (len carstr) 2) t)   4574             (let broke (ppr (cadr expr) (+ col (len carstr) 2) t)
3796               (pprest (cddr expr)   4575               (pprest (cddr expr)
3797                       (+ col (len carstr) 2)   4576                       (+ col (len carstr) 2)
3798                       (no broke)))   4577                       (no broke)))
3799             t)   4578             t)
3800         (do (rpar) t))))   4579         (do (rpar) t))))
3801           4580        
3802 (def pprest (exprs col (o oneline t))   4581 (def pprest (exprs col (o oneline t))
3803   (if (and oneline   4582   (if (and oneline
3804            (all (fn (e)   4583            (all (fn (e)
3805                   (or (atom e) (and (is (car e) 'quote) (atom (cadr e)))))   4584                   (or (atom e) (and (is (car e) 'quote) (atom (cadr e)))))
3806                 exprs))   4585                 exprs))
3807       (do (map (fn (e) (pr " ") (write e))   4586       (do (map (fn (e) (pr " ") (write e))
3808                exprs)   4587                exprs)
3809           (rpar))   4588           (rpar))
3810       (do (when exprs   4589       (do (when exprs
3811             (each e exprs (prn) (ppr e col)))   4590             (each e exprs (prn) (ppr e col)))
3812           (rpar))))   4591           (rpar))))
3813                    4592                 
3814 (def write-spaced (xs)   4593 (def write-spaced (xs)
3815   (when xs   4594   (when xs
3816     (write (car xs))   4595     (write (car xs))
3817     (each x (cdr xs) (pr " ") (write x))))   4596     (each x (cdr xs) (pr " ") (write x))))
3818      4597   
3819 (def sp ((o n 1)) (repeat n (pr " ")))   4598 (def sp ((o n 1)) (repeat n (pr " ")))
3820 (def lpar () (pr "("))   4599 (def lpar () (pr "("))
3821 (def rpar () (pr ")"))   4600 (def rpar () (pr ")"))
3822     4601  
3823     4602  
3824 ./prompt.arc   4603 ./prompt.arc
3825 ; Prompt: Web-based programming application.  4 Aug 06.   4604 ; Prompt: Web-based programming application.  4 Aug 06.
3826     4605  
3827 (= appdir* "arc/apps/")   4606 (= appdir* "arc/apps/")
3828     4607  
3829 (defop prompt req    4608 (defop prompt req 
3830   (let user (get-user req)   4609   (let user (get-user req)
3831     (if (admin user)   4610     (if (admin user)
3832         (prompt-page user)   4611         (prompt-page user)
3833         (pr "Sorry."))))   4612         (pr "Sorry."))))
3834     4613  
3835 (def prompt-page (user . msg)   4614 (def prompt-page (user . msg)
3836   (ensure-dir appdir*)   4615   (ensure-dir appdir*)
3837   (ensure-dir (string appdir* user))   4616   (ensure-dir (string appdir* user))
3838   (whitepage   4617   (whitepage
3839     (prbold "Prompt")   4618     (prbold "Prompt")
3840     (hspace 20)   4619     (hspace 20)
3841     (pr user " | ")   4620     (pr user " | ")
3842     (link "logout")   4621     (link "logout")
3843     (when msg (hspace 10) (apply pr msg))   4622     (when msg (hspace 10) (apply pr msg))
3844     (br2)   4623     (br2)
3845     (tag (table border 0 cellspacing 10)   4624     (tag (table border 0 cellspacing 10)
3846       (each app (dir (+ appdir* user))   4625       (each app (dir (+ appdir* user))
3847         (tr (td app)   4626         (tr (td app)
3848             (td (userlink user 'edit   (edit-app user app))) <> 4627             (td (ulink user 'edit   (edit-app user app)))
3849             (td (userlink user 'run    (run-app  user app)))   4628             (td (ulink user 'run    (run-app  user app)))
3850             (td (hspace 40) = 4629             (td (hspace 40)
3851                 (userlink user 'delete (rem-app  user app)))))) <> 4630                 (ulink user 'delete (rem-app  user app))))))
3852     (br2) = 4631     (br2)
3853     (aform (fn (req)   4632     (aform (fn (req)
3854              (when-usermatch user req <> 4633              (when-umatch user req
3855                (aif (goodname (arg req "app")) = 4634                (aif (goodname (arg req "app"))
3856                     (edit-app user it)   4635                     (edit-app user it)
3857                     (prompt-page user "Bad name."))))   4636                     (prompt-page user "Bad name."))))
3858        (tab (row "name:" (input "app") (submit "create app"))))))   4637        (tab (row "name:" (input "app") (submit "create app"))))))
3859     4638  
3860 (def app-path (user app)    4639 (def app-path (user app) 
3861   (and user app (+ appdir* user "/" app)))   4640   (and user app (+ appdir* user "/" app)))
3862     4641  
3863 (def read-app (user app)   4642 (def read-app (user app)
3864   (aand (app-path user app)    4643   (aand (app-path user app) 
3865         (file-exists it)   4644         (file-exists it)
3866         (w/infile i it (readall i)))) <> 4645         (readfile it)))
3867   = 4646  
3868 (def write-app (user app exprs)   4647 (def write-app (user app exprs)
3869   (awhen (app-path user app)   4648   (awhen (app-path user app)
3870     (w/outfile o it    4649     (w/outfile o it 
3871       (each e exprs (write e o)))))   4650       (each e exprs (write e o)))))
3872     4651  
3873 (def rem-app (user app)   4652 (def rem-app (user app)
3874   (let file (app-path user app)   4653   (let file (app-path user app)
3875     (if (file-exists file)   4654     (if (file-exists file)
3876         (do (rmfile (app-path user app))   4655         (do (rmfile (app-path user app))
3877             (prompt-page user "Program " app " deleted."))   4656             (prompt-page user "Program " app " deleted."))
3878         (prompt-page user "No such app."))))   4657         (prompt-page user "No such app."))))
3879     4658  
3880 (def edit-app (user app)   4659 (def edit-app (user app)
3881   (whitepage   4660   (whitepage
3882     (pr "user: " user " app: " app)   4661     (pr "user: " user " app: " app)
3883     (br2)   4662     (br2)
3884     (aform (fn (req)   4663     (aform (fn (req)
3885              (let u2 (get-user req)   4664              (let u2 (get-user req)
3886                (if (is u2 user)   4665                (if (is u2 user)
3887                    (do (when (is (arg req "cmd") "save")   4666                    (do (when (is (arg req "cmd") "save")
3888                          (write-app user app (readall (arg req "exprs"))))   4667                          (write-app user app (readall (arg req "exprs"))))
3889                        (prompt-page user))   4668                        (prompt-page user))
3890                    (login-page 'both nil   4669                    (login-page 'both nil
3891                                (fn (u ip) (prompt-page u))))))   4670                                (fn (u ip) (prompt-page u))))))
3892       (textarea "exprs" 10 82   4671       (textarea "exprs" 10 82
3893         (pprcode (read-app user app)))   4672         (pprcode (read-app user app)))
3894       (br2)   4673       (br2)
3895       (buts 'cmd "save" "cancel"))))   4674       (buts 'cmd "save" "cancel"))))
3896     4675  
3897 (def pprcode (exprs)    4676 (def pprcode (exprs) 
3898   (each e exprs   4677   (each e exprs
3899     (ppr e)    4678     (ppr e) 
3900     (pr "\n\n")))   4679     (pr "\n\n")))
3901     4680  
3902 (def view-app (user app)   4681 (def view-app (user app)
3903   (whitepage   4682   (whitepage
3904     (pr "user: " user " app: " app)   4683     (pr "user: " user " app: " app)
3905     (br2)   4684     (br2)
3906     (tag xmp (pprcode (read-app user app)))))   4685     (tag xmp (pprcode (read-app user app)))))
3907     4686  
3908 (def run-app (user app)   4687 (def run-app (user app)
3909   (let exprs (read-app user app)   4688   (let exprs (read-app user app)
3910     (if exprs    4689     (if exprs 
3911         (on-err (fn (c) (pr "Error: " (details c)))   4690         (on-err (fn (c) (pr "Error: " (details c)))
3912           (fn () (map eval exprs)))   4691           (fn () (map eval exprs)))
3913         (prompt-page user "Error: No application " app " for user " user))))   4692         (prompt-page user "Error: No application " app " for user " user))))
3914     4693  
3915 (nil! repl-history*) <> 4694 (wipe repl-history*)
3916   = 4695  
3917 (defop repl req   4696 (defop repl req
3918   (if (admin (get-user req))   4697   (if (admin (get-user req))
3919       (replpage req)   4698       (replpage req)
3920       (pr "Sorry.")))   4699       (pr "Sorry.")))
3921     4700  
3922 (def replpage (req)   4701 (def replpage (req)
3923   (whitepage   4702   (whitepage
3924     (repl (readall (or (arg req "expr") "")) "repl")))   4703     (repl (readall (or (arg req "expr") "")) "repl")))
3925     4704  
3926 (def repl (exprs url)   4705 (def repl (exprs url)
3927     (each expr exprs    4706     (each expr exprs 
3928       (on-err (fn (c) (push (list expr c t) repl-history*))   4707       (on-err (fn (c) (push (list expr c t) repl-history*))
3929               (fn ()    4708               (fn () 
3930                 (= that (eval expr) thatexpr expr)   4709                 (= that (eval expr) thatexpr expr)
3931                 (push (list expr that) repl-history*))))   4710                 (push (list expr that) repl-history*))))
3932     (form url   4711     (form url
3933       (textarea "expr" 8 60)   4712       (textarea "expr" 8 60)
3934       (sp)    4713       (sp) 
3935       (submit))   4714       (submit))
3936     (tag xmp   4715     (tag xmp
3937       (each (expr val err) (firstn 20 repl-history*)   4716       (each (expr val err) (firstn 20 repl-history*)
3938         (pr "> ")   4717         (pr "> ")
3939         (ppr expr)   4718         (ppr expr)
3940         (prn)   4719         (prn)
3941         (prn (if err "Error: " "")   4720         (prn (if err "Error: " "")
3942              (ellipsize (tostring (write val)) 800)))))   4721              (ellipsize (tostring (write val)) 800)))))
3943     4722  
3944     4723  
3945 ./srv.arc   4724 ./srv.arc
3946 ; (server) then http://tintin.archub.org:8080/foo  <>    
3947     4725 ; HTTP Server.
3948 ; could make form fields that know their value type because of   4726  
3949 ; gensymed names, and so the receiving fn gets args that are not   4727 ; To improve performance with static files, set static-max-age*.
3950 ; strings but parsed values.   4728  
3951     4729 (= arcdir* "arc/" logdir* "arc/logs/" staticdir* "static/")
3952 ; write w/socket      
3953   = 4730  
3954 ; set breaksrv* to t to be able to ^c the server <>    
3955     4731 (= quitsrv* nil breaksrv* nil) 
3956 (= arcdir* "arc/" logdir* "arc/logs/" quitsrv* nil breaksrv* nil)       
3957   = 4732  
3958 (def serve ((o port 8080))   4733 (def serve ((o port 8080))
3959   (nil! quitsrv*) <> 4734   (wipe quitsrv*)
3960   (ensure-install)   4735   (ensure-srvdirs)
      4736   (map [apply new-bgthread _] pending-bgthreads*)
3961   (let s (open-socket port)    4737   (w/socket s port
      4738     (setuid 2) ; XXX switch from root to pg
3962     (prn "ready to serve port " port) ; (flushout)   4739     (prn "ready to serve port " port)
      4740     (flushout)
3963     (= currsock* s) = 4741     (= currsock* s)
3964     (after (while (no quitsrv*)  <> 4742     (until quitsrv*
3965              (if breaksrv*       
3966                  (handle-request s)      
3967                  (errsafe (handle-request s))))   4743       (handle-request s breaksrv*)))
3968            (close s)      
3969            (prn "quit server"))))   4744   (prn "quit server"))
3970   = 4745  
3971 (def serve1 ((o port 8080))   4746 (def serve1 ((o port 8080))
3972   (let s (open-socket port) <> 4747   (w/socket s port (handle-request s t)))
3973     (after (handle-request s) (close s))))   4748  
      4749 (def ensure-srvdirs ()
      4750   (map ensure-dir (list arcdir* logdir* staticdir*)))
3974   = 4751  
3975 (= srv-noisy* nil)   4752 (= srv-noisy* nil)
3976     4753  
3977 ; http requests currently capped at 2 meg by socket-accept   4754 ; http requests currently capped at 2 meg by socket-accept
3978     4755  
3979 ; should threads process requests one at a time? no, then   4756 ; should threads process requests one at a time? no, then
3980 ; a browser that's slow consuming the data could hang the   4757 ; a browser that's slow consuming the data could hang the
3981 ; whole server.   4758 ; whole server.
3982     4759  
3983 ; wait for a connection from a browser and start a thread   4760 ; wait for a connection from a browser and start a thread
3984 ; to handle it. also arrange to kill that thread if it   4761 ; to handle it. also arrange to kill that thread if it
3985 ; has not completed in threadlife* seconds.   4762 ; has not completed in threadlife* seconds.
3986     4763  
3987 (= srvthreads* nil threadlimit* 50 threadlife* 30) <> 4764 (= threadlife* 30  requests* 0  requests/ip* (table) 
3988     4765    throttle-ips* (table)  ignore-ips* (table)  spurned* (table))
3989 ; Could auto-throttle ips, e.g. if one has more than x% of recent requests.   4766  
3990     4767 (def handle-request (s breaksrv)
3991 (= requests* 0 requests/ip* (table) throttle-ips* (table) throttle-time* 60)   4768   (if breaksrv
3992     4769       (handle-request-1 s)
3993 (def handle-request (s (o life threadlife*))   4770       (errsafe (handle-request-1 s))))
3994   (if (< (len (= srvthreads* (rem dead srvthreads*)))    4771  
3995               threadlimit*)   4772 (def handle-request-1 (s)
3996       (with ((i o ip) (socket-accept s))   4773   (let (i o ip) (socket-accept s)
      4774     (if (and (or (ignore-ips* ip) (abusive-ip ip))
      4775              (++ (spurned* ip 0)))
      4776         (force-close i o)
3997         (++ requests*)   4777         (do (++ requests*)
3998         (= (requests/ip* ip) (+ 1 (or (requests/ip* ip) 0)))   4778             (++ (requests/ip* ip 0))
      4779             (with (th1 nil th2 nil)
3999         (let th (thread (fn ()    4780               (= th1 (thread
4000                           (if (throttle-ips* ip) (sleep (rand throttle-time*)))   4781                        (after (handle-request-thread i o ip)
      4782                               (close i o)
4001                           (handle-request-thread i o ip)))   4783                               (kill-thread th2))))
4002           (push th srvthreads*)      
4003           (thread (fn ()   4784               (= th2 (thread
4004                     (sleep life)   4785                        (sleep threadlife*)
      4786                        (unless (dead th1)
4005                     (unless (dead th) (prn "srv thread took too long"))   4787                          (prn "srv thread took too long for " ip))
4006                     (break-thread th)   4788                        (break-thread th1)
      4789                        (force-close i o))))))))
4007                     (close o)   4790  
      4791 ; Returns true if ip has made req-limit* requests in less than
      4792 ; req-window* seconds.  If an ip is throttled, only 1 request is 
      4793 ; allowed per req-window* seconds.  If an ip makes req-limit* 
      4794 ; requests in less than dos-window* seconds, it is a treated as a DoS
      4795 ; attack and put in ignore-ips* (for this server invocation).
4008                     (close i)))))   4796  
      4797 ; To adjust this while running, adjust the req-window* time, not 
      4798 ; req-limit*, because algorithm doesn't enforce decreases in the latter.
4009       (sleep .2)))   4799  
      4800 (= req-times* (table) req-limit* 30 req-window* 10 dos-window* 2)
4010   = 4801  
    -+ 4802 (def abusive-ip (ip)
      4803   (and (only.> (requests/ip* ip) 250)
      4804        (let now (seconds)
      4805          (do1 (if (req-times* ip)
      4806                   (and (>= (qlen (req-times* ip)) 
      4807                            (if (throttle-ips* ip) 1 req-limit*))
      4808                        (let dt (- now (deq (req-times* ip)))
      4809                          (if (< dt dos-window*) (set (ignore-ips* ip)))
      4810                          (< dt req-window*)))
      4811                   (do (= (req-times* ip) (queue))
      4812                       nil))
      4813               (enq now (req-times* ip))))))
      4814  
4011 (def handle-request-thread (i o ip) = 4815 (def handle-request-thread (i o ip)
4012   (with (nls 0 lines nil line nil responded nil) <> 4816   (with (nls 0 lines nil line nil responded nil t0 (msec))
4013     (after = 4817     (after
4014       (whilet c (and (no responded) (readc i)) <> 4818       (whilet c (unless responded (readc i))
4015         (if srv-noisy* (pr c)) = 4819         (if srv-noisy* (pr c))
4016         (if (is c #\newline)   4820         (if (is c #\newline)
4017             (if (is (++ nls) 2)    4821             (if (is (++ nls) 2) 
4018                 (do (let (type op args n cooks) (parseheader (rev lines)) <> 4822                 (let (type op args n cooks) (parseheader (rev lines))
4019                       (srvlog 'srv ip type op cooks)   4823                   (let t1 (msec)
4020                       (case type   4824                     (case type
4021                         get  (respond o op args cooks ip)   4825                       get  (respond o op args cooks ip)
4022                         post (handle-post i o op n cooks ip)   4826                       post (handle-post i o op args n cooks ip)
4023                              (respond-err o "Unknown request: " (car lines))))   4827                            (respond-err o "Unknown request: " (car lines)))
      4828                     (log-request type op args cooks ip t0 t1)
4024                     (= responded t))   4829                     (set responded)))
4025                 (do (push (coerce (rev line) 'string) lines)   4830                 (do (push (string (rev line)) lines)
4026                     (= line nil)))   4831                     (wipe line)))
4027             (unless (is c #\return) = 4832             (unless (is c #\return)
4028               (push c line)   4833               (push c line)
4029               (= nls 0))))   4834               (= nls 0))))
4030       (close o) <>    
4031       (close i)))   4835       (close i o)))
4032   (harvest-fnids)) = 4836   (harvest-fnids))
4033     4837  
    -+ 4838 (def log-request (type op args cooks ip t0 t1)
      4839   (with (parsetime (- t1 t0) respondtime (- (msec) t1))
      4840     (srvlog 'srv ip 
      4841                  parsetime 
      4842                  respondtime 
      4843                  (if (> (+ parsetime respondtime) 1000) "***" "")
      4844                  type
      4845                  op
      4846                  (let arg1 (car args)
      4847                    (if (caris arg1 "fnid") "" arg1))
      4848                  cooks)))
      4849  
4034 ; Could ignore return chars (which come from textarea fields) here by = 4850 ; Could ignore return chars (which come from textarea fields) here by
4035 ; (unless (is c #\return) (push c line))   4851 ; (unless (is c #\return) (push c line))
4036     4852  
4037 (def handle-post (i o op n cooks ip) <> 4853 (def handle-post (i o op args n cooks ip)
4038   (if srv-noisy* (pr "Post Contents: ")) = 4854   (if srv-noisy* (pr "Post Contents: "))
4039   (if (no n)   4855   (if (no n)
4040       (respond-err o "Post request without Content-Length.")   4856       (respond-err o "Post request without Content-Length.")
4041       (let line nil   4857       (let line nil
4042         (whilet c (and (> n 0) (readc i))   4858         (whilet c (and (> n 0) (readc i))
4043           (if srv-noisy* (pr c))   4859           (if srv-noisy* (pr c))
4044           (-- n)   4860           (-- n)
4045           (push c line))    4861           (push c line)) 
4046         (if srv-noisy* (pr "\n\n"))   4862         (if srv-noisy* (pr "\n\n"))
4047         (respond o op (parseargs (coerce (rev line) 'string)) cooks ip)))) <> 4863         (respond o op (+ (parseargs (string (rev line))) args) cooks ip))))
4048   = 4864  
4049 (= header* "HTTP/1.0 200 OK <> 4865 (= header* "HTTP/1.1 200 OK
4050 Content-Type: text/html   4866 Content-Type: text/html; charset=utf-8
4051 Connection: close") = 4867 Connection: close")
4052     4868  
    <> 4869 (= type-header* (table))
      4870  
      4871 (def gen-type-header (ctype)
4053 (= gif-header* "HTTP/1.0 200 OK   4872   (+ "HTTP/1.0 200 OK
4054 Content-Type: image/gif   4873 Content-Type: "
      4874      ctype
      4875      "
4055 Connection: close")   4876 Connection: close"))
4056   = 4877  
    -+ 4878 (map (fn ((k v)) (= (type-header* k) (gen-type-header v)))
      4879      '((gif       "image/gif")
      4880        (jpg       "image/jpeg")
      4881        (png       "image/png")
      4882        (text/html "text/html; charset=utf-8")))
      4883  
4057 (= rdheader* "HTTP/1.0 302 Moved") = 4884 (= rdheader* "HTTP/1.0 302 Moved")
4058     4885  
4059 (= srvops* (table) redirectors* (table) optimes* (table)) <> 4886 (= srvops* (table) redirector* (table) optimes* (table) opcounts* (table))
4060   = 4887  
4061 (def save-optime (name elapsed)   4888 (def save-optime (name elapsed)
    -+ 4889   ; this is the place to put a/b testing
      4890   ; toggle a flag and push elapsed into one of two lists
      4891   (++ (opcounts* name 0))
4062   (unless (optimes* name) (= (optimes* name) (queue))) = 4892   (unless (optimes* name) (= (optimes* name) (queue)))
4063   (enq-limit elapsed (optimes* name) 1000))   4893   (enq-limit elapsed (optimes* name) 1000))
4064     4894  
4065 ; For ops that want to add their own headers.  They must thus remember    4895 ; For ops that want to add their own headers.  They must thus remember 
4066 ; to prn a blank line before anything meant to be part of the page.   4896 ; to prn a blank line before anything meant to be part of the page.
4067     4897  
4068 (mac defop-raw (name parms . body)   4898 (mac defop-raw (name parms . body)
4069   (w/uniq t1   4899   (w/uniq t1
4070     `(= (srvops* ',name)    4900     `(= (srvops* ',name) 
4071         (fn ,parms    4901         (fn ,parms 
4072           (let ,t1 (msec)   4902           (let ,t1 (msec)
4073             (do1 (do ,@body)   4903             (do1 (do ,@body)
4074                  (save-optime ',name (- (msec) ,t1))))))))   4904                  (save-optime ',name (- (msec) ,t1))))))))
4075     4905  
4076 (mac defopr-raw (name parms . body)   4906 (mac defopr-raw (name parms . body)
4077   `(= (redirectors* ',name) t <> 4907   `(= (redirector* ',name) t
4078       (srvops* ',name)      (fn ,parms ,@body)))   4908       (srvops* ',name)     (fn ,parms ,@body)))
4079   = 4909  
4080 (mac defop (name parm . body)   4910 (mac defop (name parm . body)
4081   (w/uniq gs   4911   (w/uniq gs
    <> 4912     `(do (wipe (redirector* ',name))
4082     `(defop-raw ,name (,gs ,parm)    4913          (defop-raw ,name (,gs ,parm) 
4083        (w/stdout ,gs (prn) ,@body))))   4914            (w/stdout ,gs (prn) ,@body)))))
4084   = 4915  
4085 ; Defines op as a redirector.  Its retval is new location.   4916 ; Defines op as a redirector.  Its retval is new location.
4086     4917  
4087 (mac defopr (name parm . body)   4918 (mac defopr (name parm . body)
4088   (w/uniq gs   4919   (w/uniq gs
4089     `(do (t! (redirectors* ',name)) <> 4920     `(do (set (redirector* ',name))
4090          (defop-raw ,name (,gs ,parm) = 4921          (defop-raw ,name (,gs ,parm)
4091            ,@body))))   4922            ,@body))))
4092     4923  
4093 ;(mac testop (name . args) `((srvops* ',name) ,@args))   4924 ;(mac testop (name . args) `((srvops* ',name) ,@args))
4094     4925  
4095 (deftem request   4926 (deftem request
4096   args  nil   4927   args  nil
4097   cooks nil   4928   cooks nil
4098   ip    nil)   4929   ip    nil)
4099     4930  
4100 (= unknown-msg* "Unknown operator.") <> 4931 (= unknown-msg* "Unknown." max-age* (table) static-max-age* nil)
4101   = 4932  
4102 (def respond (str op args cooks ip)   4933 (def respond (str op args cooks ip)
4103   (w/stdout str   4934   (w/stdout str
4104     (if (gifname op) <>    
4105         (do (prn gif-header*)      
4106             (prn)      
4107             (w/infile i (coerce op 'string)      
4108               (whilet b (readb i)      
4109                 (writeb b str))))      
4110         (aif (srvops* op)   4935     (iflet f (srvops* op)
4111              (let req (inst 'request 'args args 'cooks cooks 'ip ip)   4936            (let req (inst 'request 'args args 'cooks cooks 'ip ip)
4112                (if (redirectors* op)   4937              (if (redirector* op)
4113                    (do (prn rdheader*)   4938                  (do (prn rdheader*)
4114                        (let loc (it str req) ; may write to str, e.g. cookies      
4115                          (prn "Location: " loc))   4939                      (prn "Location: " (f str req))
4116                        (prn))   4940                      (prn))
4117                    (do (prn header*)   4941                  (do (prn header*)
      4942                      (awhen (max-age* op)
      4943                        (prn "Cache-Control: max-age=" it))
4118                        (it str req))))   4944                      (f str req))))
      4945            (let filetype (static-filetype op)
      4946              (aif (and filetype (file-exists (string staticdir* op)))
      4947                   (do (prn (type-header* filetype))
      4948                       (awhen static-max-age*
      4949                         (prn "Cache-Control: max-age=" it))
      4950                       (prn)
      4951                       (w/infile i it
      4952                         (whilet b (readb i)
      4953                           (writeb b str))))
4119              (respond-err str unknown-msg*)))))   4954                   (respond-err str unknown-msg*))))))
4120   = 4955  
4121 (def gifname (sym) <> 4956 (def static-filetype (sym)
4122   (let str (coerce sym 'string)   4957   (let fname (coerce sym 'string)
4123     (and (endmatch ".gif" str) (~find #\/ str))))   4958     (and (~find #\/ fname)
      4959          (case (downcase (last (check (tokens fname #\.) ~single)))
      4960            "gif"  'gif
      4961            "jpg"  'jpg
      4962            "jpeg" 'jpg
      4963            "png"  'png
      4964            "css"  'text/html
      4965            "txt"  'text/html
      4966            "htm"  'text/html
      4967            "html" 'text/html
      4968            "arc"  'text/html
      4969            ))))
4124   = 4970  
4125 (def respond-err (str msg . args)   4971 (def respond-err (str msg . args)
4126   (w/stdout str   4972   (w/stdout str
4127     (prn header*)   4973     (prn header*)
4128     (prn)   4974     (prn)
4129     (apply pr msg args)))   4975     (apply pr msg args)))
4130     4976  
4131 (def parseheader (lines)   4977 (def parseheader (lines)
4132   (let (type op args) (parseurl (car lines))   4978   (let (type op args) (parseurl (car lines))
4133     (list type   4979     (list type
4134           op   4980           op
4135           args   4981           args
4136           (and (is type 'post)   4982           (and (is type 'post)
4137                (some (fn (s)   4983                (some (fn (s)
4138                        (and (begins s "Content-Length:")   4984                        (and (begins s "Content-Length:")
4139                             (coerce (cadr (tokens s)) 'int))) <> 4985                             (errsafe:coerce (cadr (tokens s)) 'int)))
4140                      (cdr lines))) = 4986                      (cdr lines)))
4141           (some (fn (s)   4987           (some (fn (s)
4142                   (and (begins s "Cookie:")   4988                   (and (begins s "Cookie:")
4143                        (parsecookies s)))   4989                        (parsecookies s)))
4144                 (cdr lines)))))   4990                 (cdr lines)))))
4145     4991  
4146 ; (parseurl "GET /p1?foo=bar&ug etc") -> (get p1 (("foo" "bar") ("ug")))   4992 ; (parseurl "GET /p1?foo=bar&ug etc") -> (get p1 (("foo" "bar") ("ug")))
4147     4993  
4148 (def parseurl (s)   4994 (def parseurl (s)
4149   (let (type url) (tokens s)   4995   (let (type url) (tokens s)
4150     (let (base args) (tokens url #\?)   4996     (let (base args) (tokens url #\?)
4151       (list (coerce (downcase type) 'sym) <> 4997       (list (sym (downcase type))
4152             (coerce (subseq base 1) 'sym)   4998             (sym (cut base 1))
4153             (if args = 4999             (if args
4154                 (parseargs args)   5000                 (parseargs args)
4155                 nil)))))   5001                 nil)))))
4156     5002  
4157 ; don't urldecode field names or anything in cookies; correct? <> 5003 ; I don't urldecode field names or anything in cookies; correct?
4158   = 5004  
4159 (def parseargs (s)   5005 (def parseargs (s)
4160   (map (fn ((k v)) (list k (urldecode v)))   5006   (map (fn ((k v)) (list k (urldecode v)))
4161        (map [tokens _ #\=] (tokens s #\&))))   5007        (map [tokens _ #\=] (tokens s #\&))))
4162     5008  
4163 (def parsecookies (s)   5009 (def parsecookies (s)
4164   (map [tokens _ #\=]    5010   (map [tokens _ #\=] 
4165        (cdr (tokens s [or (whitec _) (is _ #\;)]))))   5011        (cdr (tokens s [or (whitec _) (is _ #\;)]))))
4166     5012  
4167 (def arg (req key) (alref (req 'args) key)) <> 5013 (def arg (req key) (alref req!args key))
4168   = 5014  
4169 ; *** Warning: does not currently urlencode args, so if need to do   5015 ; *** Warning: does not currently urlencode args, so if need to do
4170 ; that replace v with (urlencode v).   5016 ; that replace v with (urlencode v).
4171     5017  
4172 (def reassemble-args (req)   5018 (def reassemble-args (req)
4173   (aif (req 'args) <> 5019   (aif req!args
4174        (apply string "?" (intersperse '& = 5020        (apply string "?" (intersperse '&
4175                                       (map (fn (pair) <> 5021                                       (map (fn ((k v))
4176                                             (let (k v) pair      
4177                                               (string k '= v)))   5022                                              (string k '= v))
4178                                            it))) = 5023                                            it)))
4179        ""))   5024        ""))
4180     5025  
4181 (= fns* (table) fnids* nil timed-fnids* nil)   5026 (= fns* (table) fnids* nil timed-fnids* nil)
4182     5027  
4183 ; count on huge (expt 64 10) size of fnid space to avoid clashes   5028 ; count on huge (expt 64 10) size of fnid space to avoid clashes
4184     5029  
4185 (def new-fnid ()   5030 (def new-fnid ()
4186   (let key (sym (rand-string 10)) <> 5031   (check (sym (rand-string 10)) ~fns* (new-fnid)))
4187     (if (fns* key)      
4188         (new-fnid)      
4189         key)))      
4190   = 5032  
4191 (def fnid (f)   5033 (def fnid (f)
4192   (atlet key (new-fnid)   5034   (atlet key (new-fnid)
4193     (= (fns* key) f)   5035     (= (fns* key) f)
4194     (push key fnids*)   5036     (push key fnids*)
4195     key))   5037     key))
4196     5038  
4197 (def timed-fnid (lasts f)   5039 (def timed-fnid (lasts f)
4198   (atlet key (new-fnid)   5040   (atlet key (new-fnid)
4199     (= (fns* key) f)   5041     (= (fns* key) f)
4200     (push (list key (seconds) lasts) timed-fnids*)   5042     (push (list key (seconds) lasts) timed-fnids*)
4201     key))   5043     key))
4202     5044  
4203 ; Within f, it will be bound to the fn's own fnid.  Remember that this is   5045 ; Within f, it will be bound to the fn's own fnid.  Remember that this is
4204 ; so low-level that need to generate the newline to separate from the headers   5046 ; so low-level that need to generate the newline to separate from the headers
4205 ; within the body of f.   5047 ; within the body of f.
4206     5048  
4207 (mac afnid (f)   5049 (mac afnid (f)
4208   `(atlet it (new-fnid)   5050   `(atlet it (new-fnid)
4209      (= (fns* it) ,f)   5051      (= (fns* it) ,f)
4210      (push it fnids*)   5052      (push it fnids*)
4211      it))   5053      it))
4212     5054  
4213 ;(defop test-afnid req   5055 ;(defop test-afnid req
4214 ;  (tag (a href (url-for (afnid (fn (req) (prn) (pr "my fnid is " it)))))   5056 ;  (tag (a href (url-for (afnid (fn (req) (prn) (pr "my fnid is " it)))))
4215 ;    (pr "click here")))   5057 ;    (pr "click here")))
4216     5058  
4217 ; To be more sophisticated, instead of killing fnids, could first    5059 ; To be more sophisticated, instead of killing fnids, could first 
4218 ; replace them with fns that tell the server it's harvesting too    5060 ; replace them with fns that tell the server it's harvesting too 
4219 ; aggressively if they start to get called.  But the right thing to    5061 ; aggressively if they start to get called.  But the right thing to 
4220 ; do is estimate what the max no of fnids can be and set the harvest    5062 ; do is estimate what the max no of fnids can be and set the harvest 
4221 ; limit there-- beyond that the only solution is to buy more memory.   5063 ; limit there-- beyond that the only solution is to buy more memory.
4222     5064  
4223 (def harvest-fnids ((o n 20000)) <> 5065 (def harvest-fnids ((o n 50000))  ; was 20000
4224   (when (> (len fns*) n)    5066   (when (len> fns* n) 
4225     (atomic      
4226       (pull (fn ((id created lasts))   5067     (pull (fn ((id created lasts))
4227               (when (> (- (seconds) created) lasts)       5068             (when (> (since created) lasts)    
4228                 (nil! (fns* id))   5069               (wipe (fns* id))
4229                 t))   5070               t))
4230             timed-fnids*))   5071           timed-fnids*)
4231     (atlet nharvest (truncate (/ n 10))   5072     (atlet nharvest (trunc (/ n 10))
4232       (let (kill keep) (splitn nharvest (rev fnids*))    5073       (let (kill keep) (split (rev fnids*) nharvest)
4233         (= fnids* (rev keep))  = 5074         (= fnids* (rev keep)) 
4234         (each id kill    5075         (each id kill 
4235           (nil! (fns* id))))))) <> 5076           (wipe (fns* id)))))))
4236   = 5077  
4237 (= fnurl* "x" rfnurl* "r" rfnurl2* "y" jfnurl* "a") <> 5078 (= fnurl* "/x" rfnurl* "/r" rfnurl2* "/y" jfnurl* "/a")
      5079  
      5080 (= dead-msg* "\nUnknown or expired link.")
4238   = 5081  
4239 (defop-raw x (str req)   5082 (defop-raw x (str req)
    <> 5083   (w/stdout str 
4240   (let id (sym (arg req "fnid"))   5084     (aif (fns* (sym (arg req "fnid")))
4241     (aif (fns* id)      
4242          (w/stdout str (it req))   5085          (it req)
4243          (w/stdout str (prn) (pr "unknown or expired link")))))   5086          (pr dead-msg*))))
4244   = 5087  
4245 (defopr-raw y (str req)   5088 (defopr-raw y (str req)
4246   (let id (sym (arg req "fnid")) <> 5089   (aif (fns* (sym (arg req "fnid")))
4247     (aif (fns* id)      
4248          (w/stdout str (it req))   5090        (w/stdout str (it req))
4249          "deadlink")))   5091        "deadlink"))
4250   = 5092  
4251 ; For asynchronous calls; discards the page.  Would be better to tell   5093 ; For asynchronous calls; discards the page.  Would be better to tell
4252 ; the fn not to generate it.   5094 ; the fn not to generate it.
4253     5095  
4254 (defop-raw a (str req)   5096 (defop-raw a (str req)
4255   (let id (sym (arg req "fnid")) <> 5097   (aif (fns* (sym (arg req "fnid")))
4256     (aif (fns* id) (tostring (it req)))))   5098        (tostring (it req))))
4257   = 5099  
4258 (defopr r req   5100 (defopr r req
4259   (let id (sym (arg req "fnid")) <> 5101   (aif (fns* (sym (arg req "fnid")))
4260     (aif (fns* id)      
4261          (it req)   5102        (it req)
4262          "deadlink")))   5103        "deadlink"))
4263   = 5104  
4264 (defop deadlink req   5105 (defop deadlink req
4265   (pr "unknown or expired link")) <> 5106   (pr dead-msg*))
4266   = 5107  
4267 (def url-for (fnid)   5108 (def url-for (fnid)
4268   (string fnurl* "?fnid=" fnid))   5109   (string fnurl* "?fnid=" fnid))
4269     5110  
4270 (def flink (f)   5111 (def flink (f)
4271   (string fnurl* "?fnid=" (fnid (fn (req) (prn) (f req)))))   5112   (string fnurl* "?fnid=" (fnid (fn (req) (prn) (f req)))))
4272     5113  
4273 ; couldn't I just say (fnid f) here? +-    
4274        
4275 (def rflink (f) = 5114 (def rflink (f)
4276   (string rfnurl* "?fnid=" (fnid (fn (req) (f req))))) <> 5115   (string rfnurl* "?fnid=" (fnid f)))
4277    = 5116   
4278 ; Since it's just an expr, gensym a parm for (ignored) args.   5117 ; Since it's just an expr, gensym a parm for (ignored) args.
4279     5118  
4280 (mac w/link (expr . body)   5119 (mac w/link (expr . body)
4281   (w/uniq g <>    
4282     `(tag (a href (flink (fn (,g) ,expr)))   5120   `(tag (a href (flink (fn (,(uniq)) ,expr)))
4283        ,@body)))   5121      ,@body))
4284   = 5122  
4285 (mac w/rlink (expr . body)   5123 (mac w/rlink (expr . body)
4286   (w/uniq g <>    
4287     `(tag (a href (rflink (fn (,g) ,expr)))   5124   `(tag (a href (rflink (fn (,(uniq)) ,expr)))
4288        ,@body)))   5125      ,@body))
4289   = 5126  
4290 (mac onlink (text . body)   5127 (mac onlink (text . body)
4291   `(w/link (do ,@body) (pr ,text)))   5128   `(w/link (do ,@body) (pr ,text)))
    -+ 5129  
      5130 (mac onrlink (text . body)
      5131   `(w/rlink (do ,@body) (pr ,text)))
4292   = 5132  
4293 ; bad to have both flink and linkf; rename flink something like fnid-link   5133 ; bad to have both flink and linkf; rename flink something like fnid-link
4294     5134  
4295 (mac linkf (text parms . body)   5135 (mac linkf (text parms . body)
4296   `(tag (a href (flink (fn ,parms ,@body))) (pr ,text)))   5136   `(tag (a href (flink (fn ,parms ,@body))) (pr ,text)))
4297     5137  
4298 (mac rlinkf (text parms . body)   5138 (mac rlinkf (text parms . body)
4299   `(tag (a href (rflink (fn ,parms ,@body))) (pr ,text)))   5139   `(tag (a href (rflink (fn ,parms ,@body))) (pr ,text)))
4300     5140  
4301 ;(defop top req (linkf 'whoami? (req) (pr "I am " (get-user req))))   5141 ;(defop top req (linkf 'whoami? (req) (pr "I am " (get-user req))))
4302     5142  
4303 ;(defop testf req (w/link (pr "ha ha ha") (pr "laugh")))   5143 ;(defop testf req (w/link (pr "ha ha ha") (pr "laugh")))
4304     5144  
4305 (mac w/link-if (test expr . body)   5145 (mac w/link-if (test expr . body)
4306   (w/uniq g <>    
4307     `(tag-if ,test (a href (flink (fn (,g) ,expr)))   5146   `(tag-if ,test (a href (flink (fn (,(uniq)) ,expr)))
4308        ,@body)))   5147      ,@body))
4309   = 5148  
    -+ 5149 (def fnid-field (id)
      5150   (gentag input type 'hidden name 'fnid value id))
      5151  
4310 ; f should be a fn of one arg, which will be http request args. = 5152 ; f should be a fn of one arg, which will be http request args.
    -+ 5153  
      5154 (def fnform (f bodyfn (o redir))
      5155   (tag (form method 'post action (if redir rfnurl2* fnurl*))
      5156     (fnid-field (fnid f))
      5157     (bodyfn)))
      5158  
4311 ; Could also make a version that uses just an expr, and var capture. = 5159 ; Could also make a version that uses just an expr, and var capture.
4312 ; Is there a way to ensure user doesn't use "fnid" as a key?   5160 ; Is there a way to ensure user doesn't use "fnid" as a key?
4313     5161  
4314 (mac aform (f . body)   5162 (mac aform (f . body)
4315   (w/uniq (gi ga) <> 5163   (w/uniq ga
      5164     `(tag (form method 'post action fnurl*)
4316     `(let ,gi (fnid (fn (,ga)   5165        (fnid-field (fnid (fn (,ga)
4317                       (prn)   5166                            (prn)
4318                       (,f ,ga)))   5167                            (,f ,ga))))
4319        (tag (form method 'post action fnurl*)      
4320          (gentag input type 'hidden name 'fnid value ,gi)      
4321          ,@body))))   5168        ,@body)))
4322   = 5169  
    -+ 5170 ;(defop test1 req
      5171 ;  (fnform (fn (req) (prn) (pr req))
      5172 ;          (fn () (single-input "" 'foo 20 "submit"))))
      5173  
      5174 ;(defop test2 req
      5175 ;  (aform (fn (req) (pr req))
      5176 ;    (single-input "" 'foo 20 "submit")))
      5177  
4323 ; Like aform except creates a fnid that will last for lasts seconds = 5178 ; Like aform except creates a fnid that will last for lasts seconds
4324 ; (unless the server is restarted).   5179 ; (unless the server is restarted).
4325     5180  
4326 (mac timed-aform (lasts f . body) <> 5181 (mac taform (lasts f . body)
4327   (w/uniq (gl gf gi ga) = 5182   (w/uniq (gl gf gi ga)
4328     `(withs (,gl ,lasts   5183     `(withs (,gl ,lasts
4329              ,gf (fn (,ga) (prn) (,f ,ga)) <> 5184              ,gf (fn (,ga) (prn) (,f ,ga)))
4330              ,gi (if ,gl (timed-fnid ,lasts ,gf) (fnid ,gf)))      
4331        (tag (form method 'post action fnurl*) = 5185        (tag (form method 'post action fnurl*)
4332          (gentag input type 'hidden name 'fnid value ,gi) <> 5186          (fnid-field (if ,gl (timed-fnid ,gl ,gf) (fnid ,gf)))
4333          ,@body)))) = 5187          ,@body))))
4334     5188  
4335 (mac arform (f . body)   5189 (mac arform (f . body)
    <> 5190   `(tag (form method 'post action rfnurl*)
      5191      (fnid-field (fnid ,f))
      5192      ,@body))
      5193  
      5194 ; overlong
      5195  
      5196 (mac tarform (lasts f . body)
4336   (w/uniq gi   5197   (w/uniq (gl gf)
4337     `(let ,gi (fnid ,f)   5198     `(withs (,gl ,lasts ,gf ,f)
4338        (tag (form method 'post action rfnurl*) = 5199        (tag (form method 'post action rfnurl*)
4339          (gentag input type 'hidden name 'fnid value ,gi) <> 5200          (fnid-field (if ,gl (timed-fnid ,gl ,gf) (fnid ,gf)))
4340          ,@body)))) = 5201          ,@body))))
4341     5202  
4342 (mac aformh (f . body)   5203 (mac aformh (f . body)
4343   (w/uniq gi <>    
4344     `(let ,gi (fnid ,f)      
4345        (tag (form method 'post action fnurl*)   5204   `(tag (form method 'post action fnurl*)
4346          (gentag input type 'hidden name 'fnid value ,gi)   5205      (fnid-field (fnid ,f))
4347          ,@body))))   5206      ,@body))
4348   = 5207  
4349 (mac arformh (f . body)   5208 (mac arformh (f . body)
4350   (w/uniq gi <>    
4351     `(let ,gi (fnid ,f)      
4352        (tag (form method 'post action rfnurl2*)   5209   `(tag (form method 'post action rfnurl2*)
4353          (gentag input type 'hidden name 'fnid value ,gi)   5210      (fnid-field (fnid ,f))
4354          ,@body))))   5211      ,@body))
4355   = 5212  
4356 ; only unique per server invocation   5213 ; only unique per server invocation
4357     5214  
4358 (= unique-ids* (table))   5215 (= unique-ids* (table))
4359     5216  
4360 (def unique-id ((o len 8))   5217 (def unique-id ((o len 8))
4361   (let id (sym (rand-string (max 5 len)))   5218   (let id (sym (rand-string (max 5 len)))
4362     (if (unique-ids* id)   5219     (if (unique-ids* id)
4363         (unique-id)   5220         (unique-id)
4364         (= (unique-ids* id) id))))   5221         (= (unique-ids* id) id))))
4365     5222  
4366   +-    
4367 (def srvlog (type . args) = 5223 (def srvlog (type . args)
4368   (w/appendfile o (string logdir* type "-" (memodate)) <> 5224   (w/appendfile o (logfile-name type)
4369     (w/stdout o (apply prs (seconds) args) (prn))))   5225     (w/stdout o (atomic (apply prs (seconds) args) (prn)))))
4370   = 5226  
    -+ 5227 (def logfile-name (type)
      5228   (string logdir* type "-" (memodate)))
      5229  
4371 (with (lastasked nil lastval nil) = 5230 (with (lastasked nil lastval nil)
4372     5231  
4373 (def memodate ()   5232 (def memodate ()
4374   (let now (seconds)   5233   (let now (seconds)
4375     (if (or (no lastasked) (> (- now lastasked) 60))   5234     (if (or (no lastasked) (> (- now lastasked) 60))
4376         (= lastasked now lastval (date)) <> 5235         (= lastasked now lastval (datestring))
4377         lastval))) = 5236         lastval)))
4378     5237  
4379 )   5238 )
4380     5239  
4381 (defop || req <>    
4382   (pr "It's alive."))   5240 (defop || req (pr "It's alive."))
4383   = 5241  
4384 (defop topips req   5242 (defop topips req
4385   (when (admin (get-user req))   5243   (when (admin (get-user req))
4386     (whitepage   5244     (whitepage
4387       (spacetable <> 5245       (sptab
4388         (each ip (let leaders nil  = 5246         (each ip (let leaders nil 
4389                    (maptable (fn (ip n)   5247                    (maptable (fn (ip n)
4390                                (when (> n 100)   5248                                (when (> n 100)
4391                                  (insort (compare > requests/ip*)   5249                                  (insort (compare > requests/ip*)
4392                                          ip   5250                                          ip
4393                                          leaders)))   5251                                          leaders)))
4394                              requests/ip*)   5252                              requests/ip*)
4395                    leaders)   5253                    leaders)
4396           (let n (requests/ip* ip)   5254           (let n (requests/ip* ip)
4397             (row ip n (num (* 100 (/ n requests*)) 1)))))))) <> 5255             (row ip n (pr (num (* 100 (/ n requests*)) 1)))))))))
4398   = 5256  
    <> 5257 (defop spurned req
4399 (def ensure-install ()   5258   (when (admin (get-user req))
      5259     (whitepage
      5260       (sptab
      5261         (map (fn ((ip n)) (row ip n))
      5262              (sortable spurned*))))))
4400   (ensure-dir arcdir*)   5263  
      5264 ; eventually promote to general util
4401   (ensure-dir logdir*)   5265  
      5266 (def sortable (ht (o f >))
      5267   (let res nil
      5268     (maptable (fn kv
      5269                 (insort (compare f cadr) kv res))
      5270               ht)
      5271     res))
4402   (when (empty hpasswords*)   5272  
4403     (create-acct "frug" "frug")   5273  
      5274 ; Background Threads
4404     (writefile1 'frug adminfile*))   5275  
      5276 (= bgthreads* (table) pending-bgthreads* nil)
4405   (load-userinfo))   5277  
      5278 (def new-bgthread (id f sec)
      5279   (aif (bgthreads* id) (break-thread it))
      5280   (= (bgthreads* id) (new-thread (fn () 
      5281                                    (while t
      5282                                      (sleep sec)
      5283                                      (f))))))
4406   = 5284  
    -+ 5285 ; should be a macro for this?
      5286  
      5287 (mac defbg (id sec . body)
      5288   `(do (pull [caris _ ',id] pending-bgthreads*)
      5289        (push (list ',id (fn () ,@body) ,sec) 
      5290              pending-bgthreads*)))
      5291  
      5292  
      5293  
      5294 ; Idea: make form fields that know their value type because of
      5295 ; gensymed names, and so the receiving fn gets args that are not
      5296 ; strings but parsed values.
4407   = 5297  
4408     5298  
4409 ./strings.arc   5299 ./strings.arc
4410 ; Matching.  Spun off 29 Jul 06.   5300 ; Matching.  Spun off 29 Jul 06.
4411     5301  
4412 ; Arc> (tostring (writec (coerce 133 'char))) <> 5302 ; arc> (tostring (writec (coerce 133 'char)))
4413 ; = 5303 ;
4414 ;> (define ss (open-output-string))   5304 ;> (define ss (open-output-string))
4415 ;> (write-char (integer->char 133) ss)   5305 ;> (write-char (integer->char 133) ss)
4416 ;> (get-output-string ss)   5306 ;> (get-output-string ss)
4417 ;"\u0085"   5307 ;"\u0085"
4418     5308  
4419 (def tokens (s (o sep whitec))   5309 (def tokens (s (o sep whitec))
4420   (let test (if (isa sep 'fn) sep (fn (c) (is c sep))) <> 5310   (let test (testify sep)
4421     (let rec (afn (cs toks tok) = 5311     (let rec (afn (cs toks tok)
4422                (if (no cs)         (consif tok toks)   5312                (if (no cs)         (consif tok toks)
4423                    (test (car cs)) (self (cdr cs) (consif tok toks) nil)   5313                    (test (car cs)) (self (cdr cs) (consif tok toks) nil)
4424                                    (self (cdr cs) toks (cons (car cs) tok))))   5314                                    (self (cdr cs) toks (cons (car cs) tok))))
4425     (rev (map [coerce _ 'string] <> 5315       (rev (map [coerce _ 'string]
4426               (map rev (rec (coerce s 'cons) nil nil)))))))   5316                 (map rev (rec (coerce s 'cons) nil nil)))))))
4427   = 5317  
    -+ 5318 ; names of cut, split, halve not optimal
      5319  
      5320 (def halve (s (o sep whitec))
      5321   (let test (testify sep)
      5322     (let rec (afn (cs tok)
      5323                (if (no cs)         (list (rev tok))
      5324                    (test (car cs)) (list cs (rev tok))
      5325                                    (self (cdr cs) (cons (car cs) tok))))
      5326       (rev (map [coerce _ 'string]
      5327                 (rec (coerce s 'cons) nil))))))
      5328  
      5329 ; maybe promote to arc.arc, but if so include a list clause
      5330  
      5331 (def positions (test seq)
      5332   (accum a
      5333     (let f (testify test)
      5334       (forlen i seq
      5335         (if (f (seq i)) (a i))))))
      5336  
      5337 (def lines (s)
      5338   (accum a
      5339     ((afn ((p . ps))
      5340        (if ps
      5341            (do (a (rem #\return (cut s (+ p 1) (car ps))))
      5342                (self ps))
      5343            (a (cut s (+ p 1)))))
      5344      (cons -1 (positions #\newline s)))))
      5345  
      5346 (def slices (s test)
      5347   (accum a
      5348     ((afn ((p . ps))
      5349        (if ps
      5350            (do (a (cut s (+ p 1) (car ps)))
      5351                (self ps))
      5352            (a (cut s (+ p 1)))))
      5353      (cons -1 (positions test s)))))
      5354  
4428 ; > (require (lib "uri-codec.ss" "net")) = 5355 ; > (require (lib "uri-codec.ss" "net"))
4429 ;> (form-urlencoded-decode "x%ce%bbx")   5356 ;> (form-urlencoded-decode "x%ce%bbx")
4430 ;"xλx"   5357 ;"xλx"
4431     5358  
4432 ; first byte: 0-7F, 1 char; c2-df 2; e0-ef 3, f0-f4 4.    5359 ; first byte: 0-7F, 1 char; c2-df 2; e0-ef 3, f0-f4 4. 
4433     5360  
    -+ 5361 ; Fixed for utf8 by pc.
      5362  
4434 (def urldecode (s) = 5363 (def urldecode (s)
4435   (tostring <> 5364  (tostring
4436     (forlen i s   5365   (forlen i s
4437       (caselet c (s i)   5366     (caselet c (s i)
4438         #\+ (writec #\space)   5367       #\+ (writec #\space)
4439         #\% (do (when (> (- (len s) i) 2)   5368       #\% (do (when (> (- (len s) i) 2)
4440                   (let code  (coerce (subseq s (+ i 1) (+ i 3))   5369                 (writeb (int (cut s (+ i 1) (+ i 3)) 16)))
4441                                      'int 16)      
4442                     (if (> code 126)      
4443                         (pr (latin1-hack code))      
4444                         (writec (coerce code 'char)))))      
4445                 (++ i 2))   5370               (++ i 2))
4446             (writec c)))))   5371           (writec c)))))
4447   = 5372  
4448   <> 5373 (def urlencode (s)
4449 ; Converts utf8 chars between 128 and 255 to ascii char or string.      
4450 ; Not able to convert every char; generates X if can't.  Probably not      
4451 ; the ultimate solution.  The Right Thing would be to preserve these      
4452 ; chars, instead of downgrading them to ascii.  To do that, would have      
4453 ; to convert them to unicode.  E.g. ellipsis in unicode is #x2026,      
4454 ; euro sign is #x20ac.        
4455     5374   (tostring 
4456 ; In Mzscheme: (display (integer->char #xE9))      
4457     5375     (each c s 
4458 ; Then have to figure out how to print them back out, both in forms       
4459 ; and in pages.  Presumably do the reverse translation and &-escape them.      
4460     5376       (writec #\%)
4461 ; For much of this range, unicode and utf8 agree.  233 is e-acute in      
4462 ; both.  It's chars like 133 that are a problem.  So perhaps as a      
4463 ; start try preserving e.g. 233.      
4464     5377       (let i (int c)
4465 ; This would be faster if I made a macro that translated it into      
4466 ; a hashtable or even string.      
4467     5378         (if (< i 16) (writec #\0))
4468 (def latin1-hack (i)      
4469   (if (is i 128)      "EUR"    ; euros      
4470       (is i 133)      "..."      
4471       (<= 145 i 146)  #\'      
4472       (<= 147 i 148)  #\"      
4473       (is i 151)      "--"     ; long dash      
4474       (is i 154)      #\S      
4475       (is i 155)      #\>      
4476       (is i 156)      "oe"      
4477       (is i 158)      #\z      
4478       (is i 159)      #\Y      
4479       (is i 162)      #\c      ; cents   5379         (pr (coerce i 'string 16))))))
4480       (is i 163)      "GBP"    ; pounds      
4481       (is i 165)      "JPY"    ; yen      
4482       (is i 166)      #\|      
4483       (is i 171)      "<<"      
4484       (is i 187)      ">>"      
4485       (<= 192 i 197)  #\A      
4486       (is i 198)      "AE"      
4487       (is i 199)      #\C      
4488       (<= 200 i 203)  #\E      
4489       (<= 204 i 207)  #\I      
4490       (is i 209)      #\N      
4491       (<= 210 i 214)  #\O      
4492       (is i 215)      #\x      
4493       (is i 216)      #\O      
4494       (<= 217 i 220)  #\U      
4495       (is i 221)      #\Y      
4496       (is i 223)      "ss"      
4497       (<= 224 i 229)  #\a      
4498       (is i 230)      "ae"      
4499       (is i 231)      #\c      
4500       (<= 232 i 235)  #\e      
4501       (<= 236 i 239)  #\i      
4502       (is i 241)      #\n      
4503       (<= 242 i 246)  #\o      
4504       (is i 247)      #\/      
4505       (is i 248)      #\o      
4506       (<= 249 i 252)  #\u      
4507       (in i 253 255)  #\y      
4508       #\X      
4509       ))      
4510   = 5380  
4511 (mac litmatch (pat string (o start 0))   5381 (mac litmatch (pat string (o start 0))
4512   (w/uniq (gstring gstart)   5382   (w/uniq (gstring gstart)
4513     `(with (,gstring ,string ,gstart ,start)   5383     `(with (,gstring ,string ,gstart ,start)
4514        (unless (> (+ ,gstart ,(len pat)) (len ,gstring))   5384        (unless (> (+ ,gstart ,(len pat)) (len ,gstring))
4515          (and ,@(let acc nil   5385          (and ,@(let acc nil
4516                   (forlen i pat   5386                   (forlen i pat
4517                     (push `(is ,(pat i) (,gstring (+ ,gstart ,i)))   5387                     (push `(is ,(pat i) (,gstring (+ ,gstart ,i)))
4518                            acc))   5388                            acc))
4519                   (rev acc)))))))   5389                   (rev acc)))))))
4520     5390  
4521 ; litmatch would be cleaner if map worked for string and integer args:   5391 ; litmatch would be cleaner if map worked for string and integer args:
4522     5392  
4523 ;             ,@(map (fn (n c)     5393 ;             ,@(map (fn (n c)  
4524 ;                      `(is ,c (,gstring (+ ,gstart ,n))))   5394 ;                      `(is ,c (,gstring (+ ,gstart ,n))))
4525 ;                    (len pat)   5395 ;                    (len pat)
4526 ;                    pat)   5396 ;                    pat)
4527     5397  
4528 (mac endmatch (pat string)   5398 (mac endmatch (pat string)
4529   (w/uniq (gstring glen)   5399   (w/uniq (gstring glen)
4530     `(withs (,gstring ,string ,glen (len ,gstring))   5400     `(withs (,gstring ,string ,glen (len ,gstring))
4531        (unless (> ,(len pat) (len ,gstring))   5401        (unless (> ,(len pat) (len ,gstring))
4532          (and ,@(let acc nil   5402          (and ,@(let acc nil
4533                   (forlen i pat   5403                   (forlen i pat
4534                     (push `(is ,(pat (- (len pat) 1 i))    5404                     (push `(is ,(pat (- (len pat) 1 i)) 
4535                                (,gstring (- ,glen 1 ,i)))   5405                                (,gstring (- ,glen 1 ,i)))
4536                            acc))   5406                            acc))
4537                   (rev acc)))))))   5407                   (rev acc)))))))
4538     5408  
4539 (def posmatch (pat seq (o start 0))   5409 (def posmatch (pat seq (o start 0))
4540   (catch   5410   (catch
4541     (if (isa pat 'fn)   5411     (if (isa pat 'fn)
4542         (for i start (- (len seq) 1)   5412         (for i start (- (len seq) 1)
4543           (when (pat (seq i)) (throw i)))   5413           (when (pat (seq i)) (throw i)))
4544         (for i start (- (len seq) (- (len pat) 2)) <> 5414         (for i start (- (len seq) (len pat))
4545           (when (headmatch pat seq i) (throw i)))) = 5415           (when (headmatch pat seq i) (throw i))))
4546     nil))   5416     nil))
4547     5417  
4548 (def headmatch (pat seq (o start 0))   5418 (def headmatch (pat seq (o start 0))
4549   (let p (len pat)    5419   (let p (len pat) 
4550     ((afn (i)         5420     ((afn (i)      
4551        (or (is i p)    5421        (or (is i p) 
4552            (and (is (pat i) (seq (+ i start)))   5422            (and (is (pat i) (seq (+ i start)))
4553                 (self (+ i 1)))))   5423                 (self (+ i 1)))))
4554      0)))   5424      0)))
4555     5425  
4556 (def begins (seq pat (o start 0))   5426 (def begins (seq pat (o start 0))
4557   (unless (> (len pat) (- (len seq) start)) <> 5427   (unless (len> pat (- (len seq) start))
4558     (headmatch pat seq start))) = 5428     (headmatch pat seq start)))
4559     5429  
4560 (def subst (new old seq)   5430 (def subst (new old seq)
4561   (let boundary (+ (- (len seq) (len old)) 1)   5431   (let boundary (+ (- (len seq) (len old)) 1)
4562     (tostring    5432     (tostring 
4563       (forlen i seq   5433       (forlen i seq
4564         (if (and (< i boundary) (headmatch old seq i))   5434         (if (and (< i boundary) (headmatch old seq i))
4565             (do (++ i (- (len old) 1))   5435             (do (++ i (- (len old) 1))
4566                 (pr new))   5436                 (pr new))
4567             (pr (seq i)))))))   5437             (pr (seq i)))))))
4568     5438  
4569 (def multisubst (pairs seq)   5439 (def multisubst (pairs seq)
4570   (tostring    5440   (tostring 
4571     (forlen i seq   5441     (forlen i seq
4572       (iflet (old new) (find [begins seq (car _) i] pairs)   5442       (iflet (old new) (find [begins seq (car _) i] pairs)
4573         (do (++ i (- (len old) 1))   5443         (do (++ i (- (len old) 1))
4574             (pr new))   5444             (pr new))
4575         (pr (seq i))))))   5445         (pr (seq i))))))
4576     5446  
    -+ 5447 ; not a good name
      5448  
4577 (def findsubseq (pat seq (o start 0)) = 5449 (def findsubseq (pat seq (o start 0))
4578   (if (< (- (len seq) start) (len pat))   5450   (if (< (- (len seq) start) (len pat))
4579        nil   5451        nil
4580       (if (headmatch pat seq start)   5452       (if (headmatch pat seq start)
4581           start   5453           start
4582           (findsubseq pat seq (+ start 1)))))   5454           (findsubseq pat seq (+ start 1)))))
4583     5455  
4584 (def blank (s) (~find ~whitec s))   5456 (def blank (s) (~find ~whitec s))
4585     5457  
    <> 5458 (def nonblank (s) (unless (blank s) s))
4586 ; should make it possible for test to be a literal as well   5459  
4587     5460 (def trim (s (o where 'both) (o test whitec))
4588 (def trim (s where (o test whitec))   5461   (withs (f   (testify test)
4589   (let p1 (pos [no (test _)] s)   5462            p1 (pos ~f s))
4590     (if p1 = 5463     (if p1
4591         (subseq <> 5464         (cut
4592                 (if (in where 'front 'both) p1 0)   5465              (if (in where 'front 'both) p1 0)
4593                 (when (in where 'end 'both)   5466              (when (in where 'end 'both)
4594                   (let i (- (len s) 1)   5467                (let i (- (len s) 1)
4595                     (while (and (> i p1) (test (s i)))   5468                  (while (and (> i p1) (f (s i)))
4596                       (-- i))   5469                    (-- i))
4597                     (+ i 1))))   5470                  (+ i 1))))
4598         ""))) = 5471         "")))
4599     5472  
4600 (def num (m (o digits 2) (o trail-zeros nil) (o init-zero nil)) <> 5473 (def num (n (o digits 2) (o trail-zeros nil) (o init-zero nil))
4601   (let comma   5474   (withs (comma
4602        (fn (n)   5475           (fn (i)
4603          (tostring   5476             (tostring
4604            (map [apply pr (rev _)]   5477               (map [apply pr (rev _)]
4605                 (rev (intersperse '(#\,)   5478                    (rev (intersperse '(#\,)
4606                                   (tuples (rev (coerce (string n) 'cons))   5479                                      (tuples (rev (coerce (string i) 'cons))
4607                                           3))))))   5480                                              3))))))
      5481           abrep
      5482           (let a (abs n)
4608     (if (< digits 1)   5483             (if (< digits 1)
4609          (comma (roundup m))   5484                  (comma (roundup a))
4610         (exact m)   5485                 (exact a)
4611          (string (comma m)   5486                  (string (comma a)
4612                  (when (and trail-zeros (> digits 0))   5487                          (when (and trail-zeros (> digits 0))
4613                    (string "." (newstring digits #\0))))   5488                            (string "." (newstring digits #\0))))
      5489                  (withs (d (expt 10 digits)
      5490                          m (/ (roundup (* a d)) d)
4614          (let n (truncate m)   5491                          i (trunc m)
      5492                          r (abs (trunc (- (* m d) (* i d)))))
      5493                    (+ (if (is i 0) 
4615            (string (if (is n 0) (if init-zero 0 "") (comma n))   5494                           (if (or init-zero (is r 0)) "0" "")
4616                    "."   5495                           (comma i))
4617                    (withs (rest (string (abs (roundup   5496                       (withs (rest   (string r)
4618                                                (- (* m (expt 10 digits))      
4619                                                   (* n (expt 10 digits))))))      
4620                            v2 (string (newstring (- digits (len rest)) #\0)   5497                               padded (+ (newstring (- digits (len rest)) #\0)
4621                                       rest))   5498                                         rest)
4622                      (if trail-zeros   5499                               final  (if trail-zeros
      5500                                          padded
      5501                                          (trim padded 'end [is _ #\0])))
      5502                         (string (unless (empty final) ".")
      5503                                 final)))))))
      5504     (if (and (< n 0) (find [and (digit _) (isnt _ #\0)] abrep))
      5505         (+ "-" abrep)
      5506         abrep)))
      5507         
4623                          v2   5508  
      5509 ; English
4624                          (trim v2 'end [is _ #\0]))))))))   5510  
      5511 (def pluralize (n str)
      5512   (if (or (is n 1) (single n))
      5513       str
      5514       (string str "s")))
4625   = 5515  
    -+ 5516 (def plural (n x)
      5517   (string n #\  (pluralize n x)))
4626   = 5518  
    -+ 5519  
4627 ; http://www.eki.ee/letter/chardata.cgi?HTML4=1 = 5520 ; http://www.eki.ee/letter/chardata.cgi?HTML4=1
4628 ; http://jrgraphix.net/research/unicode_blocks.php?block=1   5521 ; http://jrgraphix.net/research/unicode_blocks.php?block=1
4629 ; http://home.tiscali.nl/t876506/utf8tbl.html   5522 ; http://home.tiscali.nl/t876506/utf8tbl.html
4630 ; http://www.fileformat.info/info/unicode/block/latin_supplement/utf8test.htm   5523 ; http://www.fileformat.info/info/unicode/block/latin_supplement/utf8test.htm
4631 ; http://en.wikipedia.org/wiki/Utf-8   5524 ; http://en.wikipedia.org/wiki/Utf-8
4632 ; http://unicode.org/charts/charindex2.html   5525 ; http://unicode.org/charts/charindex2.html
4633     5526  
    -+ 5527 ./news.arc
      5528 ; News.  2 Sep 06.
      5529  
      5530 ; to run news: (nsv), then go to http://localhost:8080
      5531 ; put usernames of admins, separated by whitespace, in arc/admins
      5532  
      5533 ; bug: somehow (+ votedir* nil) is getting evaluated.
      5534  
      5535 (declare 'atstrings t)
      5536  
      5537 (= this-site*    "My Forum"
      5538    site-url*     "http://news.yourdomain.com/"
      5539    parent-url*   "http://www.yourdomain.com"
      5540    favicon-url*  ""
      5541    site-desc*    "What this site is about."               ; for rss feed
      5542    site-color*   (color 180 180 180)
      5543    border-color* (color 180 180 180)
      5544    prefer-url*   t)
      5545  
      5546  
      5547 ; Structures
      5548  
      5549 ; Could add (html) types like choice, yesno to profile fields.  But not 
      5550 ; as part of deftem, which is defstruct.  Need another mac on top of 
      5551 ; deftem.  Should not need the type specs in user-fields.
      5552  
      5553 (deftem profile
      5554   id         nil
      5555   name       nil
      5556   created    (seconds)
      5557   auth       0
      5558   member     nil
      5559   submitted  nil
      5560   votes      nil   ; for now just recent, elts each (time id by sitename dir)
      5561   karma      1
      5562   avg        nil
      5563   weight     .5
      5564   ignore     nil
      5565   email      nil
      5566   about      nil
      5567   showdead   nil
      5568   noprocrast nil
      5569   firstview  nil
      5570   lastview   nil
      5571   maxvisit   20 
      5572   minaway    180
      5573   topcolor   nil
      5574   keys       nil
      5575   delay      0)
      5576  
      5577 (deftem item
      5578   id         nil
      5579   type       nil
      5580   by         nil
      5581   ip         nil
      5582   time       (seconds)
      5583   url        nil
      5584   title      nil
      5585   text       nil
      5586   votes      nil   ; elts each (time ip user type score)
      5587   score      0
      5588   sockvotes  0
      5589   flags      nil
      5590   dead       nil
      5591   deleted    nil
      5592   parts      nil
      5593   parent     nil
      5594   kids       nil
      5595   keys       nil)
      5596  
      5597  
      5598 ; Load and Save
      5599  
      5600 (= newsdir*  "arc/news/"
      5601    storydir* "arc/news/story/"
      5602    profdir*  "arc/news/profile/"
      5603    votedir*  "arc/news/vote/")
      5604  
      5605 (= votes* (table) profs* (table))
      5606  
      5607 (= initload-users* nil)
      5608  
      5609 (def nsv ((o port 8080))
      5610   (map ensure-dir (list arcdir* newsdir* storydir* votedir* profdir*))
      5611   (unless stories* (load-items))
      5612   (if (and initload-users* (empty profs*)) (load-users))
      5613   (asv port))
      5614  
      5615 (def load-users ()
      5616   (pr "load users: ")
      5617   (noisy-each 100 id (dir profdir*)
      5618     (load-user id)))
      5619  
      5620 ; For some reason vote files occasionally get written out in a 
      5621 ; broken way.  The nature of the errors (random missing or extra
      5622 ; chars) suggests the bug is lower-level than anything in Arc.
      5623 ; Which unfortunately means all lists written to disk are probably
      5624 ; vulnerable to it, since that's all save-table does.
      5625  
      5626 (def load-user (u)
      5627   (= (votes* u) (load-table (+ votedir* u))
      5628      (profs* u) (temload 'profile (+ profdir* u)))
      5629   u)
      5630  
      5631 ; Have to check goodname because some user ids come from http requests.
      5632 ; So this is like safe-item.  Don't need a sep fn there though.
      5633  
      5634 (def profile (u)
      5635   (or (profs* u)
      5636       (aand (goodname u)
      5637             (file-exists (+ profdir* u))
      5638             (= (profs* u) (temload 'profile it)))))
      5639  
      5640 (def votes (u)
      5641   (or (votes* u)
      5642       (aand (file-exists (+ votedir* u))
      5643             (= (votes* u) (load-table it)))))
      5644           
      5645 (def init-user (u)
      5646   (= (votes* u) (table) 
      5647      (profs* u) (inst 'profile 'id u))
      5648   (save-votes u)
      5649   (save-prof u)
      5650   u)
      5651  
      5652 ; Need this because can create users on the server (for other apps)
      5653 ; without setting up places to store their state as news users.
      5654 ; See the admin op in app.arc.  So all calls to login-page from the 
      5655 ; news app need to call this in the after-login fn.
      5656  
      5657 (def ensure-news-user (u)
      5658   (if (profile u) u (init-user u)))
      5659  
      5660 (def save-votes (u) (save-table (votes* u) (+ votedir* u)))
      5661  
      5662 (def save-prof  (u) (save-table (profs* u) (+ profdir* u)))
      5663  
      5664 (mac uvar (u k) `((profile ,u) ',k))
      5665  
      5666 (mac karma   (u) `(uvar ,u karma))
      5667 (mac ignored (u) `(uvar ,u ignore))
      5668  
      5669 ; Note that users will now only consider currently loaded users.
      5670  
      5671 (def users ((o f idfn)) 
      5672   (keep f (keys profs*)))
      5673  
      5674 (def check-key (u k)
      5675   (and u (mem k (uvar u keys))))
      5676  
      5677 (def author (u i) (is u i!by))
      5678  
      5679  
      5680 (= stories* nil comments* nil 
      5681    items* (table) url->story* (table)
      5682    maxid* 0 initload* 15000)
      5683  
      5684 ; The dir expression yields stories in order of file creation time 
      5685 ; (because arc infile truncates), so could just rev the list instead of
      5686 ; sorting, but sort anyway.
      5687  
      5688 ; Note that stories* etc only include the initloaded (i.e. recent)
      5689 ; ones, plus those created since this server process started.
      5690  
      5691 ; Could be smarter about preloading by keeping track of popular pages.
      5692  
      5693 (def load-items ()
      5694   (system (+ "rm " storydir* "*.tmp"))
      5695   (pr "load items: ") 
      5696   (with (items (table)
      5697          ids   (sort > (map int (dir storydir*))))
      5698     (if ids (= maxid* (car ids)))
      5699     (noisy-each 100 id (firstn initload* ids)
      5700       (let i (load-item id)
      5701         (push i (items i!type))))
      5702     (= stories*  (rev (merge (compare < !id) items!story items!poll))
      5703        comments* (rev items!comment))
      5704     (hook 'initload items))
      5705   (ensure-topstories))
      5706  
      5707 (def ensure-topstories ()
      5708   (aif (errsafe (readfile1 (+ newsdir* "topstories")))
      5709        (= ranked-stories* (map item it))
      5710        (do (prn "ranking stories.") 
      5711            (flushout)
      5712            (gen-topstories))))
      5713  
      5714 (def astory   (i) (is i!type 'story))
      5715 (def acomment (i) (is i!type 'comment))
      5716 (def apoll    (i) (is i!type 'poll))
      5717  
      5718 (def load-item (id)
      5719   (let i (temload 'item (+ storydir* id))
      5720     (= (items* id) i)
      5721     (awhen (and (astory&live i) (check i!url ~blank))
      5722       (register-url i it))
      5723     i))
      5724  
      5725 ; Note that duplicates are only prevented of items that have at some 
      5726 ; point been loaded. 
      5727  
      5728 (def register-url (i url)
      5729   (= (url->story* (canonical-url url)) i!id))
      5730  
      5731 ; redefined later
      5732  
      5733 (= stemmable-sites* (table))
      5734  
      5735 (def canonical-url (url)
      5736   (if (stemmable-sites* (sitename url))
      5737       (cut url 0 (pos #\? url))
      5738       url))
      5739  
      5740 (def new-item-id ()
      5741   (evtil (++ maxid*) [~file-exists (+ storydir* _)]))
      5742  
      5743 (def item (id)
      5744   (or (items* id) (errsafe:load-item id)))
      5745  
      5746 (def kids (i) (map item i!kids))
      5747  
      5748 ; For use on external item references (from urls).  Checks id is int 
      5749 ; because people try e.g. item?id=363/blank.php
      5750  
      5751 (def safe-item (id)
      5752   (ok-id&item (if (isa id 'string) (saferead id) id)))
      5753  
      5754 (def ok-id (id) 
      5755   (and (exact id) (<= 1 id maxid*)))
      5756  
      5757 (def arg->item (req key)
      5758   (safe-item:saferead (arg req key)))
      5759  
      5760 (def live (i) (nor i!dead i!deleted))
      5761  
      5762 (def save-item (i) (save-table i (+ storydir* i!id)))
      5763  
      5764 (def kill (i how)
      5765   (unless i!dead
      5766     (log-kill i how)
      5767     (wipe (comment-cache* i!id))
      5768     (set i!dead)
      5769     (save-item i)))
      5770  
      5771 (= kill-log* nil)
      5772  
      5773 (def log-kill (i how)
      5774   (push (list i!id how) kill-log*))
      5775  
      5776 (mac each-loaded-item (var . body)
      5777   (w/uniq g
      5778     `(let ,g nil
      5779        (loop (= ,g maxid*) (> ,g 0) (-- ,g)
      5780          (whenlet ,var (items* ,g)
      5781            ,@body)))))
      5782  
      5783 (def loaded-items (test)
      5784   (accum a (each-loaded-item i (test&a i))))
      5785  
      5786 (def newslog args (apply srvlog 'news args))
      5787  
      5788  
      5789 ; Ranking
      5790  
      5791 ; Votes divided by the age in hours to the gravityth power.
      5792 ; Would be interesting to scale gravity in a slider.
      5793  
      5794 (= gravity* 1.8 timebase* 120 front-threshold* 1 
      5795    nourl-factor* .4 lightweight-factor* .3 )
      5796  
      5797 (def frontpage-rank (s (o scorefn realscore) (o gravity gravity*))
      5798   (* (/ (let base (- (scorefn s) 1)
      5799           (if (> base 0) (expt base .8) base))
      5800         (expt (/ (+ (item-age s) timebase*) 60) gravity))
      5801      (if (no (in s!type 'story 'poll))  .5
      5802          (blank s!url)                  nourl-factor*
      5803          (lightweight s)                (min lightweight-factor* 
      5804                                              (contro-factor s))
      5805                                         (contro-factor s))))
      5806  
      5807 (def contro-factor (s)
      5808   (aif (check (visible-family nil s) [> _ 20])
      5809        (min 1 (expt (/ (realscore s) it) 2))
      5810        1))
      5811  
      5812 (def realscore (i) (- i!score i!sockvotes))
      5813  
      5814 (disktable lightweights* (+ newsdir* "lightweights"))
      5815  
      5816 (def lightweight (s)
      5817   (or s!dead
      5818       (mem 'rally s!keys)  ; title is a rallying cry
      5819       (mem 'image s!keys)  ; post is mainly image(s)
      5820       (lightweights* (sitename s!url))
      5821       (lightweight-url s!url)))
      5822  
      5823 (defmemo lightweight-url (url)
      5824   (in (downcase (last (tokens url #\.))) "png" "jpg" "jpeg"))
      5825  
      5826 (def item-age (i) (minutes-since i!time))
      5827  
      5828 (def user-age (u) (minutes-since (uvar u created)))
      5829  
      5830 ; Only looks at the 1000 most recent stories, which might one day be a 
      5831 ; problem if there is massive spam. 
      5832  
      5833 (def gen-topstories ()
      5834   (= ranked-stories* (rank-stories 180 1000 (memo frontpage-rank))))
      5835  
      5836 (def save-topstories ()
      5837   (writefile (map !id (firstn 180 ranked-stories*))
      5838              (+ newsdir* "topstories")))
      5839  
      5840 (def rank-stories (n consider scorefn)
      5841   (bestn n (compare > scorefn) (latest-items metastory nil consider)))
      5842  
      5843 ; With virtual lists the above call to latest-items could be simply:
      5844 ; (map item (retrieve consider metastory:item (gen maxid* [- _ 1])))
      5845  
      5846 (def latest-items (test (o stop) (o n))
      5847   (accum a
      5848     (catch 
      5849       (down id maxid* 1
      5850         (let i (item id)
      5851           (if (or (and stop (stop i)) (and n (<= n 0))) 
      5852               (throw))
      5853           (when (test i) 
      5854             (a i) 
      5855             (if n (-- n))))))))
      5856              
      5857 ; redefined later
      5858  
      5859 (def metastory (i) (and i (in i!type 'story 'poll)))
      5860  
      5861 (def adjust-rank (s (o scorefn frontpage-rank))
      5862   (insortnew (compare > (memo scorefn)) s ranked-stories*)
      5863   (save-topstories))
      5864  
      5865 ; If something rose high then stopped getting votes, its score would
      5866 ; decline but it would stay near the top.  Newly inserted stories would
      5867 ; thus get stuck in front of it. I avoid this by regularly adjusting 
      5868 ; the rank of a random top story.
      5869  
      5870 (defbg rerank-random 30 (rerank-random))
      5871  
      5872 (def rerank-random ()
      5873   (when ranked-stories*
      5874     (adjust-rank (ranked-stories* (rand (min 50 (len ranked-stories*)))))))
      5875  
      5876 (def topstories (user n (o threshold front-threshold*))
      5877   (retrieve n 
      5878             [and (>= (realscore _) threshold) (cansee user _)]
      5879             ranked-stories*))
      5880  
      5881 (= max-delay* 10)
      5882  
      5883 (def cansee (user i)
      5884   (if i!deleted   (admin user)
      5885       i!dead      (or (author user i) (seesdead user))
      5886       (delayed i) (author user i)
      5887       t))
      5888  
      5889 (let mature (table)
      5890   (def delayed (i)
      5891     (and (no (mature i!id))
      5892          (acomment i)
      5893          (or (< (item-age i) (min max-delay* (uvar i!by delay)))
      5894              (do (set (mature i!id))
      5895                  nil)))))
      5896  
      5897 (def seesdead (user)
      5898   (or (and user (uvar user showdead) (no (ignored user)))
      5899       (editor user)))
      5900  
      5901 (def visible (user is)
      5902   (keep [cansee user _] is))
      5903  
      5904 (def cansee-descendant (user c)
      5905   (or (cansee user c)
      5906       (some [cansee-descendant user (item _)] 
      5907             c!kids)))
      5908   
      5909 (def editor (u) 
      5910   (and u (or (admin u) (> (uvar u auth) 0))))
      5911  
      5912 (def member (u) 
      5913   (and u (or (admin u) (uvar u member))))
      5914  
      5915  
      5916 ; Page Layout
      5917  
      5918 (= up-url* "grayarrow.gif" down-url* "graydown.gif" logo-url* "arc.png")
      5919  
      5920 (defopr favicon.ico req favicon-url*)
      5921  
      5922 ; redefined later
      5923  
      5924 (def gen-css-url ()
      5925   (prn "<link rel=\"stylesheet\" type=\"text/css\" href=\"news.css\">"))
      5926  
      5927 (mac npage (title . body)
      5928   `(tag html 
      5929      (tag head 
      5930        (gen-css-url)
      5931        (prn "<link rel=\"shortcut icon\" href=\"" favicon-url* "\">")
      5932        (tag script (pr votejs*))
      5933        (tag title (pr ,title)))
      5934      (tag body 
      5935        (center
      5936          (tag (table border 0 cellpadding 0 cellspacing 0 width "85%"
      5937                      bgcolor sand)
      5938            ,@body)))))
      5939  
      5940 (= pagefns* nil)
      5941  
      5942 (mac fulltop (user lid label title whence . body)
      5943   (w/uniq (gu gi gl gt gw)
      5944     `(with (,gu ,user ,gi ,lid ,gl ,label ,gt ,title ,gw ,whence)
      5945        (npage (+ this-site* (if ,gt (+ bar* ,gt) ""))
      5946          (if (check-procrast ,gu)
      5947              (do (pagetop 'full ,gi ,gl ,gt ,gu ,gw)
      5948                  (hook 'page ,gu ,gl)
      5949                  ,@body)
      5950              (row (procrast-msg ,gu ,gw)))))))
      5951  
      5952 (mac longpage (user t1 lid label title whence . body)
      5953   (w/uniq (gu gt gi)
      5954     `(with (,gu ,user ,gt ,t1 ,gi ,lid)
      5955        (fulltop ,gu ,gi ,label ,title ,whence
      5956          (trtd ,@body)
      5957          (trtd (vspace 10)
      5958                (color-stripe (main-color ,gu))
      5959                (br)
      5960                (center
      5961                  (hook 'longfoot)
      5962                  (admin-bar ,gu (- (msec) ,gt) ,whence)))))))
      5963  
      5964 (def admin-bar (user elapsed whence)
      5965   (when (admin user)
      5966     (br2)
      5967     (w/bars
      5968       (pr (len items*) "/" maxid* " loaded")
      5969       (pr (round (/ (memory) 1000000)) " mb")
      5970       (pr elapsed " msec")
      5971       (link "settings" "newsadmin")
      5972       (hook 'admin-bar user whence))))
      5973  
      5974 (def color-stripe (c)
      5975   (tag (table width "100%" cellspacing 0 cellpadding 1)
      5976     (tr (tdcolor c))))
      5977  
      5978 (mac shortpage (user lid label title whence . body)
      5979   `(fulltop ,user ,lid ,label ,title ,whence 
      5980      (trtd ,@body)))
      5981  
      5982 (mac minipage (label . body)
      5983   `(npage (+ this-site* bar* ,label)
      5984      (pagetop nil nil ,label)
      5985      (trtd ,@body)))
      5986  
      5987 (def msgpage (user msg (o title))
      5988   (minipage (or title "Message")
      5989     (spanclass admin
      5990       (center (if (len> msg 80) 
      5991                   (widtable 500 msg)
      5992                   (pr msg))))
      5993     (br2)))
      5994  
      5995 (= (max-age* 'news.css) 86400)   ; cache css in browser for 1 day
      5996  
      5997 ; turn off server caching via (= caching* 0) or won't see changes
      5998  
      5999 (defop news.css req
      6000   (pr "
      6001 body  { font-family:Verdana; font-size:10pt; color:#828282; }
      6002 td    { font-family:Verdana; font-size:10pt; color:#828282; }
      6003  
      6004 .admin td   { font-family:Verdana; font-size:8.5pt; color:#000000; }
      6005 .subtext td { font-family:Verdana; font-size:  7pt; color:#828282; }
      6006  
      6007 input    { font-family:Courier; font-size:10pt; color:#000000; }
      6008 input[type=\"submit\"] { font-family:Verdana; }
      6009 textarea { font-family:Courier; font-size:10pt; color:#000000; }
      6010  
      6011 a:link    { color:#000000; text-decoration:none; } 
      6012 a:visited { color:#828282; text-decoration:none; }
      6013  
      6014 .default { font-family:Verdana; font-size: 10pt; color:#828282; }
      6015 .admin   { font-family:Verdana; font-size:8.5pt; color:#000000; }
      6016 .title   { font-family:Verdana; font-size: 10pt; color:#828282; }
      6017 .adtitle { font-family:Verdana; font-size:  9pt; color:#828282; }
      6018 .subtext { font-family:Verdana; font-size:  7pt; color:#828282; }
      6019 .yclinks { font-family:Verdana; font-size:  8pt; color:#828282; }
      6020 .pagetop { font-family:Verdana; font-size: 10pt; color:#222222; }
      6021 .comhead { font-family:Verdana; font-size:  8pt; color:#828282; }
      6022 .comment { font-family:Verdana; font-size:  9pt; }
      6023 .dead    { font-family:Verdana; font-size:  9pt; color:#dddddd; }
      6024  
      6025 .comment a:link, .comment a:visited { text-decoration:underline;}
      6026 .dead a:link, .dead a:visited { color:#dddddd; }
      6027 .pagetop a:visited { color:#000000;}
      6028 .topsel a:link, .topsel a:visited { color:#ffffff; }
      6029  
      6030 .subtext a:link, .subtext a:visited { color:#828282; }
      6031 .subtext a:hover { text-decoration:underline; }
      6032  
      6033 .comhead a:link, .subtext a:visited { color:#828282; }
      6034 .comhead a:hover { text-decoration:underline; }
      6035  
      6036 .default p { margin-top: 8px; margin-bottom: 0px; }
      6037  
      6038 .pagebreak {page-break-before:always}
      6039  
      6040 pre { overflow: auto; padding: 2px; max-width:600px; }
      6041 pre:hover {overflow:auto} "))
      6042  
      6043 ; only need pre padding because of a bug in Mac Firefox
      6044  
      6045 ; Without setting the bottom margin of p tags to 0, 1- and n-para comments
      6046 ; have different space at the bottom.  This solution suggested by Devin.
      6047 ; Really am using p tags wrong (as separators rather than wrappers) and the
      6048 ; correct thing to do would be to wrap each para in <p></p>.  Then whatever
      6049 ; I set the bottom spacing to, it would be the same no matter how many paras
      6050 ; in a comment. In this case by setting the bottom spacing of p to 0, I'm
      6051 ; making it the same as no p, which is what the first para has.
      6052  
      6053 ; supplied by pb
      6054 ;.vote { padding-left:2px; vertical-align:top; }
      6055 ;.comment { margin-top:1ex; margin-bottom:1ex; color:black; }
      6056 ;.vote IMG { border:0; margin: 3px 2px 3px 2px; }
      6057 ;.reply { font-size:smaller; text-decoration:underline !important; }
      6058  
      6059 (= votejs* "
      6060 function byId(id) {
      6061   return document.getElementById(id);
      6062 }
      6063  
      6064 function vote(node) {
      6065   var v = node.id.split(/_/);   // {'up', '123'}
      6066   var item = v[1]; 
      6067  
      6068   // adjust score
      6069   var score = byId('score_' + item);
      6070   var newscore = parseInt(score.innerHTML) + (v[0] == 'up' ? 1 : -1);
      6071   score.innerHTML = newscore + (newscore == 1 ? ' point' : ' points');
      6072  
      6073   // hide arrows
      6074   byId('up_'   + item).style.visibility = 'hidden';
      6075   byId('down_' + item).style.visibility = 'hidden';
      6076  
      6077   // ping server
      6078   var ping = new Image();
      6079   ping.src = node.href;
      6080  
      6081   return false; // cancel browser nav
      6082 } ")
      6083  
      6084  
      6085 ; Page top
      6086  
      6087 (= sand (color 246 246 239) textgray (gray 130))
      6088  
      6089 (def main-color (user) 
      6090   (aif (and user (uvar user topcolor))
      6091        (hex>color it)
      6092        site-color*))
      6093  
      6094 (def pagetop (switch lid label (o title) (o user) (o whence))
      6095 ; (tr (tdcolor black (vspace 5)))
      6096   (tr (tdcolor (main-color user)
      6097         (tag (table border 0 cellpadding 0 cellspacing 0 width "100%"
      6098                     style "padding:2px")
      6099           (tr (gen-logo)
      6100               (when (is switch 'full)
      6101                 (tag (td style "line-height:12pt; height:10px;")
      6102                   (spanclass pagetop
      6103                     (tag b (link this-site* "news"))
      6104                     (hspace 10)
      6105                     (toprow user label))))
      6106              (if (is switch 'full)
      6107                  (tag (td style "text-align:right;padding-right:4px;")
      6108                    (spanclass pagetop (topright user whence)))
      6109                  (tag (td style "line-height:12pt; height:10px;")
      6110                    (spanclass pagetop (prbold label))))))))
      6111   (map [_ user] pagefns*)
      6112   (spacerow 10))
      6113  
      6114 (def gen-logo ()
      6115   (tag (td style "width:18px;padding-right:4px")
      6116     (tag (a href parent-url*)
      6117       (tag (img src logo-url* width 18 height 18 
      6118                 style "border:1px #@(hexrep border-color*) solid;")))))
      6119  
      6120 (= toplabels* '(nil "welcome" "new" "threads" "comments" "leaders" "*"))
      6121  
      6122 ; redefined later
      6123  
      6124 (= welcome-url* "welcome")
      6125  
      6126 (def toprow (user label)
      6127   (w/bars 
      6128     (when (noob user)
      6129       (toplink "welcome" welcome-url* label)) 
      6130     (toplink "new" "newest" label)
      6131     (when user
      6132       (toplink "threads" (threads-url user) label))
      6133     (toplink "comments" "newcomments" label)
      6134     (toplink "leaders"  "leaders"     label)
      6135     (hook 'toprow user label)
      6136     (link "submit")
      6137     (unless (mem label toplabels*)
      6138       (fontcolor white (pr label)))))
      6139  
      6140 (def toplink (name dest label)
      6141   (tag-if (is name label) (span class 'topsel)
      6142     (link name dest)))
      6143  
      6144 (def topright (user whence (o showkarma t))
      6145   (when user 
      6146     (userlink user user nil)
      6147     (when showkarma (pr  "&nbsp;(@(karma user))"))
      6148     (pr "&nbsp;|&nbsp;"))
      6149   (if user
      6150       (rlinkf 'logout (req)
      6151         (when-umatch/r user req
      6152           (logout-user user)
      6153           whence))
      6154       (onlink "login"
      6155         (login-page 'both nil 
      6156                     (list (fn (u ip) 
      6157                             (ensure-news-user u)
      6158                             (newslog ip u 'top-login))
      6159                           whence)))))
      6160  
      6161 (def noob (user)
      6162   (and user (< (days-since (uvar user created)) 1)))
      6163  
      6164  
      6165 ; News-Specific Defop Variants
      6166  
      6167 (mac defopt (name parm test msg . body)
      6168   `(defop ,name ,parm
      6169      (if (,test (get-user ,parm))
      6170          (do ,@body)
      6171          (login-page 'both (+ "Please log in" ,msg ".")
      6172                      (list (fn (u ip) (ensure-news-user u))
      6173                            (string ',name (reassemble-args ,parm)))))))
      6174  
      6175 (mac defopg (name parm . body)
      6176   `(defopt ,name ,parm idfn "" ,@body))
      6177  
      6178 (mac defope (name parm . body)
      6179   `(defopt ,name ,parm editor " as an editor" ,@body))
      6180  
      6181 (mac defopa (name parm . body)
      6182   `(defopt ,name ,parm admin " as an administrator" ,@body))
      6183  
      6184 (mac opexpand (definer name parms . body)
      6185   (w/uniq gr
      6186     `(,definer ,name ,gr
      6187        (with (user (get-user ,gr) ip (,gr 'ip))
      6188          (with ,(and parms (mappend [list _ (list 'arg gr (string _))]
      6189                                     parms))
      6190            (newslog ip user ',name ,@parms)
      6191            ,@body)))))
      6192  
      6193 (= newsop-names* nil)
      6194  
      6195 (mac newsop args
      6196   `(do (pushnew ',(car args) newsop-names*)
      6197        (opexpand defop ,@args)))
      6198  
      6199 (mac adop (name parms . body)
      6200   (w/uniq g
      6201     `(opexpand defopa ,name ,parms 
      6202        (let ,g (string ',name)
      6203          (shortpage user nil ,g ,g ,g
      6204            ,@body)))))
      6205  
      6206 (mac edop (name parms . body)
      6207   (w/uniq g
      6208     `(opexpand defope ,name ,parms 
      6209        (let ,g (string ',name)
      6210          (shortpage user nil ,g ,g ,g
      6211            ,@body)))))
      6212  
      6213  
      6214 ; News Admin
      6215  
      6216 (defopa newsadmin req 
      6217   (let user (get-user req)
      6218     (newslog req!ip user 'newsadmin)
      6219     (newsadmin-page user)))
      6220  
      6221 ; Note that caching* is reset to val in source when restart server.
      6222  
      6223 (def nad-fields ()
      6224   `((num      caching         ,caching*                       t t)
      6225     (bigtoks  comment-kill    ,comment-kill*                  t t)
      6226     (bigtoks  comment-ignore  ,comment-ignore*                t t)
      6227     (bigtoks  lightweights    ,(sort < (keys lightweights*))  t t)))
      6228  
      6229 ; Need a util like vars-form for a collection of variables.
      6230 ; Or could generalize vars-form to think of places (in the setf sense).
      6231  
      6232 (def newsadmin-page (user)
      6233   (shortpage user nil nil "newsadmin" "newsadmin"
      6234     (vars-form user 
      6235                (nad-fields)
      6236                (fn (name val)
      6237                  (case name
      6238                    caching            (= caching* val)
      6239                    comment-kill       (todisk comment-kill* val)
      6240                    comment-ignore     (todisk comment-ignore* val)
      6241                    lightweights       (todisk lightweights* (memtable val))
      6242                    ))
      6243                (fn () (newsadmin-page user))) 
      6244     (br2)
      6245     (aform (fn (req)
      6246              (with (user (get-user req) subject (arg req "id"))
      6247                (if (profile subject)
      6248                    (do (killallby subject)
      6249                        (submitted-page user subject))
      6250                    (admin&newsadmin-page user))))
      6251       (single-input "" 'id 20 "kill all by"))
      6252     (br2)
      6253     (aform (fn (req)
      6254              (let user (get-user req)
      6255                (set-ip-ban user (arg req "ip") t)
      6256                (admin&newsadmin-page user)))
      6257       (single-input "" 'ip 20 "ban ip"))))
      6258  
      6259  
      6260 ; Users
      6261  
      6262 (newsop user (id)
      6263   (if (only.profile id)
      6264       (user-page user id)
      6265       (pr "No such user.")))
      6266  
      6267 (def user-page (user subject)
      6268   (let here (user-url subject)
      6269     (shortpage user nil nil (+ "Profile: " subject) here
      6270       (profile-form user subject)
      6271       (br2)
      6272       (when (some astory:item (uvar subject submitted))
      6273         (underlink "submissions" (submitted-url subject)))
      6274       (when (some acomment:item (uvar subject submitted))
      6275         (sp)
      6276         (underlink "comments" (threads-url subject)))
      6277       (hook 'user user subject))))
      6278  
      6279 (def profile-form (user subject)
      6280   (let prof (profile subject) 
      6281     (vars-form user
      6282                (user-fields user subject)
      6283                (fn (name val) 
      6284                  (when (and (is name 'ignore) val (no prof!ignore))
      6285                    (log-ignore user subject 'profile))
      6286                  (= (prof name) val))
      6287                (fn () (save-prof subject)
      6288                       (user-page user subject)))))
      6289  
      6290 (= topcolor-threshold* 250)
      6291  
      6292 (def user-fields (user subject)
      6293   (withs (e (editor user) 
      6294           a (admin user) 
      6295           w (is user subject)
      6296           k (and w (> (karma user) topcolor-threshold*))
      6297           u (or a w)
      6298           m (or a (and (member user) w))
      6299           p (profile subject))
      6300     `((string  user       ,subject                                  t   nil)
      6301       (string  name       ,(p 'name)                               ,m  ,m)
      6302       (string  created    ,(text-age:user-age subject)              t   nil)
      6303       (string  password   ,(resetpw-link)                          ,w   nil)
      6304       (string  saved      ,(saved-link user subject)               ,u   nil)
      6305       (int     auth       ,(p 'auth)                               ,e  ,a)
      6306       (yesno   member     ,(p 'member)                             ,a  ,a)
      6307       (posint  karma      ,(p 'karma)                               t  ,a)
      6308       (num     avg        ,(p 'avg)                                ,a  nil)
      6309       (yesno   ignore     ,(p 'ignore)                             ,e  ,e)
      6310       (num     weight     ,(p 'weight)                             ,a  ,a)
      6311       (mdtext2 about      ,(p 'about)                               t  ,u)
      6312       (string  email      ,(p 'email)                              ,u  ,u)
      6313       (yesno   showdead   ,(p 'showdead)                           ,u  ,u)
      6314       (yesno   noprocrast ,(p 'noprocrast)                         ,u  ,u)
      6315       (string  firstview  ,(p 'firstview)                          ,a   nil)
      6316       (string  lastview   ,(p 'lastview)                           ,a   nil)
      6317       (posint  maxvisit   ,(p 'maxvisit)                           ,u  ,u)
      6318       (posint  minaway    ,(p 'minaway)                            ,u  ,u)
      6319       (sexpr   keys       ,(p 'keys)                               ,a  ,a)
      6320       (hexcol  topcolor   ,(or (p 'topcolor) (hexrep site-color*)) ,k  ,k)
      6321       (int     delay      ,(p 'delay)                              ,u  ,u))))
      6322  
      6323 (def saved-link (user subject)
      6324   (when (or (admin user) (is user subject))
      6325     (let n (if (len> (votes subject) 500) 
      6326                "many" 
      6327                (len (voted-stories user subject)))
      6328       (if (is n 0)
      6329           ""
      6330           (tostring (underlink n (saved-url subject)))))))
      6331  
      6332 (def resetpw-link ()
      6333   (tostring (underlink "reset password" "resetpw")))
      6334  
      6335 (newsop welcome ()
      6336   (pr "Welcome to " this-site* ", " user "!"))
      6337  
      6338  
      6339 ; Main Operators
      6340  
      6341 ; remember to set caching to 0 when testing non-logged-in 
      6342  
      6343 (= caching* 1 perpage* 30 threads-perpage* 10 maxend* 210)
      6344  
      6345 ; Limiting that newscache can't take any arguments except the user.
      6346 ; To allow other arguments, would have to turn the cache from a single 
      6347 ; stored value to a hash table whose keys were lists of arguments.
      6348  
      6349 (mac newscache (name user time . body)
      6350   (w/uniq gc
      6351     `(let ,gc (cache (fn () (* caching* ,time))
      6352                      (fn () (tostring (let ,user nil ,@body))))
      6353        (def ,name (,user) 
      6354          (if ,user 
      6355              (do ,@body) 
      6356              (pr (,gc)))))))
      6357  
      6358  
      6359 (newsop news () (newspage user))
      6360  
      6361 (newsop ||   () (newspage user))
      6362  
      6363 ;(newsop index.html () (newspage user))
      6364  
      6365 (newscache newspage user 90
      6366   (listpage user (msec) (topstories user maxend*) nil nil "news"))
      6367  
      6368 (def listpage (user t1 items label title (o url label) (o number t))
      6369   (hook 'listpage user)
      6370   (longpage user t1 nil label title url
      6371     (display-items user items label title url 0 perpage* number)))
      6372  
      6373  
      6374 (newsop newest () (newestpage user))
      6375  
      6376 ; Note: dead/deleted items will persist for the remaining life of the 
      6377 ; cached page.  If this were a prob, could make deletion clear caches.
      6378  
      6379 (newscache newestpage user 40
      6380   (listpage user (msec) (newstories user maxend*) "new" "New Links" "newest"))
      6381  
      6382 (def newstories (user n)
      6383   (retrieve n [cansee user _] stories*))
      6384  
      6385  
      6386 (newsop best () (bestpage user))
      6387  
      6388 (newscache bestpage user 1000
      6389   (listpage user (msec) (beststories user maxend*) "best" "Top Links"))
      6390  
      6391 ; As no of stories gets huge, could test visibility in fn sent to best.
      6392  
      6393 (def beststories (user n)
      6394   (bestn n (compare > realscore) (visible user stories*)))
      6395  
      6396  
      6397 (newsop noobstories () (noobspage user stories*))
      6398 (newsop noobcomments () (noobspage user comments*))
      6399  
      6400 (def noobspage (user source)
      6401   (listpage user (msec) (noobs user maxend* source) "noobs" "New Accounts"))
      6402  
      6403 (def noobs (user n source)
      6404   (retrieve n [and (cansee user _) (bynoob _)] source))
      6405  
      6406 (def bynoob (i)
      6407   (< (- (user-age i!by) (item-age i)) 2880))
      6408  
      6409  
      6410 (newsop bestcomments () (bestcpage user))
      6411  
      6412 (newscache bestcpage user 1000
      6413   (listpage user (msec) (bestcomments user maxend*) 
      6414             "best comments" "Best Comments" "bestcomments" nil))
      6415  
      6416 (def bestcomments (user n)
      6417   (bestn n (compare > realscore) (visible user comments*)))
      6418  
      6419  
      6420 (newsop lists () 
      6421   (longpage user (msec) nil "lists" "Lists" "lists"
      6422     (sptab
      6423       (row (link "best")         "Highest voted recent links.")
      6424       (row (link "active")       "Most active current discussions.")
      6425       (row (link "bestcomments") "Highest voted recent comments.")
      6426       (row (link "noobstories")  "Submissions from new accounts.")
      6427       (row (link "noobcomments") "Comments from new accounts.")
      6428       (when (admin user)
      6429         (map row:link
      6430              '(optimes topips flagged killed badguys badlogins goodlogins)))
      6431       (hook 'listspage user))))
      6432  
      6433  
      6434 (def saved-url (user) (+ "saved?id=" user))
      6435  
      6436 (newsop saved (id) 
      6437   (if (only.profile id)
      6438       (savedpage user id) 
      6439       (pr "No such user.")))
      6440  
      6441 (def savedpage (user subject)
      6442   (if (or (is user subject) (admin user))
      6443       (listpage user (msec)
      6444                 (sort (compare < item-age) (voted-stories user subject)) 
      6445                "saved" "Saved Links" (saved-url subject))
      6446       (pr "Can't display that.")))
      6447  
      6448 (def voted-stories (user subject)
      6449   (keep [and (astory _) (cansee user _)]
      6450         (map item (keys:votes subject))))
      6451  
      6452  
      6453 ; Story Display
      6454  
      6455 (def display-items (user items label title whence 
      6456                     (o start 0) (o end perpage*) (o number))
      6457   (zerotable
      6458     (let n start
      6459       (each i (cut items start end)
      6460         (display-item (and number (++ n)) i user whence t)
      6461         (spacerow (if (acomment i) 15 5))))
      6462     (when end
      6463       (let newend (+ end perpage*)
      6464         (when (and (<= newend maxend*) (< end (len items)))
      6465           (spacerow 10)
      6466           (tr (tag (td colspan (if number 2 1)))
      6467               (tag (td class 'title)
      6468                 (morelink display-items 
      6469                           items label title end newend number))))))))
      6470  
      6471 ; This code is inevitably complex because the More fn needs to know 
      6472 ; its own fnid in order to supply a correct whence arg to stuff on 
      6473 ; the page it generates, like logout and delete links.
      6474  
      6475 (def morelink (f items label title . args)
      6476   (tag (a href 
      6477           (url-for
      6478             (afnid (fn (req)
      6479                      (prn)
      6480                      (with (url  (url-for it)     ; it bound by afnid
      6481                             user (get-user req))
      6482                        (newslog req!ip user 'more label)
      6483                        (longpage user (msec) nil label title url
      6484                          (apply f user items label title url args))))))
      6485           rel 'nofollow)
      6486     (pr "More")))
      6487  
      6488 (def display-story (i s user whence)
      6489   (when (or (cansee user s) (s 'kids))
      6490     (tr (display-item-number i)
      6491         (td (votelinks s user whence))
      6492         (titleline s s!url user whence))
      6493     (tr (tag (td colspan (if i 2 1)))    
      6494         (tag (td class 'subtext)
      6495           (hook 'itemline s user)
      6496           (itemline s user)
      6497           (when (in s!type 'story 'poll) (commentlink s user))
      6498           (editlink s user)
      6499           (when (apoll s) (addoptlink s user))
      6500           (unless i (flaglink s user whence))
      6501           (killlink s user whence)
      6502           (blastlink s user whence)
      6503           (blastlink s user whence t)
      6504           (deletelink s user whence)))))
      6505  
      6506 (def display-item-number (i)
      6507   (when i (tag (td align 'right valign 'top class 'title)
      6508             (pr i "."))))
      6509  
      6510 (= follow-threshold* 5)
      6511  
      6512 (def titleline (s url user whence)
      6513   (tag (td class 'title)
      6514     (if (cansee user s)
      6515         (do (deadmark s user)
      6516             (titlelink s url user)
      6517             (pdflink url)
      6518             (awhen (sitename url)
      6519               (spanclass comhead
      6520                 (pr " (" )
      6521                 (if (admin user)
      6522                     (w/rlink (do (set-site-ban user
      6523                                                it
      6524                                                (case (car (banned-sites* it))
      6525                                                  nil    'ignore
      6526                                                  ignore 'kill
      6527                                                  kill   nil))
      6528                                  whence)
      6529                       (let ban (car (banned-sites* it))
      6530                         (tag-if ban (font color (case ban 
      6531                                                   ignore darkred 
      6532                                                   kill   darkblue))
      6533                           (pr it))))
      6534                     (pr it))
      6535                 (pr ") "))))
      6536         (pr (pseudo-text s)))))
      6537  
      6538 (def titlelink (s url user)
      6539   (let toself (blank url)
      6540     (tag (a href (if toself 
      6541                       (item-url s!id) 
      6542                      (or (live s) (author user s) (editor user))
      6543                       url
      6544                       nil)
      6545             rel  (unless (or toself (> (realscore s) follow-threshold*))
      6546                    'nofollow)) 
      6547       (pr s!title))))
      6548  
      6549 (def pdflink (url)
      6550   (awhen (vacuumize url)
      6551     (pr " [") 
      6552     (link "scribd" it)
      6553     (pr "]")))
      6554  
      6555 (defmemo vacuumize (url)
      6556   (and (or (endmatch ".pdf" url) (endmatch ".PDF" url))
      6557        (+ "http://www.scribd.com/vacuum?url=" url)))
      6558       
      6559 (def pseudo-text (i)
      6560   (if i!deleted "[deleted]" "[dead]"))
      6561  
      6562 (def deadmark (i user)
      6563   (when (and i!dead (seesdead user))
      6564     (pr " [dead] "))
      6565   (when (and i!deleted (admin user))
      6566     (pr " [deleted] ")))
      6567  
      6568 (= downvote-threshold* 200 downvote-time* 1440)
      6569  
      6570 (= votewid* 14)
      6571       
      6572 (def votelinks (i user whence (o downtoo))
      6573   (center
      6574     (if (and (cansee user i)
      6575              (or (no user)
      6576                  (no ((votes user) i!id))))
      6577          (do (votelink i user whence 'up)
      6578              (if (and downtoo 
      6579                       (or (admin user)
      6580                           (< (item-age i) downvote-time*))
      6581                       (canvote user i 'down))
      6582                  (do (br)
      6583                      (votelink i user whence 'down))
      6584                  ; don't understand why needed, but is, or a new
      6585                  ; page is generated on voting
      6586                  (tag (span id (+ "down_" i!id)))))
      6587         (author user i)
      6588          (do (fontcolor orange (pr "*"))
      6589              (br)
      6590              (hspace votewid*))
      6591         (hspace votewid*))))
      6592  
      6593 ; could memoize votelink more, esp for non-logged in users,
      6594 ; since only uparrow is shown; could straight memoize
      6595  
      6596 ; redefined later (identically) so the outs catch new vals of up-url, etc.
      6597  
      6598 (def votelink (i user whence dir)
      6599   (tag (a id      (if user (string dir '_ i!id))
      6600           onclick (if user "return vote(this)")
      6601           href    (vote-url user i dir whence))
      6602     (if (is dir 'up)
      6603         (out (gentag img src up-url*   border 0 vspace 3 hspace 2))
      6604         (out (gentag img src down-url* border 0 vspace 3 hspace 2)))))
      6605  
      6606 (def vote-url (user i dir whence)
      6607   (+ "vote?" "for=" i!id
      6608              "&dir=" dir
      6609              (if user (+ "&by=" user "&auth=" (user->cookie* user)))
      6610              "&whence=" (urlencode whence)))
      6611  
      6612 (= lowest-score* -4)
      6613  
      6614 ; Not much stricter than whether to generate the arrow.  Further tests 
      6615 ; applied in vote-for.
      6616  
      6617 (def canvote (user i dir)
      6618   (and user
      6619        (news-type&live i)
      6620        (or (is dir 'up) (> i!score lowest-score*))
      6621        (no ((votes user) i!id))
      6622        (or (is dir 'up)
      6623            (and (acomment i)
      6624                 (> (karma user) downvote-threshold*)
      6625                 (no (aand i!parent (author user (item it))))))))
      6626  
      6627 ; Need the by argument or someone could trick logged in users into 
      6628 ; voting something up by clicking on a link.  But a bad guy doesn't 
      6629 ; know how to generate an auth arg that matches each user's cookie.
      6630  
      6631 (newsop vote (by for dir auth whence)
      6632   (with (i      (safe-item for)
      6633          dir    (saferead dir)
      6634          whence (if whence (urldecode whence) "news"))
      6635     (if (no i)
      6636          (pr "No such item.")
      6637         (no (in dir 'up 'down))
      6638          (pr "Can't make that vote.")
      6639         (and by (or (isnt by user) (isnt (sym auth) (user->cookie* user))))
      6640          (pr "User mismatch.")
      6641         (no user)
      6642          (login-page 'both "You have to be logged in to vote."
      6643                      (list (fn (u ip)
      6644                              (ensure-news-user u)
      6645                              (newslog ip u 'vote-login)
      6646                              (when (canvote u i dir)
      6647                                (vote-for u i dir)
      6648                                (logvote ip u i)))
      6649                            whence))
      6650         (canvote user i dir)
      6651          (do (vote-for by i dir)
      6652              (logvote ip by i))
      6653          (pr "Can't make that vote."))))
      6654  
      6655 (def itemline (i user)
      6656   (when (cansee user i) 
      6657     (when (news-type i) (itemscore i user))
      6658     (byline i user)))
      6659  
      6660 (def itemscore (i (o user))
      6661   (tag (span id (+ "score_" i!id))
      6662     (pr (plural (if (is i!type 'pollopt) (realscore i) i!score)
      6663                 "point")))
      6664   (hook 'itemscore i user))
      6665  
      6666 ; redefined later
      6667  
      6668 (def byline (i user)
      6669   (pr " by @(tostring (userlink user i!by)) @(text-age:item-age i) "))
      6670  
      6671 (def user-url (user) (+ "user?id=" user))
      6672  
      6673 (= show-avg* nil)
      6674  
      6675 (def userlink (user subject (o show-avg t))
      6676   (link (user-name user subject) (user-url subject))
      6677   (awhen (and show-avg* (admin user) show-avg (uvar subject avg))
      6678     (pr " (@(num it 1 t t))")))
      6679  
      6680 (= noob-color* (color 60 150 60))
      6681  
      6682 (def user-name (user subject)
      6683   (if (and (editor user) (ignored subject))
      6684        (tostring (fontcolor darkred (pr subject)))
      6685       (and (editor user) (< (user-age subject) 1440))
      6686        (tostring (fontcolor noob-color* (pr subject)))
      6687       subject))
      6688  
      6689 (= show-threadavg* nil)
      6690  
      6691 (def commentlink (i user)
      6692   (when (cansee user i) 
      6693     (pr bar*)
      6694     (tag (a href (item-url i!id))
      6695       (let n (- (visible-family user i) 1)
      6696         (if (> n 0)
      6697             (do (pr (plural n "comment"))
      6698                 (awhen (and show-threadavg* (admin user) (threadavg i))
      6699                   (pr " (@(num it 1 t t))")))
      6700             (pr "discuss"))))))
      6701  
      6702 (def visible-family (user i)
      6703   (+ (if (cansee user i) 1 0)
      6704      (sum [visible-family user (item _)] i!kids)))
      6705  
      6706 (def threadavg (i)
      6707   (only.avg (map [or (uvar _ avg) 1] 
      6708                  (rem admin (dedup (map !by (keep live (family i))))))))
      6709  
      6710 (= user-changetime* 120 editor-changetime* 1440)
      6711  
      6712 (= everchange* (table) noedit* (table))
      6713  
      6714 (def canedit (user i)
      6715   (or (admin user)
      6716       (and (~noedit* i!type)
      6717            (editor user) 
      6718            (< (item-age i) editor-changetime*))
      6719       (own-changeable-item user i)))
      6720  
      6721 (def own-changeable-item (user i)
      6722   (and (author user i)
      6723        (~mem 'locked i!keys)
      6724        (no i!deleted)
      6725        (or (everchange* i!type)
      6726            (< (item-age i) user-changetime*))))
      6727  
      6728 (def editlink (i user)
      6729   (when (canedit user i)
      6730     (pr bar*)
      6731     (link "edit" (edit-url i))))
      6732  
      6733 (def addoptlink (p user)
      6734   (when (or (admin user) (author user p))
      6735     (pr bar*)
      6736     (onlink "add choice" (add-pollopt-page p user))))
      6737  
      6738 ; reset later
      6739  
      6740 (= flag-threshold* 30 flag-kill-threshold* 7 many-flags* 1)
      6741  
      6742 ; Un-flagging something doesn't unkill it, if it's now no longer
      6743 ; over flag-kill-threshold.  Ok, since arbitrary threshold anyway.
      6744  
      6745 (def flaglink (i user whence)
      6746   (when (and user
      6747              (isnt user i!by)
      6748              (or (admin user) (> (karma user) flag-threshold*)))
      6749     (pr bar*)
      6750     (w/rlink (do (togglemem user i!flags)
      6751                  (when (and (~mem 'nokill i!keys)
      6752                             (len> i!flags flag-kill-threshold*)
      6753                             (< (realscore i) 10)
      6754                             (~find admin:!2 i!vote))
      6755                    (kill i 'flags))
      6756                  whence)
      6757       (pr "@(if (mem user i!flags) 'un)flag"))
      6758     (when (and (admin user) (len> i!flags many-flags*))
      6759       (pr bar* (plural (len i!flags) "flag") " ")
      6760       (w/rlink (do (togglemem 'nokill i!keys)
      6761                    (save-item i)
      6762                    whence)
      6763         (pr (if (mem 'nokill i!keys) "un-notice" "noted"))))))
      6764  
      6765 (def killlink (i user whence)
      6766   (when (admin user)
      6767     (pr bar*)
      6768     (w/rlink (do (zap no i!dead)
      6769                  (if i!dead 
      6770                      (do (pull 'nokill i!keys)
      6771                          (log-kill i user))
      6772                      (pushnew 'nokill i!keys))
      6773                  (save-item i)
      6774                  whence)
      6775       (pr "@(if i!dead 'un)kill"))))
      6776  
      6777 ; Blast kills the submission and bans the user.  Nuke also bans the 
      6778 ; site, so that all future submitters will be ignored.  Does not ban 
      6779 ; the ip address, but that will eventually get banned by maybe-ban-ip.
      6780  
      6781 (def blastlink (i user whence (o nuke))
      6782   (when (and (admin user) 
      6783              (or (no nuke) (~empty i!url)))
      6784     (pr bar*)
      6785     (w/rlink (do (toggle-blast i user nuke)
      6786                  whence)
      6787       (prt (if (ignored i!by) "un-") (if nuke "nuke" "blast")))))
      6788  
      6789 (def toggle-blast (i user (o nuke))
      6790   (atomic
      6791     (if (ignored i!by)
      6792         (do (wipe i!dead (ignored i!by))
      6793             (awhen (and nuke (sitename i!url))
      6794               (set-site-ban user it nil)))
      6795         (do (set i!dead)
      6796             (ignore user i!by (if nuke 'nuke 'blast))
      6797             (awhen (and nuke (sitename i!url))
      6798               (set-site-ban user it 'ignore))))
      6799     (if i!dead (log-kill i user))
      6800     (save-item i)
      6801     (save-prof i!by)))
      6802  
      6803 (def candelete (user i)
      6804   (or (admin user) (own-changeable-item user i)))
      6805  
      6806 (def deletelink (i user whence)
      6807   (when (candelete user i)
      6808     (pr bar*)
      6809     (linkf (if i!deleted "undelete" "delete") (req)
      6810       (let user (get-user req)
      6811         (if (candelete user i)
      6812             (del-confirm-page user i whence)
      6813             (prn "You can't delete that."))))))
      6814  
      6815 ; Undeleting stories could cause a slight inconsistency. If a story
      6816 ; linking to x gets deleted, another submission can take its place in
      6817 ; url->story.  If the original is then undeleted, there will be two 
      6818 ; stories with equal claim to be in url->story.  (The more recent will
      6819 ; win because it happens to get loaded later.)  Not a big problem.
      6820  
      6821 (def del-confirm-page (user i whence)
      6822   (minipage "Confirm"
      6823     (tab 
      6824       ; link never used so not testable but think correct
      6825       (display-item nil i user (flink [del-confirm-page (get-user _) i whence]))
      6826       (spacerow 20)
      6827       (tr (td)
      6828           (td (urform user req
      6829                       (do (when (candelete user i)
      6830                             (= i!deleted (is (arg req "b") "Yes"))
      6831                             (save-item i))
      6832                           whence)
      6833                  (prn "Do you want this to @(if i!deleted 'stay 'be) deleted?")
      6834                  (br2)
      6835                  (but "Yes" "b") (sp) (but "No" "b")))))))
      6836  
      6837 (def permalink (story user)
      6838   (when (cansee user story)
      6839     (pr bar*) 
      6840     (link "link" (item-url story!id))))
      6841  
      6842 (def logvote (ip user story)
      6843   (newslog ip user 'vote (story 'id) (list (story 'title))))
      6844  
      6845 (def text-age (a)
      6846   (tostring
      6847     (if (>= a 1440) (pr (plural (trunc (/ a 1440)) "day")    " ago")
      6848         (>= a   60) (pr (plural (trunc (/ a 60))   "hour")   " ago")
      6849                     (pr (plural (trunc a)          "minute") " ago"))))
      6850  
      6851  
      6852 ; Voting
      6853  
      6854 ; A user needs legit-threshold karma for a vote to count if there has 
      6855 ; already been a vote from the same IP address.  A new account below both
      6856 ; new- thresholds won't affect rankings, though such votes still affect 
      6857 ; scores unless not a legit-user.
      6858  
      6859 (= legit-threshold* 0 new-age-threshold* 0 new-karma-threshold* 2)
      6860  
      6861 (def legit-user (user) 
      6862   (or (editor user)
      6863       (> (karma user) legit-threshold*)))
      6864  
      6865 (def possible-sockpuppet (user)
      6866   (or (ignored user)
      6867       (< (uvar user weight) .5)
      6868       (and (< (user-age user) new-age-threshold*)
      6869            (< (karma user) new-karma-threshold*))))
      6870  
      6871 (= downvote-ratio-limit* .65 recent-votes* nil votewindow* 100)
      6872  
      6873 ; Note: if vote-for by one user changes (s 'score) while s is being
      6874 ; edited by another, the save after the edit will overwrite the change.
      6875 ; Actual votes can't be lost because that field is not editable.  Not a
      6876 ; big enough problem to drag in locking.
      6877  
      6878 (def vote-for (user i (o dir 'up))
      6879   (unless (or ((votes user) i!id) 
      6880               (and (~live i) (isnt user i!by)))
      6881     (withs (ip   (logins* user)
      6882             vote (list (seconds) ip user dir i!score))
      6883       (unless (or (and (or (ignored user) (check-key user 'novote))
      6884                        (isnt user i!by))
      6885                   (and (is dir 'down)
      6886                        (~editor user)
      6887                        (or (check-key user 'nodowns)
      6888                            (> (downvote-ratio user) downvote-ratio-limit*)
      6889                            ; prevention of karma-bombing
      6890                            (just-downvoted user i!by)))
      6891                   (and (~legit-user user)
      6892                        (isnt user i!by)
      6893                        (find [is (cadr _) ip] i!votes))
      6894                   (and (isnt i!type 'pollopt)
      6895                        (biased-voter i vote)))
      6896         (++ i!score (case dir up 1 down -1))
      6897         ; canvote protects against sockpuppet downvote of comments 
      6898         (when (and (is dir 'up) (possible-sockpuppet user))
      6899           (++ i!sockvotes))
      6900         (metastory&adjust-rank i)
      6901         (unless (or (author user i)
      6902                     (and (is ip i!ip) (~editor user))
      6903                     (is i!type 'pollopt))
      6904           (++ (karma i!by) (case dir up 1 down -1))
      6905           (save-prof i!by))
      6906         (wipe (comment-cache* i!id)))
      6907       (if (admin user) (pushnew 'nokill i!keys))
      6908       (push vote i!votes)
      6909       (save-item i)
      6910       (push (list (seconds) i!id i!by (sitename i!url) dir)
      6911             (uvar user votes))
      6912       (= ((votes* user) i!id) vote)
      6913       (save-votes user)
      6914       (zap [firstn votewindow* _] (uvar user votes))
      6915       (save-prof user)
      6916       (push (cons i!id vote) recent-votes*))))
      6917  
      6918 ; redefined later
      6919  
      6920 (def biased-voter (i vote) nil)
      6921  
      6922 ; ugly to access vote fields by position number
      6923  
      6924 (def downvote-ratio (user (o sample 20))
      6925   (ratio [is _.1.3 'down]
      6926          (keep [let by ((item (car _)) 'by)
      6927                  (nor (is by user) (ignored by))]
      6928                (bestn sample (compare > car:cadr) (tablist (votes user))))))
      6929  
      6930 (def just-downvoted (user victim (o n 3))
      6931   (let prev (firstn n (recent-votes-by user))
      6932     (and (is (len prev) n)
      6933          (all (fn ((id sec ip voter dir score))
      6934                 (and (author victim (item id)) (is dir 'down)))
      6935               prev))))
      6936  
      6937 ; Ugly to pluck out fourth element.  Should read votes into a vote
      6938 ; template.  They're stored slightly differently in two diff places: 
      6939 ; in one with the voter in the car and the other without.
      6940  
      6941 (def recent-votes-by (user)
      6942   (keep [is _.3 user] recent-votes*))
      6943  
      6944  
      6945 ; Story Submission
      6946  
      6947 (newsop submit ()
      6948   (if user 
      6949       (submit-page user "" "" t) 
      6950       (submit-login-warning "" "" t)))
      6951  
      6952 (def submit-login-warning ((o url) (o title) (o showtext) (o text))
      6953   (login-page 'both "You have to be logged in to submit."
      6954               (fn (user ip) 
      6955                 (ensure-news-user user)
      6956                 (newslog ip user 'submit-login)
      6957                 (submit-page user url title showtext text))))
      6958  
      6959 (def submit-page (user (o url) (o title) (o showtext) (o text "") (o msg))
      6960   (minipage "Submit"
      6961     (pagemessage msg)
      6962     (urform user req
      6963             (process-story (get-user req)
      6964                            (clean-url (arg req "u"))
      6965                            (striptags (arg req "t"))
      6966                            showtext
      6967                            (and showtext (md-from-form (arg req "x") t))
      6968                            req!ip)
      6969       (tab
      6970         (row "title"  (input "t" title 50))
      6971         (if prefer-url*
      6972             (do (row "url" (input "u" url 50))
      6973                 (when showtext
      6974                   (row "" "<b>or</b>")
      6975                   (row "text" (textarea "x" 4 50 (only.pr text)))))
      6976             (do (row "text" (textarea "x" 4 50 (only.pr text)))
      6977                 (row "" "<b>or</b>")
      6978                 (row "url" (input "u" url 50))))
      6979         (row "" (submit))
      6980         (spacerow 20)
      6981         (row "" submit-instructions*)))))
      6982  
      6983 (= submit-instructions*
      6984    "Leave url blank to submit a question for discussion. If there is 
      6985     no url, the text (if any) will appear at the top of the comments 
      6986     page. If there is a url, the text will be ignored.")
      6987  
      6988 ; For use by outside code like bookmarklet.
      6989 ; http://news.domain.com/submitlink?u=http://foo.com&t=Foo
      6990 ; Added a confirm step to avoid xss hacks.
      6991  
      6992 (newsop submitlink (u t)
      6993   (if user 
      6994       (submit-page user u t)
      6995       (submit-login-warning u t)))
      6996  
      6997 (= title-limit* 80
      6998    retry*       "Please try again."
      6999    toolong*     "Please make title < @title-limit* characters."
      7000    bothblank*   "The url and text fields can't both be blank.  Please
      7001                  either supply a url, or if you're asking a question,
      7002                  put it in the text field."
      7003    toofast*     "You're submitting too fast.  Please slow down.  Thanks."
      7004    spammage*    "Stop spamming us.  You're wasting your time.")
      7005  
      7006 ; Only for annoyingly high-volume spammers. For ordinary spammers it's
      7007 ; enough to ban their sites and ip addresses.
      7008  
      7009 (disktable big-spamsites* (+ newsdir* "big-spamsites"))
      7010  
      7011 (def process-story (user url title showtext text ip)
      7012   (aif (and (~blank url) (live-story-w/url url))
      7013        (do (vote-for user it)
      7014            (item-url it!id))
      7015        (if (no user)
      7016             (flink [submit-login-warning url title showtext text])
      7017            (no (and (or (blank url) (valid-url url)) 
      7018                     (~blank title)))
      7019             (flink [submit-page user url title showtext text retry*])
      7020            (len> title title-limit*)
      7021             (flink [submit-page user url title showtext text toolong*])
      7022            (and (blank url) (blank text))
      7023             (flink [submit-page user url title showtext text bothblank*])
      7024            (let site (sitename url)
      7025              (or (big-spamsites* site) (recent-spam site)))
      7026             (flink [msgpage user spammage*])
      7027            (oversubmitting user ip 'story url)
      7028             (flink [msgpage user toofast*])
      7029            (let s (create-story url (process-title title) text user ip)
      7030              (story-ban-test user s ip url)
      7031              (when (ignored user) (kill s 'ignored))
      7032              (submit-item user s)
      7033              (maybe-ban-ip s)
      7034              "newest"))))
      7035  
      7036 (def submit-item (user i)
      7037   (push i!id (uvar user submitted))
      7038   (save-prof user)
      7039   (vote-for user i))
      7040  
      7041 (def recent-spam (site)
      7042   (and (caris (banned-sites* site) 'ignore)
      7043        (recent-items [is (sitename _!url) site] 720)))
      7044  
      7045 (def recent-items (test minutes)
      7046   (let cutoff (- (seconds) (* 60 minutes))
      7047     (latest-items test [< _!time cutoff])))
      7048  
      7049 ; Turn this on when spam becomes a problem.
      7050  
      7051 (= enforce-oversubmit* nil)
      7052  
      7053 ; New user can't submit more than 2 stories in a 2 hour period.
      7054 ; Give overeager users the key toofast to make limit permanent.
      7055  
      7056 (def oversubmitting (user ip kind (o url))
      7057   (and enforce-oversubmit*
      7058        (or (check-key user 'toofast)
      7059            (ignored user)
      7060            (< (user-age user) new-age-threshold*)
      7061            (< (karma user) new-karma-threshold*))
      7062        (len> (recent-items [or (author user _) (is _!ip ip)] 180)
      7063              (if (is kind 'story)
      7064                  (if (bad-user user) 0 1)
      7065                  (if (bad-user user) 1 10)))))
      7066  
      7067 ; Note that by deliberate tricks, someone could submit a story with a 
      7068 ; blank title.
      7069  
      7070 (diskvar scrubrules* (+ newsdir* "scrubrules"))
      7071  
      7072 (def process-title (s)
      7073   (let s2 (multisubst scrubrules* s)
      7074     (zap upcase (s2 0))
      7075     s2))
      7076  
      7077 (def live-story-w/url (url) 
      7078   (aand (url->story* (canonical-url url)) (check (item it) live)))
      7079  
      7080 (def parse-site (url)
      7081   (rev (tokens (cadr (tokens url [in _ #\/ #\?])) #\.)))
      7082  
      7083 (defmemo sitename (url)
      7084   (and (valid-url url)
      7085        (let toks (parse-site (rem #\space url))
      7086          (if (isa (saferead (car toks)) 'int)
      7087              (tostring (prall toks "" "."))
      7088              (let (t1 t2 t3 . rest) toks  
      7089                (if (and (~in t3 nil "www")
      7090                         (or (mem t1 multi-tld-countries*) 
      7091                             (mem t2 long-domains*)))
      7092                    (+ t3 "." t2 "." t1)
      7093                    (and t2 (+ t2 "." t1))))))))
      7094  
      7095 (= multi-tld-countries* '("uk" "jp" "au" "in" "ph" "tr" "za" "my" "nz" "br" 
      7096                           "mx" "th" "sg" "id" "pk" "eg" "il" "at" "pl"))
      7097  
      7098 (= long-domains* '("blogspot" "wordpress" "livejournal" "blogs" "typepad" 
      7099                    "weebly" "posterous" "blog-city" "supersized" "dreamhosters"
      7100                    ; "sampasite"  "multiply" "wetpaint" ; all spam, just ban
      7101                    "eurekster" "blogsome" "edogo" "blog" "com"))
      7102  
      7103 (def create-story (url title text user ip)
      7104   (newslog ip user 'create url (list title))
      7105   (let s (inst 'item 'type 'story 'id (new-item-id) 
      7106                      'url url 'title title 'text text 'by user 'ip ip)
      7107     (save-item s)
      7108     (= (items* s!id) s)
      7109     (unless (blank url) (register-url s url))
      7110     (push s stories*)
      7111     s))
      7112  
      7113  
      7114 ; Bans
      7115  
      7116 (def ignore (user subject cause)
      7117   (set (ignored subject))
      7118   (save-prof subject)
      7119   (log-ignore user subject cause))
      7120  
      7121 (diskvar ignore-log* (+ newsdir* "ignore-log"))
      7122  
      7123 (def log-ignore (user subject cause)
      7124   (todisk ignore-log* (cons (list subject user cause) ignore-log*)))
      7125  
      7126 ; Kill means stuff with this substring gets killed. Ignore is stronger,
      7127 ; means that user will be auto-ignored.  Eventually this info should
      7128 ; be stored on disk and not in the source code.
      7129  
      7130 (disktable banned-ips*     (+ newsdir* "banned-ips"))   ; was ips
      7131 (disktable banned-sites*   (+ newsdir* "banned-sites")) ; was sites
      7132  
      7133 (diskvar  comment-kill*    (+ newsdir* "comment-kill"))
      7134 (diskvar  comment-ignore*  (+ newsdir* "comment-ignore"))
      7135  
      7136 (= comment-kill* nil ip-ban-threshold* 3)
      7137  
      7138 (def set-ip-ban (user ip yesno (o info))
      7139   (= (banned-ips* ip) (and yesno (list user (seconds) info)))
      7140   (todisk banned-ips*))
      7141  
      7142 (def set-site-ban (user site ban (o info))
      7143   (= (banned-sites* site) (and ban (list ban user (seconds) info)))
      7144   (todisk banned-sites*))
      7145  
      7146 ; Kill submissions from banned ips, but don't auto-ignore users from
      7147 ; them, because eventually ips will become legit again.
      7148  
      7149 ; Note that ban tests are only applied when a link or comment is
      7150 ; submitted, not each time it's edited.  This will do for now.
      7151  
      7152 (def story-ban-test (user i ip url)
      7153   (site-ban-test user i url)
      7154   (ip-ban-test i ip)
      7155   (hook 'story-ban-test user i ip url))
      7156  
      7157 (def site-ban-test (user i url)
      7158   (whenlet ban (banned-sites* (sitename url))
      7159     (if (caris ban 'ignore) (ignore nil user 'site-ban))
      7160     (kill i 'site-ban)))
      7161  
      7162 (def ip-ban-test (i ip)
      7163   (if (banned-ips* ip) (kill i 'banned-ip)))
      7164  
      7165 (def comment-ban-test (user i ip string kill-list ignore-list)
      7166   (when (some [posmatch _ string] ignore-list)
      7167     (ignore nil user 'comment-ban))
      7168   (when (or (banned-ips* ip) (some [posmatch _ string] kill-list))
      7169     (kill i 'comment-ban)))
      7170  
      7171 ; An IP is banned when multiple ignored users have submitted over
      7172 ; ban-threshold* (currently loaded) dead stories from it.  
      7173  
      7174 ; Can consider comments too if that later starts to be a problem,
      7175 ; but the threshold may start to be higher because then you'd be
      7176 ; dealing with trolls rather than spammers.
      7177  
      7178 (def maybe-ban-ip (s)
      7179   (when (and s!dead (ignored s!by))
      7180     (let bads (loaded-items [and _!dead (astory _) (is _!ip s!ip)])
      7181       (when (and (len> bads ip-ban-threshold*)
      7182                  (some [and (ignored _!by) (isnt _!by s!by)] bads))
      7183         (set-ip-ban nil s!ip t)))))
      7184  
      7185 (def killallby (user) 
      7186   (map [kill _ 'all] (submissions user)))
      7187  
      7188 ; Only called from repl.
      7189  
      7190 (def kill-whole-thread (c)
      7191   (kill c 'thread)
      7192   (map kill-whole-thread:item c!kids))
      7193  
      7194  
      7195 ; Polls
      7196  
      7197 ; a way to add a karma threshold for voting in a poll
      7198 ;  or better still an arbitrary test fn, or at least pair of name/threshold.
      7199 ; option to sort the elements of a poll when displaying
      7200 ; exclusive field? (means only allow one vote per poll)
      7201  
      7202 (= poll-threshold* 20)
      7203  
      7204 (newsop newpoll ()
      7205   (if (and user (> (karma user) poll-threshold*))
      7206       (newpoll-page user)
      7207       (pr "Sorry, you need @poll-threshold* karma to create a poll.")))
      7208   
      7209 (def newpoll-page (user (o title "Poll: ") (o text "") (o opts "") (o msg))
      7210   (minipage "New Poll"
      7211     (pagemessage msg)
      7212     (urform user req
      7213             (process-poll (get-user req)
      7214                           (striptags (arg req "t"))
      7215                           (md-from-form (arg req "x") t)
      7216                           (striptags (arg req "o"))
      7217                           req!ip)
      7218       (tab   
      7219         (row "title"   (input "t" title 50))
      7220         (row "text"    (textarea "x" 4 50 (only.pr text)))
      7221         (row ""        "Use blank lines to separate choices:")
      7222         (row "choices" (textarea "o" 7 50 (only.pr opts)))
      7223         (row ""        (submit))))))
      7224  
      7225 (= fewopts* "A poll must have at least two options.")
      7226  
      7227 (def process-poll (user title text opts ip)
      7228   (if (or (blank title) (blank opts))
      7229        (flink [newpoll-page user title text opts retry*])
      7230       (len> title title-limit*)
      7231        (flink [newpoll-page user title text opts toolong*])
      7232       (len< (paras opts) 2)
      7233        (flink [newpoll-page user title text opts fewopts*])
      7234       (atlet p (create-poll (multisubst scrubrules* title) text opts user ip)
      7235         (ip-ban-test p ip)
      7236         (when (ignored user) (kill p 'ignored))
      7237         (submit-item user p)
      7238         (maybe-ban-ip p)
      7239         "newest")))
      7240  
      7241 (def create-poll (title text opts user ip)
      7242   (newslog ip user 'create-poll title)
      7243   (let p (inst 'item 'type 'poll 'id (new-item-id)
      7244                      'title title 'text text 'by user 'ip ip)
      7245     (= p!parts (map get!id (map [create-pollopt p nil nil _ user ip]
      7246                                 (paras opts))))
      7247     (save-item p)
      7248     (= (items* p!id) p)
      7249     (push p stories*)
      7250     p))
      7251  
      7252 (def create-pollopt (p url title text user ip)
      7253   (let o (inst 'item 'type 'pollopt 'id (new-item-id)
      7254                      'url url 'title title 'text text 'parent p!id
      7255                      'by user 'ip ip)
      7256     (save-item o)
      7257     (= (items* o!id) o) 
      7258     o))
      7259  
      7260 (def add-pollopt-page (p user)
      7261   (minipage "Add Poll Choice"
      7262     (urform user req
      7263             (do (add-pollopt user p (striptags (arg req "x")) req!ip)
      7264                 (item-url p!id))
      7265       (tab
      7266         (row "text" (textarea "x" 4 50))
      7267         (row ""     (submit))))))
      7268  
      7269 (def add-pollopt (user p text ip)
      7270   (unless (blank text)
      7271     (atlet o (create-pollopt p nil nil text user ip)
      7272       (++ p!parts (list o!id))
      7273       (save-item p))))
      7274  
      7275 (def display-pollopts (p user whence)
      7276   (each o (visible user (map item p!parts))
      7277     (display-pollopt nil o user whence)
      7278     (spacerow 7)))
      7279  
      7280 (def display-pollopt (n o user whence)
      7281   (tr (display-item-number n)
      7282       (tag (td valign 'top)
      7283         (votelinks o user whence))
      7284       (tag (td class 'comment)
      7285         (tag (div style "margin-top:1px;margin-bottom:0px")
      7286           (if (~cansee user o) (pr (pseudo-text o))
      7287               (~live o)        (spanclass dead 
      7288                                  (pr (if (~blank o!title) o!title o!text)))
      7289                                (if (and (~blank o!title) (~blank o!url))
      7290                                    (link o!title o!url)
      7291                                    (fontcolor black (pr o!text)))))))
      7292   (tr (if n (td))
      7293       (td)
      7294       (tag (td class 'default)
      7295         (spanclass comhead
      7296           (itemscore o)
      7297           (editlink o user)
      7298           (killlink o user whence)
      7299           (deletelink o user whence)
      7300           (deadmark o user)))))
      7301  
      7302  
      7303 ; Individual Item Page (= Comments Page of Stories)
      7304  
      7305 (defmemo item-url (id) (+ "item?id=" id))
      7306  
      7307 (newsop item (id)
      7308   (let s (safe-item id)
      7309     (if (news-type s)
      7310         (do (if s!deleted (note-baditem user ip))
      7311             (item-page user s))
      7312         (do (note-baditem user ip)
      7313             (pr "No such item.")))))
      7314  
      7315 (= baditemreqs* (table) baditem-threshold* 1/100)
      7316  
      7317 ; Something looking at a lot of deleted items is probably the bad sort
      7318 ; of crawler.  Throttle it for this server invocation.
      7319  
      7320 (def note-baditem (user ip)
      7321   (unless (admin user)
      7322     (++ (baditemreqs* ip 0))
      7323     (with (r (requests/ip* ip) b (baditemreqs* ip))
      7324        (when (and (> r 500) (> (/ b r) baditem-threshold*))
      7325          (set (throttle-ips* ip))))))
      7326  
      7327 ; redefined later
      7328  
      7329 (def news-type (i) (and i (in i!type 'story 'comment 'poll 'pollopt)))
      7330  
      7331 (def item-page (user i)
      7332   (with (title (and (cansee user i)
      7333                     (or i!title (aand i!text (ellipsize (striptags it)))))
      7334          here (item-url i!id))
      7335     (longpage user (msec) nil nil title here
      7336       (tab (display-item nil i user here)
      7337            (display-item-text i user)
      7338            (when (apoll i)
      7339              (spacerow 10)
      7340              (tr (td)
      7341                  (td (tab (display-pollopts i user here)))))
      7342            (when (and (cansee user i) (comments-active i))
      7343              (spacerow 10)
      7344              (row "" (comment-form i user here))))
      7345       (br2) 
      7346       (when (and i!kids (commentable i))
      7347         (tab (display-subcomments i user here))
      7348         (br2)))))
      7349  
      7350 (def commentable (i) (in i!type 'story 'comment 'poll))
      7351  
      7352 ; By default the ability to comment on an item is turned off after 
      7353 ; 45 days, but this can be overriden with commentable key.
      7354  
      7355 (= commentable-threshold* (* 60 24 45))
      7356  
      7357 (def comments-active (i)
      7358   (and (live&commentable i)
      7359        (live (superparent i))
      7360        (or (< (item-age i) commentable-threshold*)
      7361            (mem 'commentable i!keys))))
      7362  
      7363  
      7364 (= displayfn* (table))
      7365  
      7366 (= (displayfn* 'story)   (fn (n i user here inlist)
      7367                            (display-story n i user here)))
      7368  
      7369 (= (displayfn* 'comment) (fn (n i user here inlist)
      7370                            (display-comment n i user here nil 0 nil inlist)))
      7371  
      7372 (= (displayfn* 'poll)    (displayfn* 'story))
      7373  
      7374 (= (displayfn* 'pollopt) (fn (n i user here inlist)
      7375                            (display-pollopt n i user here)))
      7376  
      7377 (def display-item (n i user here (o inlist))
      7378   ((displayfn* (i 'type)) n i user here inlist))
      7379  
      7380 (def superparent (i)
      7381   (aif i!parent (superparent:item it) i))
      7382  
      7383 (def display-item-text (s user)
      7384   (when (and (cansee user s) 
      7385              (in s!type 'story 'poll)
      7386              (blank s!url) 
      7387              (~blank s!text))
      7388     (spacerow 2)
      7389     (row "" s!text)))
      7390  
      7391  
      7392 ; Edit Item
      7393  
      7394 (def edit-url (i) (+ "edit?id=" i!id))
      7395  
      7396 (newsop edit (id)
      7397   (let i (safe-item id)
      7398     (if (and i 
      7399              (cansee user i)
      7400              (editable-type i)
      7401              (or (news-type i) (admin user) (author user i)))
      7402         (edit-page user i)
      7403         (pr "No such item."))))
      7404  
      7405 (def editable-type (i) (fieldfn* i!type))
      7406  
      7407 (= fieldfn* (table))
      7408  
      7409 (= (fieldfn* 'story)
      7410    (fn (user s)
      7411      (with (a (admin user)  e (editor user)  x (canedit user s))
      7412        `((string1 title     ,s!title        t ,x)
      7413          (url     url       ,s!url          t ,e)
      7414          (mdtext2 text      ,s!text         t ,x)
      7415          ,@(standard-item-fields s a e x)))))
      7416  
      7417 (= (fieldfn* 'comment)
      7418    (fn (user c)
      7419      (with (a (admin user)  e (editor user)  x (canedit user c))
      7420        `((mdtext  text      ,c!text         t ,x)
      7421          ,@(standard-item-fields c a e x)))))
      7422  
      7423 (= (fieldfn* 'poll)
      7424    (fn (user p)
      7425      (with (a (admin user)  e (editor user)  x (canedit user p))
      7426        `((string1 title     ,p!title        t ,x)
      7427          (mdtext2 text      ,p!text         t ,x)
      7428          ,@(standard-item-fields p a e x)))))
      7429  
      7430 (= (fieldfn* 'pollopt)
      7431    (fn (user p)
      7432      (with (a (admin user)  e (editor user)  x (canedit user p))
      7433        `((string  title     ,p!title        t ,x)
      7434          (url     url       ,p!url          t ,x)
      7435          (mdtext2 text      ,p!text         t ,x)
      7436          ,@(standard-item-fields p a e x)))))
      7437  
      7438 (def standard-item-fields (i a e x)
      7439        `((int     votes     ,(len i!votes) ,a  nil)
      7440          (int     score     ,i!score        t ,a)
      7441          (int     sockvotes ,i!sockvotes   ,a ,a)
      7442          (yesno   dead      ,i!dead        ,e ,e)
      7443          (yesno   deleted   ,i!deleted     ,a ,a)
      7444          (sexpr   flags     ,i!flags       ,a nil)
      7445          (sexpr   keys      ,i!keys        ,a ,a)
      7446          (string  ip        ,i!ip          ,e  nil)))
      7447  
      7448 ; Should check valid-url etc here too.  In fact make a fn that
      7449 ; does everything that has to happen after submitting a story,
      7450 ; and call it both there and here.
      7451  
      7452 (def edit-page (user i)
      7453   (let here (edit-url i)
      7454     (shortpage user nil nil "Edit" here
      7455       (tab (display-item nil i user here)
      7456            (display-item-text i user))
      7457       (br2)
      7458       (vars-form user
      7459                  ((fieldfn* i!type) user i)
      7460                  (fn (name val) 
      7461                    (unless (ignore-edit user i name val)
      7462                      (when (and (is name 'dead) val (no i!dead))
      7463                        (log-kill i user))
      7464                      (= (i name) val)))
      7465                  (fn () (if (admin user) (pushnew 'locked i!keys))
      7466                         (save-item i)
      7467                         (metastory&adjust-rank i)
      7468                         (wipe (comment-cache* i!id))
      7469                         (edit-page user i)))
      7470       (hook 'edit user i))))
      7471  
      7472 (def ignore-edit (user i name val)
      7473   (case name title (len> val title-limit*)
      7474              dead  (and (mem 'nokill i!keys) (~admin user))))
      7475  
      7476  
      7477 ; Comment Submission
      7478  
      7479 (def comment-login-warning (parent whence (o text))
      7480   (login-page 'both "You have to be logged in to comment."
      7481               (fn (u ip)
      7482                 (ensure-news-user u)
      7483                 (newslog ip u 'comment-login)
      7484                 (addcomment-page parent u whence text))))
      7485  
      7486 (def addcomment-page (parent user whence (o text) (o msg))
      7487   (minipage "Add Comment"
      7488     (pagemessage msg)
      7489     (tab
      7490       (let here (flink [addcomment-page parent (get-user _) whence text msg])
      7491         (display-item nil parent user here))
      7492       (spacerow 10)
      7493       (row "" (comment-form parent user whence text)))))
      7494  
      7495 (= noob-comment-msg* nil)
      7496  
      7497 ; Comment forms last for 30 min (- cache time)
      7498  
      7499 (def comment-form (parent user whence (o text))
      7500   (tarform 1800
      7501            (fn (req)
      7502              (when-umatch/r user req
      7503                (process-comment user parent (arg req "text") req!ip whence)))
      7504     (textarea "text" 6 60  
      7505       (aif text (prn (unmarkdown it))))
      7506     (when (and noob-comment-msg* (noob user))
      7507       (br2)
      7508       (spanclass subtext (pr noob-comment-msg*)))
      7509     (br2)
      7510     (submit (if (acomment parent) "reply" "add comment"))))
      7511  
      7512 (= comment-threshold* -20)
      7513  
      7514 ; Have to remove #\returns because a form gives you back "a\r\nb"
      7515 ; instead of just "a\nb".   Maybe should just remove returns from
      7516 ; the vals coming in from any form, e.g. in aform.
      7517  
      7518 (def process-comment (user parent text ip whence)
      7519   (if (no user)
      7520        (flink [comment-login-warning parent whence text])
      7521       (empty text)
      7522        (flink [addcomment-page parent (get-user _) whence text retry*])
      7523       (oversubmitting user ip 'comment)
      7524        (flink [msgpage user toofast*])
      7525        (atlet c (create-comment parent (md-from-form text) user ip)
      7526          (comment-ban-test user c ip text comment-kill* comment-ignore*)
      7527          (if (bad-user user) (kill c 'ignored/karma))
      7528          (submit-item user c)
      7529          whence)))
      7530  
      7531 (def bad-user (u)
      7532   (or (ignored u) (< (karma u) comment-threshold*)))
      7533  
      7534 (def create-comment (parent text user ip)
      7535   (newslog ip user 'comment (parent 'id))
      7536   (let c (inst 'item 'type 'comment 'id (new-item-id)
      7537                      'text text 'parent parent!id 'by user 'ip ip)
      7538     (save-item c)
      7539     (= (items* c!id) c)
      7540     (push c!id parent!kids)
      7541     (save-item parent)
      7542     (push c comments*)
      7543     c))
      7544  
      7545  
      7546 ; Comment Display
      7547  
      7548 (def display-comment-tree (c user whence (o indent 0) (o initialpar))
      7549   (when (cansee-descendant user c)
      7550     (display-1comment c user whence indent initialpar)
      7551     (display-subcomments c user whence (+ indent 1))))
      7552  
      7553 (def display-1comment (c user whence indent showpar)
      7554   (row (tab (display-comment nil c user whence t indent showpar showpar))))
      7555  
      7556 (def display-subcomments (c user whence (o indent 0))
      7557   (each k (sort (compare > frontpage-rank:item) c!kids)
      7558     (display-comment-tree (item k) user whence indent)))
      7559  
      7560 (def display-comment (n c user whence (o astree) (o indent 0) 
      7561                                       (o showpar) (o showon))
      7562   (tr (display-item-number n)
      7563       (when astree (td (hspace (* indent 40))))
      7564       (tag (td valign 'top) (votelinks c user whence t))
      7565       (display-comment-body c user whence astree indent showpar showon)))
      7566  
      7567 ; Comment caching doesn't make generation of comments significantly
      7568 ; faster, but may speed up everything else by generating less garbage.
      7569  
      7570 ; It might solve the same problem more generally to make html code
      7571 ; more efficient.
      7572  
      7573 (= comment-cache* (table) comment-cache-timeout* (table) cc-window* 10000)
      7574  
      7575 (= comments-printed* 0 cc-hits* 0)
      7576  
      7577 (= comment-caching* t) 
      7578  
      7579 ; Cache comments generated for nil user that are over an hour old.
      7580 ; Only try to cache most recent 10k items.  But this window moves,
      7581 ; so if server is running a long time could have more than that in
      7582 ; cache.  Probably should actively gc expired cache entries.
      7583  
      7584 (def display-comment-body (c user whence astree indent showpar showon)
      7585   (++ comments-printed*)
      7586   (if (and comment-caching*
      7587            astree (no showpar) (no showon)
      7588            (live c)
      7589            (nor (admin user) (editor user) (author user c))
      7590            (< (- maxid* c!id) cc-window*)
      7591            (> (- (seconds) c!time) 60)) ; was 3600
      7592       (pr (cached-comment-body c user whence indent))
      7593       (gen-comment-body c user whence astree indent showpar showon)))
      7594  
      7595 (def cached-comment-body (c user whence indent)
      7596   (or (and (> (or (comment-cache-timeout* c!id) 0) (seconds))
      7597            (awhen (comment-cache* c!id)
      7598              (++ cc-hits*)
      7599              it))
      7600       (= (comment-cache-timeout* c!id)
      7601           (cc-timeout c!time)
      7602          (comment-cache* c!id)
      7603           (tostring (gen-comment-body c user whence t indent nil nil)))))
      7604  
      7605 ; Cache for the remainder of the current minute, hour, or day.
      7606  
      7607 (def cc-timeout (t0)
      7608   (let age (- (seconds) t0)
      7609     (+ t0 (if (< age 3600)
      7610                (* (+ (trunc (/ age    60)) 1)    60)
      7611               (< age 86400)
      7612                (* (+ (trunc (/ age  3600)) 1)  3600)
      7613                (* (+ (trunc (/ age 86400)) 1) 86400)))))
      7614  
      7615 (def gen-comment-body (c user whence astree indent showpar showon)
      7616   (tag (td class 'default)
      7617     (let parent (and (or (no astree) showpar) (c 'parent))
      7618       (tag (div style "margin-top:2px; margin-bottom:-10px; ")
      7619         (spanclass comhead
      7620           (itemline c user)
      7621           (permalink c user)
      7622           (when parent
      7623             (when (cansee user c) (pr bar*))
      7624             (link "parent" (item-url ((item parent) 'id))))
      7625           (editlink c user)
      7626           (killlink c user whence)
      7627           (blastlink c user whence)
      7628           (deletelink c user whence)
      7629           ; a hack to check whence but otherwise need an arg just for this
      7630           (unless (or astree (is whence "newcomments"))
      7631             (flaglink c user whence))
      7632           (deadmark c user)
      7633           (when showon
      7634             (pr " | on: ")
      7635             (let s (superparent c)
      7636               (link (ellipsize s!title 50) (item-url s!id))))))
      7637       (when (or parent (cansee user c))
      7638         (br))
      7639       (spanclass comment
      7640         (if (~cansee user c)               (pr (pseudo-text c))
      7641             (nor (live c) (author user c)) (spanclass dead (pr c!text))
      7642                                            (fontcolor (comment-color c)
      7643                                              (pr c!text))))
      7644       (when (and astree (cansee user c) (live c))
      7645         (para)
      7646         (tag (font size 1)
      7647           (if (and (~mem 'neutered c!keys)
      7648                    (replyable c indent)
      7649                    (comments-active c))
      7650               (underline (replylink c whence))
      7651               (fontcolor sand (pr "-----"))))))))
      7652  
      7653 ; For really deeply nested comments, caching could add another reply 
      7654 ; delay, but that's ok.
      7655  
      7656 ; People could beat this by going to the link url or manually entering 
      7657 ; the reply url, but deal with that if they do.
      7658  
      7659 (= reply-decay* 1.8)   ; delays: (0 0 1 3 7 12 18 25 33 42 52 63)
      7660  
      7661 (def replyable (c indent)
      7662   (or (< indent 2)
      7663       (> (item-age c) (expt (- indent 1) reply-decay*))))
      7664  
      7665 (def replylink (i whence (o title 'reply))
      7666   (link title (+ "reply?id=" i!id "&whence=" (urlencode whence))))
      7667  
      7668 (newsop reply (id whence)
      7669   (with (i      (safe-item id)
      7670          whence (or (only.urldecode whence) "news"))
      7671     (if (only.comments-active i)
      7672         (if user
      7673             (addcomment-page i user whence)
      7674             (login-page 'both "You have to be logged in to comment."
      7675                         (fn (u ip)
      7676                           (ensure-news-user u)
      7677                           (newslog ip u 'comment-login)
      7678                           (addcomment-page i u whence))))
      7679         (pr "No such item."))))
      7680  
      7681 (def comment-color (c)
      7682   (if (> c!score 0) black (grayrange c!score)))
      7683  
      7684 (defmemo grayrange (s)
      7685   (gray (min 230 (round (expt (* (+ (abs s) 2) 900) .6)))))
      7686  
      7687  
      7688 ; Threads
      7689  
      7690 (def threads-url (user) (+ "threads?id=" user))
      7691  
      7692 (newsop threads (id) 
      7693   (if id
      7694       (threads-page user id)
      7695       (pr "No user specified.")))
      7696  
      7697 (def threads-page (user subject)
      7698   (if (profile subject)
      7699       (withs (title (+ subject "'s comments")
      7700               label (if (is user subject) "threads" title)
      7701               here  (threads-url subject))
      7702         (longpage user (msec) nil label title here
      7703           (awhen (keep [and (cansee user _) (~subcomment _)]
      7704                        (comments subject maxend*))
      7705             (display-threads user it label title here))))
      7706       (prn "No such user.")))
      7707  
      7708 (def display-threads (user comments label title whence
      7709                       (o start 0) (o end threads-perpage*))
      7710   (tab 
      7711     (each c (cut comments start end)
      7712       (display-comment-tree c user whence 0 t))
      7713     (when end
      7714       (let newend (+ end threads-perpage*)
      7715         (when (and (<= newend maxend*) (< end (len comments)))
      7716           (spacerow 10)
      7717           (row (tab (tr (td (hspace 0))
      7718                         (td (hspace votewid*))
      7719                         (tag (td class 'title)
      7720                           (morelink display-threads
      7721                                     comments label title end newend))))))))))
      7722  
      7723 (def submissions (user (o limit)) 
      7724   (map item (firstn limit (uvar user submitted))))
      7725  
      7726 (def comments (user (o limit))
      7727   (map item (retrieve limit acomment:item (uvar user submitted))))
      7728   
      7729 (def subcomment (c)
      7730   (some [and (acomment _) (is _!by c!by) (no _!deleted)]
      7731         (ancestors c)))
      7732  
      7733 (def ancestors (i)
      7734   (accum a (trav i!parent a:item self:!parent:item)))
      7735  
      7736  
      7737 ; Submitted
      7738  
      7739 (def submitted-url (user) (+ "submitted?id=" user))
      7740        
      7741 (newsop submitted (id) 
      7742   (if id 
      7743       (submitted-page user id)
      7744       (pr "No user specified.")))
      7745  
      7746 (def submitted-page (user subject)
      7747   (if (profile subject)
      7748       (with (label (+ subject "'s submissions")
      7749              here  (submitted-url subject))
      7750         (longpage user (msec) nil label label here
      7751           (if (or (no (ignored subject))
      7752                   (is user subject)
      7753                   (seesdead user))
      7754               (aif (keep [and (metastory _) (cansee user _)]
      7755                          (submissions subject))
      7756                    (display-items user it label label here 0 perpage* t)))))
      7757       (pr "No such user.")))
      7758  
      7759  
      7760 ; RSS
      7761  
      7762 (newsop rss () (rsspage nil))
      7763  
      7764 (newscache rsspage user 90 
      7765   (rss-stories (retrieve perpage* live ranked-stories*)))
      7766  
      7767 (def rss-stories (stories)
      7768   (tag (rss version "2.0")
      7769     (tag channel
      7770       (tag title (pr this-site*))
      7771       (tag link (pr site-url*))
      7772       (tag description (pr site-desc*))
      7773       (each s stories
      7774         (tag item
      7775           (let comurl (+ site-url* (item-url s!id))
      7776             (tag title    (pr (eschtml s!title)))
      7777             (tag link     (pr (if (blank s!url) comurl (eschtml s!url))))
      7778             (tag comments (pr comurl))
      7779             (tag description
      7780               (cdata (link "Comments" comurl)))))))))
      7781  
      7782  
      7783 ; User Stats
      7784  
      7785 (newsop leaders () (leaderspage user))
      7786  
      7787 (= nleaders* 20)
      7788  
      7789 (newscache leaderspage user 1000
      7790   (longpage user (msec) nil "leaders" "Leaders" "leaders"
      7791     (sptab
      7792       (let i 0
      7793         (each u (firstn nleaders* (leading-users))
      7794           (tr (tdr:pr (++ i) ".")
      7795               (td (userlink user u nil))
      7796               (tdr:pr (karma u))
      7797               (when (admin user)
      7798                 (tdr:prt (only.num (uvar u avg) 2 t t))))
      7799           (if (is i 10) (spacerow 30)))))))
      7800  
      7801 (= leader-threshold* 1)  ; redefined later
      7802  
      7803 (def leading-users ()
      7804   (sort (compare > [karma _])
      7805         (users [and (> (karma _) leader-threshold*) (~admin _)])))
      7806  
      7807 (adop editors ()
      7808   (tab (each u (users [is (uvar _ auth) 1])
      7809          (row (userlink user u)))))
      7810  
      7811  
      7812 (= update-avg-threshold* 0)  ; redefined later
      7813  
      7814 (defbg update-avg 45
      7815   (unless (or (empty profs*) (no stories*))
      7816     (update-avg (rand-user [and (only.> (car (uvar _ submitted)) 
      7817                                         (- maxid* initload*))
      7818                                 (len> (uvar _ submitted) 
      7819                                       update-avg-threshold*)]))))
      7820  
      7821 (def update-avg (user)
      7822   (= (uvar user avg) (comment-score user))
      7823   (save-prof user))
      7824  
      7825 (def rand-user ((o test idfn))
      7826   (evtil (rand-key profs*) test))
      7827  
      7828 ; Ignore the most recent 5 comments since they may still be gaining votes.  
      7829 ; Also ignore the highest-scoring comment, since possibly a fluff outlier.
      7830  
      7831 (def comment-score (user)
      7832   (aif (check (nthcdr 5 (comments user 50)) [len> _ 10])
      7833        (avg (cdr (sort > (map !score (rem !deleted it)))))
      7834        nil))
      7835  
      7836  
      7837 ; Comment Analysis
      7838  
      7839 ; Instead of a separate active op, should probably display this info 
      7840 ; implicitly by e.g. changing color of commentlink or by showing the 
      7841 ; no of comments since that user last looked.
      7842  
      7843 (newsop active () (active-page user))
      7844  
      7845 (newscache active-page user 600
      7846   (listpage user (msec) (actives user) "active" "Active Threads"))
      7847  
      7848 (def actives (user (o n maxend*) (o consider 2000))
      7849   (visible user (rank-stories n consider (memo active-rank))))
      7850  
      7851 (= active-threshold* 1500)
      7852  
      7853 (def active-rank (s)
      7854   (sum [max 0 (- active-threshold* (item-age _))]
      7855        (cdr (family s))))
      7856  
      7857 (def family (i) (cons i (mappend family:item i!kids)))
      7858  
      7859  
      7860 (newsop newcomments () (newcomments-page user))
      7861  
      7862 (newscache newcomments-page user 60
      7863   (listpage user (msec) (visible user (firstn maxend* comments*))
      7864             "comments" "New Comments" "newcomments" nil))
      7865  
      7866  
      7867 ; Doc
      7868  
      7869 (defop formatdoc req
      7870   (msgpage (get-user req) formatdoc* "Formatting Options"))
      7871  
      7872 (= formatdoc-url* "formatdoc")
      7873  
      7874 (= formatdoc* 
      7875 "Blank lines separate paragraphs.
      7876 <p> Text after a blank line that is indented by two or more spaces is 
      7877 reproduced verbatim.  (This is intended for code.)
      7878 <p> Text surrounded by asterisks is italicized, if the character after the 
      7879 first asterisk isn't whitespace.
      7880 <p> Urls become links, except in the text field of a submission.<br><br>")
      7881  
      7882  
      7883 ; Noprocrast
      7884  
      7885 (def check-procrast (user)
      7886   (or (no user)
      7887       (no (uvar user noprocrast))
      7888       (let now (seconds)
      7889         (unless (uvar user firstview)
      7890           (reset-procrast user))
      7891         (or (when (< (/ (- now (uvar user firstview)) 60)
      7892                      (uvar user maxvisit))
      7893               (= (uvar user lastview) now)
      7894               (save-prof user)
      7895               t)
      7896             (when (> (/ (- now (uvar user lastview)) 60)
      7897                      (uvar user minaway))
      7898               (reset-procrast user)
      7899               t)))))
      7900                 
      7901 (def reset-procrast (user)
      7902   (= (uvar user lastview) (= (uvar user firstview) (seconds)))
      7903   (save-prof user))
      7904  
      7905 (def procrast-msg (user whence)
      7906   (let m (+ 1 (trunc (- (uvar user minaway)
      7907                         (minutes-since (uvar user lastview)))))
      7908     (pr "<b>Get back to work!</b>")
      7909     (para "Sorry, you can't see this page.  Based on the anti-procrastination
      7910            parameters you set in your profile, you'll be able to use the site 
      7911            again in " (plural m "minute") ".")
      7912     (para "(If you got this message after submitting something, don't worry,
      7913            the submission was processed.)")
      7914     (para "To change your anti-procrastination settings, go to your profile 
      7915            by clicking on your username.  If <tt>noprocrast</tt> is set to 
      7916            <tt>yes</tt>, you'll be limited to sessions of <tt>maxvisit</tt>
      7917            minutes, with <tt>minaway</tt> minutes between them.")
      7918     (para)
      7919     (w/rlink whence (underline (pr "retry")))
      7920     ; (hspace 20)
      7921     ; (w/rlink (do (reset-procrast user) whence) (underline (pr "override")))
      7922     (br2)))
      7923  
      7924  
      7925 ; Reset PW
      7926  
      7927 (defopg resetpw req (resetpw-page (get-user req)))
      7928  
      7929 (def resetpw-page (user (o msg))
      7930   (minipage "Reset Password"
      7931     (if msg
      7932          (pr msg)
      7933         (blank (uvar user email))
      7934          (do (pr "Before you do this, please add your email address to your ")
      7935              (underlink "profile" (user-url user))
      7936              (pr ". Otherwise you could lose your account if you mistype 
      7937                   your new password.")))
      7938     (br2)
      7939     (uform user req (try-resetpw user (arg req "p"))
      7940       (single-input "New password: " 'p 20 "reset" t))))
      7941  
      7942 (def try-resetpw (user newpw)
      7943   (if (len< newpw 4)
      7944       (resetpw-page user "Passwords should be a least 4 characters long.  
      7945                           Please choose another.")
      7946       (do (set-pw user newpw)
      7947           (newspage user))))
      7948  
      7949  
      7950 ; Scrubrules
      7951  
      7952 (defopa scrubrules req
      7953   (scrub-page (get-user req) scrubrules*))
      7954  
      7955 ; If have other global alists, generalize an alist edit page.
      7956 ; Or better still generalize vars-form.
      7957  
      7958 (def scrub-page (user rules (o msg nil))
      7959   (minipage "Scrubrules"
      7960     (when msg (pr msg) (br2))
      7961     (uform user req
      7962            (with (froms (lines (arg req "from"))
      7963                   tos   (lines (arg req "to")))
      7964              (if (is (len froms) (len tos))
      7965                  (do (todisk scrubrules* (map list froms tos))
      7966                      (scrub-page user scrubrules* "Changes saved."))
      7967                  (scrub-page user rules "To and from should be same length.")))
      7968       (pr "From: ")
      7969       (tag (textarea name 'from 
      7970                      cols (apply max 20 (map len (map car rules)))
      7971                      rows (+ (len rules) 3))
      7972         (apply pr #\newline (intersperse #\newline (map car rules))))
      7973       (pr " To: ")
      7974       (tag (textarea name 'to 
      7975                      cols (apply max 20 (map len (map cadr rules)))
      7976                      rows (+ (len rules) 3))
      7977         (apply pr #\newline (intersperse #\newline (map cadr rules))))
      7978       (br2)
      7979       (submit "update"))))
      7980  
      7981  
      7982 ; Abuse Analysis
      7983  
      7984 (adop badsites ()
      7985   (sptab 
      7986     (row "Dead" "Days" "Site" "O" "K" "I" "Users")
      7987     (each (site deads) (with (banned (banned-site-items)
      7988                               pairs  (killedsites))
      7989                          (+ pairs (map [list _ (banned _)]
      7990                                        (rem (fn (d)
      7991                                               (some [caris _ d] pairs))
      7992                                             (keys banned-sites*)))))
      7993       (let ban (car (banned-sites* site))
      7994         (tr (tdr (when deads
      7995                    (onlink (len deads)
      7996                            (listpage user (msec) deads
      7997                                      nil (+ "killed at " site) "badsites"))))
      7998             (tdr (when deads (pr (round (days-since ((car deads) 'time))))))
      7999             (td site)
      8000             (td (w/rlink (do (set-site-ban user site nil) "badsites")
      8001                   (fontcolor (if ban gray.220 black) (pr "x"))))
      8002             (td (w/rlink (do (set-site-ban user site 'kill) "badsites")
      8003                   (fontcolor (case ban kill darkred gray.220) (pr "x"))))
      8004             (td (w/rlink (do (set-site-ban user site 'ignore) "badsites")
      8005                   (fontcolor (case ban ignore darkred gray.220) (pr "x"))))
      8006             (td (each u (dedup (map !by deads))
      8007                   (userlink user u nil)
      8008                   (pr " "))))))))
      8009  
      8010 (defcache killedsites 300
      8011   (let bads (table [each-loaded-item i
      8012                      (awhen (and i!dead (sitename i!url))
      8013                        (push i (_ it)))])
      8014     (with (acc nil deadcount (table))
      8015       (each (site items) bads
      8016         (let n (len items)
      8017           (when (> n 2)
      8018             (= (deadcount site) n)
      8019             (insort (compare > deadcount:car)
      8020                     (list site (rev items))
      8021                     acc))))
      8022       acc)))
      8023  
      8024 (defcache banned-site-items 300
      8025   (table [each-loaded-item i
      8026            (awhen (and i!dead (check (sitename i!url) banned-sites*))
      8027              (push i (_ it)))]))
      8028  
      8029 ; Would be nice to auto unban ips whose most recent submission is > n 
      8030 ; days old, but hard to do because of lazy loading.  Would have to keep
      8031 ; a table of most recent submission per ip, and only enforce bannnedness
      8032 ; if < n days ago.
      8033  
      8034 (adop badips ()
      8035   (withs ((bads goods) (badips)
      8036           (subs ips)   (sorted-badips bads goods))
      8037     (sptab
      8038       (row "IP" "Days" "Dead" "Live" "Users")
      8039       (each ip ips
      8040         (tr (td (let banned (banned-ips* ip)
      8041                   (w/rlink (do (set-ip-ban user ip (no banned))
      8042                                "badips")
      8043                     (fontcolor (if banned darkred) (pr ip)))))
      8044             (tdr (when (or (goods ip) (bads ip))
      8045                    (pr (round (days-since 
      8046                                 (max (aif (car (goods ip)) it!time 0) 
      8047                                      (aif (car (bads  ip)) it!time 0)))))))
      8048             (tdr (onlink (len (bads ip))
      8049                          (listpage user (msec) (bads ip)
      8050                                    nil (+ "dead from " ip) "badips")))
      8051             (tdr (onlink (len (goods ip))
      8052                          (listpage user (msec) (goods ip)
      8053                                    nil (+ "live from " ip) "badips")))
      8054             (td (each u (subs ip)
      8055                   (userlink user u nil) 
      8056                   (pr " "))))))))
      8057  
      8058 (defcache badips 300
      8059   (with (bads (table) goods (table))
      8060     (each-loaded-item s
      8061       (if (and s!dead (commentable s))
      8062           (push s (bads  s!ip))
      8063           (push s (goods s!ip))))
      8064     (each (k v) bads  (zap rev (bads  k)))
      8065     (each (k v) goods (zap rev (goods k)))
      8066     (list bads goods)))
      8067  
      8068 (def sorted-badips (bads goods)
      8069   (withs (ips  (let ips (rem [len< (bads _) 2] (keys bads))
      8070                 (+ ips (rem [mem _ ips] (keys banned-ips*))))
      8071           subs (table 
      8072                  [each ip ips
      8073                    (= (_ ip) (dedup (map !by (+ (bads ip) (goods ip)))))]))
      8074     (list subs
      8075           (sort (compare > (memo [badness (subs _) (bads _) (goods _)]))
      8076                 ips))))
      8077  
      8078 (def badness (subs bads goods)
      8079   (* (/ (len bads)
      8080         (max .9 (expt (len goods) 2))
      8081         (expt (+ (days-since (aif (car bads) it!time 0))
      8082                  1)
      8083               2))
      8084      (if (len> subs 1) 20 1)))
      8085  
      8086  
      8087 (edop flagged ()
      8088   (display-selected-items user [retrieve maxend* flagged _] "flagged"))
      8089  
      8090 (def flagged (i) 
      8091   (and (live i)
      8092        (~mem 'nokill i!keys)
      8093        (len> i!flags many-flags*)))
      8094  
      8095  
      8096 (edop killed ()
      8097   (display-selected-items user [retrieve maxend* !dead _] "killed"))
      8098  
      8099 (def display-selected-items (user f whence)
      8100   (display-items user (f stories*) nil nil whence)
      8101   (vspace 35)
      8102   (color-stripe textgray)
      8103   (vspace 35)
      8104   (display-items user (f comments*) nil nil whence))
      8105  
      8106  
      8107 ; Rather useless thus; should add more data.
      8108  
      8109 (adop badguys ()
      8110   (tab (each u (sort (compare > [uvar _ created])
      8111                      (users [ignored _]))
      8112          (row (userlink user u nil)))))
      8113  
      8114 (adop badlogins ()  (logins-page bad-logins*))
      8115  
      8116 (adop goodlogins () (logins-page good-logins*))
      8117  
      8118 (def logins-page (source)
      8119   (sptab (each (time ip user) (firstn 100 (rev (qlist source)))
      8120            (row time ip user))))
      8121  
      8122  
      8123 ; Stats
      8124  
      8125 (adop optimes ()
      8126   (sptab
      8127     (tr (td "op") (tdr "avg") (tdr "med") (tdr "req") (tdr "total"))
      8128     (spacerow 10)
      8129     (each name (sort < newsop-names*)
      8130       (tr (td name)
      8131           (let ms (only.avg (qlist (optimes* name)))
      8132             (tdr:prt (only.round ms))
      8133             (tdr:prt (only.med (qlist (optimes* name))))
      8134             (let n (opcounts* name)
      8135               (tdr:prt n)
      8136               (tdr:prt (and n (round (/ (* n ms) 1000))))))))))
      8137  
      8138 (defop topcolors req
      8139   (minipage "Custom Colors"
      8140     (tab 
      8141       (each c (dedup (map downcase (trues [uvar _ topcolor] (users))))
      8142         (tr (td c) (tdcolor (hex>color c) (hspace 30)))))))
      8143  
      8144  
      8145