smalltalk-tng
diff r6f/vau-cps.rkt @ 323:454c18798969
merger
| author | Tony Garnock-Jones <tonygarnockjones@gmail.com> |
|---|---|
| date | Tue Feb 07 11:34:20 2012 -0500 (3 months ago) |
| parents | |
| children |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/r6f/vau-cps.rkt Tue Feb 07 11:34:20 2012 -0500 1.3 @@ -0,0 +1,352 @@ 1.4 +#lang racket/base 1.5 + 1.6 +(require rackunit) 1.7 + 1.8 +(struct operative (formals envformal body staticenv) #:transparent) 1.9 +(struct applicative (underlying) #:transparent) 1.10 +(struct primitive (underlying) #:transparent) 1.11 + 1.12 +(define (vau-eval exp env k) 1.13 + (cond 1.14 + ((symbol? exp) (k (lookup exp env))) 1.15 + ((not (pair? exp)) (k exp)) 1.16 + (else (vau-eval (car exp) env (make-combiner (cdr exp) env k))))) 1.17 + 1.18 +(define (make-combiner argtree env k) 1.19 + (lambda (rator) 1.20 + (cond 1.21 + ((procedure? rator) 1.22 + (vau-eval-args argtree '() env (make-primitive-applier rator k))) 1.23 + ((operative? rator) 1.24 + (vau-eval (operative-body rator) 1.25 + (extend (operative-staticenv rator) 1.26 + (bind! (vau-match (operative-formals rator) 1.27 + argtree) 1.28 + (operative-envformal rator) 1.29 + env)) 1.30 + k)) 1.31 + ((applicative? rator) 1.32 + (vau-eval-args argtree 1.33 + '() 1.34 + env 1.35 + (make-applicative-combiner (applicative-underlying rator) env k))) 1.36 + ((primitive? rator) 1.37 + (apply (primitive-underlying rator) k env argtree)) 1.38 + (else (vau-error 'vau-eval "Not a callable: ~v with argtree: ~v" rator argtree))))) 1.39 + 1.40 +(define (make-primitive-applier rator k) 1.41 + (lambda (args) 1.42 + (k (apply rator args)))) 1.43 + 1.44 +(define (make-applicative-combiner op env k) 1.45 + (lambda (args) 1.46 + (vau-eval (cons op args) env k))) 1.47 + 1.48 +(define (vau-eval-args args revacc env k) 1.49 + (if (null? args) 1.50 + (k (reverse revacc)) 1.51 + (vau-eval (car args) env 1.52 + (make-args-evaler (cdr args) revacc env k)))) 1.53 + 1.54 +(define (make-args-evaler remainder revacc env k) 1.55 + (lambda (v) 1.56 + (vau-eval-args remainder (cons v revacc) env k))) 1.57 + 1.58 +(define (vau-error . args) 1.59 + (apply error args)) 1.60 + 1.61 +(define (lookup name env) 1.62 + (if (null? env) 1.63 + (vau-error 'vau-eval "Variable not found: ~v" name) 1.64 + (hash-ref (car env) name (lambda () (lookup name (cdr env)))))) 1.65 + 1.66 +(define (extend env rib) 1.67 + (cons rib env)) 1.68 + 1.69 +(define (bind! rib name value) 1.70 + (if (eq? name '#:ignore) 1.71 + rib 1.72 + (begin (hash-set! rib name value) 1.73 + rib))) 1.74 + 1.75 +(define (empty-env) 1.76 + '()) 1.77 + 1.78 +(define (empty-rib) 1.79 + (make-hash)) 1.80 + 1.81 +(define (vau-match p v) 1.82 + (let m ((p p) 1.83 + (v v) 1.84 + (rib (empty-rib))) 1.85 + (cond 1.86 + ((pair? p) (if (pair? v) 1.87 + (m (cdr p) (cdr v) (m (car p) (car v) rib)) 1.88 + (vau-error 'vau-match "Expected a pair matching ~v; got ~v" p v))) 1.89 + ((symbol? p) (bind! rib p v)) 1.90 + ((eq? p '#:ignore) rib) 1.91 + (else (if (eqv? p v) 1.92 + rib 1.93 + (vau-error 'vau-match "Expected a literal eqv? to ~v; got ~v" p v)))))) 1.94 + 1.95 +(define (vau-lexer [port (current-input-port)]) 1.96 + (let () 1.97 + (define (eat!) (read-char port)) 1.98 + (define (emit val) (eat!) val) 1.99 + (define (emit-number acc) 1.100 + (string->number (list->string (reverse acc)))) 1.101 + (define (go state . args) 1.102 + (eat!) 1.103 + (apply state (peek-char port) args)) 1.104 + (define (keywordize str) 1.105 + (cond 1.106 + ((string=? str "t") #t) 1.107 + ((string=? str "f") #f) 1.108 + (else (string->keyword str)))) 1.109 + (define (start c) 1.110 + (cond 1.111 + ((eof-object? c) (emit c)) 1.112 + ((char-whitespace? c) (go start)) 1.113 + ((char-numeric? c) (go number (list c))) 1.114 + (else (case c 1.115 + ((#\;) (go line-comment)) 1.116 + ((#\-) (go negsign)) 1.117 + ((#\") (go string (list))) 1.118 + ((#\#) (go symbol (list) keywordize)) 1.119 + ((#\() (emit #\()) 1.120 + ((#\)) (emit #\))) 1.121 + (else (go symbol (list c) string->symbol)))))) 1.122 + (define (line-comment c) 1.123 + (case c 1.124 + ((#\return #\newline) (go start)) 1.125 + (else (go line-comment)))) 1.126 + (define (negsign c) 1.127 + (cond 1.128 + ((and (char? c) (char-numeric? c)) (number c (list #\-))) 1.129 + (else (symbol c (list #\-) string->symbol)))) 1.130 + (define (number c acc) 1.131 + (cond 1.132 + ((eof-object? c) (emit-number acc)) 1.133 + ((char-numeric? c) (go number (cons c acc))) 1.134 + ((eqv? c #\.) (go fraction (cons c acc))) 1.135 + (else (exponent c acc)))) 1.136 + (define (fraction c acc) 1.137 + (cond 1.138 + ((eof-object? c) (emit-number acc)) 1.139 + ((char-numeric? c) (go fraction (cons c acc))) 1.140 + (else (exponent c acc)))) 1.141 + (define (exponent c acc) 1.142 + (case c 1.143 + ((#\e #\E) (go expsign (cons c acc))) 1.144 + (else (emit-number acc)))) 1.145 + (define (expsign c acc) 1.146 + (case c 1.147 + ((#\- #\+) (go expdigits (cons c acc))) 1.148 + (else (cond 1.149 + ((eof-object? c) (emit-number acc)) 1.150 + ((char-numeric? c) (go expdigits (cons c acc))) 1.151 + (else (vau-error 'vau-lexer 1.152 + "Syntax error: expected at least one digit in exponent")))))) 1.153 + (define (expdigits c acc) 1.154 + (cond 1.155 + ((eof-object? c) (emit-number acc)) 1.156 + ((char-numeric? c) (go expdigits (cons c acc))) 1.157 + (else (emit-number acc)))) 1.158 + (define (string c acc) 1.159 + (case c 1.160 + ((#\") (emit (list->string (reverse acc)))) 1.161 + ((#\\) (go string-escape acc)) 1.162 + (else (if (eof-object? c) 1.163 + (vau-error 'vau-lexer "Syntax error: unterminated string") 1.164 + (go string (cons c acc)))))) 1.165 + (define (string-escape c acc) 1.166 + (case c 1.167 + ((#\\ #\") (go string (cons c acc))) 1.168 + (else (vau-error 'vau-lexer "Syntax error: bad escape in string: ~v" c)))) 1.169 + (define (symbol c acc finalize) 1.170 + (cond 1.171 + ((eof-object? c) (emit (finalize (list->string (reverse acc))))) 1.172 + ((char-alphabetic? c) (go symbol (cons c acc) finalize)) 1.173 + ((char-numeric? c) (go symbol (cons c acc) finalize)) 1.174 + ((memv c '(#\- #\! #\@ #\$ #\% #\^ #\& #\* #\_ #\= #\+ #\: #\< #\> #\/ #\?)) 1.175 + (go symbol (cons c acc) finalize)) 1.176 + (else (finalize (list->string (reverse acc)))))) 1.177 + (lambda () (start (peek-char port))))) 1.178 + 1.179 +(define (vau-read [port (current-input-port)]) 1.180 + (let ((next (vau-lexer port))) 1.181 + (define (read) 1.182 + (read* (next))) 1.183 + (define (read* v) 1.184 + (cond 1.185 + ((eqv? v #\() (read-list '())) 1.186 + ((eqv? v #\)) (vau-error 'vau-read "Syntax error: unexpected close paren")) 1.187 + (else v))) 1.188 + (define (read-list acc) 1.189 + (let ((v (next))) 1.190 + (cond 1.191 + ((eof-object? v) (vau-error 'vau-read "End-of-file in unterminated list")) 1.192 + ((eqv? v #\)) (reverse acc)) 1.193 + (else (read-list (cons (read* v) acc)))))) 1.194 + (read))) 1.195 + 1.196 +(define (lex-all s) 1.197 + (let ((next (vau-lexer (open-input-string s)))) 1.198 + (let loop () 1.199 + (let ((v (next))) 1.200 + (if (eof-object? v) 1.201 + '() 1.202 + (cons v (loop))))))) 1.203 + 1.204 +(check-equal? (lex-all "abc") '(abc)) 1.205 +(check-equal? (lex-all "\"abc\"") '("abc")) 1.206 +(check-equal? (lex-all "\"ab\\\"c\"") '("ab\"c")) 1.207 +(check-equal? (lex-all "abc 123e-2 -2.34e2 12 -12") '(abc 1.23 -234.0 12 -12)) 1.208 +(check-equal? (lex-all "(abc()( abc ) abc (#abc) ()abc)") 1.209 + '(#\( abc #\( #\) #\( abc #\) abc #\( #:abc #\) #\( #\) abc #\) )) 1.210 + 1.211 +(check-equal? (vau-read (open-input-string "(abc()( abc ) abc 12e0z (#abc) ()abc)")) 1.212 + '(abc () (abc) abc 12.0 z (#:abc) () abc)) 1.213 + 1.214 +;--------------------------------------------------------------------------- 1.215 + 1.216 +(define (alist->rib xs) 1.217 + (let ((rib (empty-rib))) 1.218 + (for-each (lambda (entry) 1.219 + (bind! rib (car entry) (cadr entry))) 1.220 + xs) 1.221 + rib)) 1.222 + 1.223 +;--------------------------------------------------------------------------- 1.224 + 1.225 +(define $begin 1.226 + (primitive (lambda (k dynenv . exps) 1.227 + (if (null? exps) 1.228 + (k (void)) 1.229 + (let loop ((exps exps)) 1.230 + (vau-eval (car exps) dynenv 1.231 + (if (null? (cdr exps)) 1.232 + k 1.233 + (lambda (v) (loop (cdr exps)))))))))) 1.234 + 1.235 +(define $vau 1.236 + (primitive (lambda (k dynenv formals envformal . exps) 1.237 + (let ((body (cond 1.238 + ((null? exps) (void)) 1.239 + ((null? (cdr exps)) (car exps)) 1.240 + (else (cons $begin exps))))) 1.241 + (k (operative formals envformal body dynenv)))))) 1.242 + 1.243 +(define coreenv 1.244 + (extend (empty-env) 1.245 + (alist->rib 1.246 + `((eval ,(lambda (exp env) (vau-eval exp env values))) 1.247 + (list* ,list*) 1.248 + ($define! ,(primitive (lambda (k dynenv name valexp) 1.249 + (when (not (symbol? name)) 1.250 + (vau-error '$define! "Needs symbol name; got ~v" name)) 1.251 + (vau-eval valexp dynenv 1.252 + (lambda (value) 1.253 + ;; TODO: destructuring-bind definitions 1.254 + (bind! (car dynenv) name value) 1.255 + (k value)))))) 1.256 + ($begin ,$begin) 1.257 + ($vau ,$vau) 1.258 + (wrap ,applicative) 1.259 + (unwrap ,applicative-underlying) 1.260 + ;; (unwrap ,(lambda (x) 1.261 + ;; (cond 1.262 + ;; ((applicative? x) (applicative-underlying x)) 1.263 + ;; ((procedure? x) (primitive (lambda (k dynenv . args) 1.264 + ;; (k (apply x args)))))))) 1.265 + )))) 1.266 + 1.267 +(define $lambda 1.268 + (vau-eval `($define! $lambda 1.269 + ($vau (formals . exps) dynenv 1.270 + (wrap (eval (list* $vau formals #:ignore exps) dynenv)))) 1.271 + coreenv 1.272 + values)) 1.273 + 1.274 +;--------------------------------------------------------------------------- 1.275 + 1.276 +(define baseenv 1.277 + (extend coreenv 1.278 + (alist->rib 1.279 + `(($if ,(primitive (lambda (k dynenv test true false) 1.280 + (vau-eval test dynenv 1.281 + (lambda (result) 1.282 + (case result 1.283 + ((#t) (vau-eval true dynenv k)) 1.284 + ((#f) (vau-eval false dynenv k)) 1.285 + (else (vau-error '$if "Test must evaluate to boolean")))))))) 1.286 + ($let ,(primitive (lambda (k dynenv bindings . exps) 1.287 + (vau-eval (list* (list* $lambda (map car bindings) exps) 1.288 + (map cadr bindings)) 1.289 + dynenv 1.290 + k)))) 1.291 + 1.292 + ;; (make-environment ,(lambda parents 1.293 + ;; (extend (apply append parents) (empty-rib)))) 1.294 + ;; (operative? ,(lambda (x) 1.295 + ;; (or (operative? x) 1.296 + ;; (primitive? x)))) 1.297 + ;; (applicative? ,(lambda (x) 1.298 + ;; (or (applicative? x) 1.299 + ;; (procedure? x)))) 1.300 + 1.301 + (open-input-file ,open-input-file) 1.302 + (close-input-port ,close-input-port) 1.303 + (eof-object? ,eof-object?) 1.304 + 1.305 + (read ,vau-read) 1.306 + 1.307 + (write ,write) 1.308 + (display ,display) 1.309 + (newline ,newline) 1.310 + (flush-output ,flush-output) 1.311 + (format ,format) 1.312 + 1.313 + (box ,box) 1.314 + (unbox ,unbox) 1.315 + (set-box! ,set-box!) 1.316 + 1.317 + (+ ,+) (- ,-) (* ,*) (/ ,/) 1.318 + (quotient ,quotient) (remainder ,remainder) (modulo ,modulo) 1.319 + (number->string ,number->string) (string->number ,string->number) 1.320 + 1.321 + (cons ,cons) 1.322 + (car ,car) 1.323 + (cdr ,cdr) 1.324 + (pair? ,pair?) 1.325 + (null? ,null?) 1.326 + 1.327 + (symbol? ,symbol?) 1.328 + 1.329 + (eq? ,eq?) 1.330 + (eqv? ,eqv?) 1.331 + (not ,not) 1.332 + (hash->list ,hash->list) 1.333 + 1.334 + )))) 1.335 + 1.336 +(define $load! 1.337 + (vau-eval `($define! $load! 1.338 + ($vau (filename-exp) dynenv 1.339 + ($define! filename (eval filename-exp dynenv)) 1.340 + ($define! p (open-input-file filename)) 1.341 + ($define! loop 1.342 + ($lambda () 1.343 + ($let ((exp (read p))) 1.344 + ($if (eof-object? exp) 1.345 + ($begin (close-input-port p) 1.346 + #:ignore) 1.347 + ($begin (eval exp dynenv) 1.348 + (loop)))))) 1.349 + (loop))) 1.350 + baseenv 1.351 + values)) 1.352 + 1.353 +(let () ;; new scope to suppress toplevel expr result printing 1.354 + (vau-eval `($load! "prelude.vau") baseenv values) 1.355 + (void))
