1#lang racket/base 2 3(require "manual.rkt" "struct.rkt" "scheme.rkt" "decode.rkt" 4 (only-in "core.rkt" content? compound-paragraph plain) 5 racket/contract/base 6 racket/file 7 racket/list 8 file/convertible ;; attached into new namespace via anchor 9 racket/serialize ;; attached into new namespace via anchor 10 racket/pretty ;; attached into new namespace via anchor 11 scribble/private/serialize ;; attached into new namespace via anchor 12 racket/sandbox racket/promise racket/port 13 racket/gui/dynamic 14 (for-syntax racket/base syntax/srcloc racket/struct) 15 racket/stxparam 16 racket/splicing 17 racket/string 18 scribble/text/wrap) 19 20(provide interaction 21 interaction0 22 interaction/no-prompt 23 interaction-eval 24 interaction-eval-show 25 racketblock+eval (rename-out [racketblock+eval schemeblock+eval]) 26 racketblock0+eval 27 racketmod+eval (rename-out [racketmod+eval schememod+eval]) 28 def+int 29 defs+int 30 examples 31 examples* 32 defexamples 33 defexamples* 34 as-examples 35 36 (contract-out 37 [make-base-eval 38 (->* [] [#:pretty-print? any/c #:lang lang-option/c] #:rest any/c any)] 39 [make-base-eval-factory 40 eval-factory/c] 41 [make-eval-factory 42 eval-factory/c] 43 [close-eval 44 (-> any/c any)] 45 46 [scribble-exn->string 47 (parameter/c (-> any/c string?))] 48 [scribble-eval-handler 49 (parameter/c (-> (-> any/c any) boolean? any/c any))] 50 [make-log-based-eval 51 (-> path-string? (or/c 'record 'replay) any)]) 52 53 with-eval-preserve-source-locations) 54 55(define lang-option/c 56 (or/c module-path? (list/c 'special symbol?) (cons/c 'begin list?))) 57 58(define eval-factory/c 59 (->* [(listof module-path?)] [#:pretty-print? any/c #:lang lang-option/c] any)) 60 61(define scribble-eval-handler 62 (make-parameter (lambda (ev c? x) (ev x)))) 63 64(define image-counter 0) 65 66(define maxlen 60) 67 68(define-namespace-anchor anchor) 69 70(define (literal-string style s) 71 (let ([m (regexp-match #rx"^(.*)( +|^ )(.*)$" s)]) 72 (if m 73 (make-element #f (list (literal-string style (cadr m)) 74 (hspace (string-length (caddr m))) 75 (literal-string style (cadddr m)))) 76 (make-element style (list s))))) 77 78(define list.flow.list (compose1 list make-flow list)) 79 80(define (format-output str style) 81 (if (string=? "" str) 82 '() 83 (list (list.flow.list 84 (let ([s (regexp-split #rx"\n" (regexp-replace #rx"\n$" str ""))]) 85 (if (= 1 (length s)) 86 (make-paragraph (list (literal-string style (car s)))) 87 (make-table 88 #f 89 (map (lambda (s) 90 (list.flow.list 91 (make-paragraph (list (literal-string style s))))) 92 s)))))))) 93 94(define (format-output-stream in style) 95 (define (add-string string-accum line-accum) 96 (if string-accum 97 (cons (list->string (reverse string-accum)) 98 (or line-accum null)) 99 line-accum)) 100 (define (add-line line-accum flow-accum) 101 (if line-accum 102 (cons (make-paragraph 103 (map (lambda (s) 104 (if (string? s) (literal-string style s) s)) 105 (reverse line-accum))) 106 flow-accum) 107 flow-accum)) 108 (let loop ([string-accum #f] [line-accum #f] [flow-accum null]) 109 (let ([v (read-char-or-special in)]) 110 (cond 111 [(eof-object? v) 112 (let* ([line-accum (add-string string-accum line-accum)] 113 [flow-accum (add-line line-accum flow-accum)]) 114 (if (null? flow-accum) 115 null 116 (list 117 (list.flow.list 118 (if (= 1 (length flow-accum)) 119 (car flow-accum) 120 (make-table 121 #f 122 (map list.flow.list (reverse flow-accum))))))))] 123 [(equal? #\newline v) 124 (loop #f #f (add-line (add-string string-accum line-accum) 125 flow-accum))] 126 [(char? v) 127 (loop (cons v (or string-accum null)) line-accum flow-accum)] 128 [else 129 (loop #f (cons v (or (add-string string-accum line-accum) null)) 130 flow-accum)])))) 131 132(define (string->wrapped-lines str) 133 (apply 134 append 135 (for/list ([line-str (regexp-split #rx"\n" str)]) 136 (wrap-line line-str maxlen 137 (λ (word fits) 138 (if ((string-length word) . > . maxlen) 139 (values (substring word 0 fits) (substring word fits) #f) 140 (values #f word #f))))))) 141 142(struct formatted-result (content)) 143 144(define (interleave inset? title expr-paras promptless?+val-list+outputs) 145 (let ([lines 146 (let loop ([expr-paras expr-paras] 147 [promptless?+val-list+outputs promptless?+val-list+outputs] 148 [first? #t] 149 [after-blank? #t]) 150 (if (null? expr-paras) 151 null 152 (append 153 (if (and (caar promptless?+val-list+outputs) 154 (not after-blank?)) 155 (list (list (list blank-line))) 156 null) 157 (list (list (let ([p (car expr-paras)]) 158 (if (flow? p) p (make-flow (list p)))))) 159 (format-output (cadr (cdar promptless?+val-list+outputs)) output-color) 160 (format-output (caddr (cdar promptless?+val-list+outputs)) error-color) 161 (cond 162 [(string? (cadar promptless?+val-list+outputs)) 163 ;; Error result case: 164 (map (lambda (s) 165 (define p (format-output s error-color)) 166 (if (null? p) 167 (list null) 168 (car p))) 169 (string->wrapped-lines (cadar promptless?+val-list+outputs)))] 170 [(box? (cadar promptless?+val-list+outputs)) 171 ;; Output written to a port 172 (format-output-stream (unbox (cadar promptless?+val-list+outputs)) 173 result-color)] 174 [else 175 ;; Normal result case: 176 (let ([val-list (cadar promptless?+val-list+outputs)]) 177 (if (equal? val-list (list (void))) 178 null 179 (map (lambda (v) 180 (list.flow.list 181 (make-paragraph 182 (list (if (formatted-result? v) 183 (formatted-result-content v) 184 (elem #:style result-color 185 (to-element/no-color 186 v #:expr? (print-as-expression)))))))) 187 val-list)))]) 188 (if (and (caar promptless?+val-list+outputs) 189 (pair? (cdr promptless?+val-list+outputs))) 190 (list (list (list blank-line))) 191 null) 192 (loop (cdr expr-paras) (cdr promptless?+val-list+outputs) #f (caar promptless?+val-list+outputs)))))]) 193 (if inset? 194 (let ([p (code-inset (make-table block-color lines))]) 195 (if title 196 (compound-paragraph 197 plain 198 (list 199 title 200 p)) 201 p)) 202 (if title 203 (compound-paragraph plain 204 (list 205 title 206 (make-table block-color lines))) 207 (make-table block-color lines))))) 208 209;; extracts from a datum or syntax object --- while keeping the 210;; syntax-objectness of the original intact, instead of always 211;; generating a syntax object or always generating a datum 212(define (extract s . ops) 213 (let loop ([s s] [ops ops]) 214 (cond [(null? ops) s] 215 [(syntax? s) (loop (syntax-e s) ops)] 216 [else (loop ((car ops) s) (cdr ops))]))) 217 218(struct nothing-to-eval ()) 219 220(struct eval-results (contents out err)) 221(define (make-eval-results contents out err) 222 (unless (and (list? contents) 223 (andmap content? contents)) 224 (raise-argument-error 'eval:results "(listof content?)" contents)) 225 (unless (string? out) 226 (raise-argument-error 'eval:results "string?" out)) 227 (unless (string? err) 228 (raise-argument-error 'eval:results "string?" err)) 229 (eval-results contents out err)) 230(define (make-eval-result content out err) 231 (unless (content? content) 232 (raise-argument-error 'eval:result "content?" content)) 233 (unless (string? out) 234 (raise-argument-error 'eval:result "string?" out)) 235 (unless (string? err) 236 (raise-argument-error 'eval:result "string?" err)) 237 (eval-results (list content) out err)) 238 239(define (extract-to-evaluate s val handle-one) 240 (let loop ([val val] [s s] [expect #f] [error-expected? #f] [promptless? #f]) 241 (syntax-case s (code:line code:comment code:contract eval:no-prompt eval:alts eval:check eval:error) 242 [(code:line v (code:comment . rest)) 243 (loop val (extract s cdr car) expect error-expected? promptless?)] 244 [(code:line v ...) 245 (for/fold ([val val]) ([v (in-list (extract s cdr))]) 246 (loop val v expect error-expected? promptless?))] 247 [(code:comment . rest) 248 (handle-one val (nothing-to-eval) expect error-expected? promptless?)] 249 [(code:contract . rest) 250 (handle-one val (nothing-to-eval) expect error-expected? promptless?)] 251 [(eval:no-prompt e ...) 252 (for/fold ([val val]) ([v (in-list (extract s cdr))]) 253 (handle-one val v expect error-expected? #t))] 254 [(eval:error e) 255 (handle-one val (extract s cdr car) expect #t promptless?)] 256 [(eval:alts p e) 257 (handle-one val (extract s cdr cdr car) expect error-expected? promptless?)] 258 [(eval:check e expect) 259 (handle-one val 260 (extract s cdr car) 261 (list (syntax->datum (datum->syntax #f (extract s cdr cdr car)))) 262 error-expected? 263 promptless?)] 264 [else (handle-one val s expect error-expected? promptless?)]))) 265 266(define (do-eval ev who no-errors?) 267 (define (get-outputs) 268 (define (get getter what) 269 (define s (getter ev)) 270 (if (string? s) 271 s 272 (error who "missing ~a, possibly from a sandbox without a `sandbox-~a' configured to 'string" 273 what (string-join (string-split what) "-")))) 274 (list (get get-output "output") (get get-error-output "error output"))) 275 (define (render-value v) 276 (let-values ([(eval-print eval-print-as-expr?) 277 (call-in-sandbox-context ev 278 (lambda () (values (current-print) (print-as-expression))))]) 279 (cond [(and (eq? eval-print (current-print)) 280 eval-print-as-expr?) 281 ;; default printer => get result as S-expression 282 (make-reader-graph (copy-value v (make-hasheq)))] 283 [else 284 ;; other printer => go through a pipe 285 ;; If it happens to be the pretty printer, tell it to retain 286 ;; convertible objects (via write-special) 287 (box (call-in-sandbox-context 288 ev 289 (lambda () 290 (define-values [in out] (make-pipe-with-specials)) 291 (parameterize ((current-output-port out) 292 (pretty-print-size-hook 293 (lambda (obj _mode _out) 294 (and (convertible? obj) 1))) 295 (pretty-print-print-hook 296 (lambda (obj _mode out) 297 (write-special (if (serializable? obj) 298 (make-serialized-convertible 299 (serialize obj)) 300 obj) 301 out)))) 302 (map (current-print) v)) 303 (close-output-port out) 304 in)))]))) 305 (define (do-ev/expect s expect error-expected?) 306 (define-values (val error? render+output) 307 (with-handlers ([(lambda (x) (not (exn:break? x))) 308 (lambda (e) 309 (when (and no-errors? 310 (not error-expected?)) 311 (error 'examples 312 (string-append "exception raised in example\n" 313 " error: ~s") 314 (if (exn? e) 315 (exn-message e) 316 e))) 317 (values e 318 #t 319 (cons ((scribble-exn->string) e) 320 (get-outputs))))]) 321 (define val (do-plain-eval ev s #t)) 322 (values val #f (cons (render-value val) (get-outputs))))) 323 (when (and error-expected? (not error?)) 324 (error 'eval "interaction failed to raise an expected exception: ~.s" s)) 325 (when expect 326 (let ([expect (do-plain-eval ev (car expect) #t)]) 327 (unless (equal? val expect) 328 (define result " result: ") 329 (define expected " expected: ") 330 (error 'eval "example result check failed: ~.s\n~a\n~a\n" 331 s 332 (string-append result (to-lines val (string-length result))) 333 (string-append expected (to-lines expect (string-length expected))))))) 334 render+output) 335 336 (define (to-lines exps blank-space) 337 (define blank (make-string blank-space #\space)) 338 (apply 339 string-append 340 (for/list ([exp (in-list exps)] 341 [i (in-naturals)]) 342 (define first-line? (= i 0)) 343 (if (= i 0) 344 (format "~e" exp) 345 (format "\n~a~e" blank exp))))) 346 347 (lambda (str) 348 (if (eval-results? str) 349 (list #f 350 (map formatted-result (eval-results-contents str)) 351 (eval-results-out str) 352 (eval-results-err str)) 353 (extract-to-evaluate 354 str 355 (list #f (list (void)) "" "") 356 (lambda (result s expect error-expected? promptless?) 357 (if (nothing-to-eval? s) 358 result 359 (cons promptless? (do-ev/expect s expect error-expected?)))))))) 360 361(module+ test 362 (require rackunit) 363 (test-case 364 "eval:check in interaction" 365 (check-not-exn (λ () (interaction (eval:check #t #t)))))) 366 367(define scribble-exn->string 368 (make-parameter 369 (λ (e) 370 (if (exn? e) 371 (exn-message e) 372 (format "uncaught exception: ~s" e))))) 373 374;; Since we evaluate everything in an interaction before we typeset, 375;; copy each value to avoid side-effects. 376(define (copy-value v ht) 377 (define (install v v2) (hash-set! ht v v2) v2) 378 (let loop ([v v]) 379 (cond 380 [(and v (hash-ref ht v #f)) => (lambda (v) v)] 381 [(syntax? v) (make-literal-syntax v)] 382 [(string? v) (install v (string-copy v))] 383 [(bytes? v) (install v (bytes-copy v))] 384 [(pair? v) 385 (let ([ph (make-placeholder #f)]) 386 (hash-set! ht v ph) 387 (placeholder-set! ph (cons (loop (car v)) (loop (cdr v)))) 388 ph)] 389 [(mpair? v) 390 (let ([p (mcons #f #f)]) 391 (hash-set! ht v p) 392 (set-mcar! p (loop (mcar v))) 393 (set-mcdr! p (loop (mcdr v))) 394 p)] 395 [(vector? v) 396 (let ([v2 (make-vector (vector-length v))]) 397 (hash-set! ht v v2) 398 (for ([i (in-range (vector-length v2))]) 399 (vector-set! v2 i (loop (vector-ref v i)))) 400 v2)] 401 [(box? v) 402 (let ([v2 (box #f)]) 403 (hash-set! ht v v2) 404 (set-box! v2 (loop (unbox v))) 405 v2)] 406 [(hash? v) 407 (let ([ph (make-placeholder #f)]) 408 (hash-set! ht v ph) 409 (let ([a (hash-map v (lambda (k v) (cons (loop k) (loop v))))]) 410 (placeholder-set! 411 ph 412 (cond [(hash-eq? v) (make-hasheq-placeholder a)] 413 [(hash-eqv? v) (make-hasheqv-placeholder a)] 414 [else (make-hash-placeholder a)]))) 415 ph)] 416 [else v]))) 417 418(define (strip-comments stx) 419 (cond 420 [(syntax? stx) 421 (datum->syntax stx (strip-comments (syntax-e stx)) stx stx stx)] 422 [(pair? stx) 423 (define a (car stx)) 424 (define (comment? a) 425 (and (pair? a) 426 (or (eq? (car a) 'code:comment) 427 (eq? (car a) 'code:contract) 428 (and (identifier? (car a)) 429 (or (eq? (syntax-e (car a)) 'code:comment) 430 (eq? (syntax-e (car a)) 'code:contract)))))) 431 (if (or (comment? a) (and (syntax? a) (comment? (syntax-e a)))) 432 (strip-comments (cdr stx)) 433 (cons (strip-comments a) 434 (strip-comments (cdr stx))))] 435 [(eq? stx 'code:blank) (void)] 436 [else stx])) 437 438(define (make-base-eval #:lang [lang '(begin)] #:pretty-print? [pretty-print? #t] . ips) 439 (call-with-trusted-sandbox-configuration 440 (lambda () 441 (parameterize ([sandbox-output 'string] 442 [sandbox-error-output 'string] 443 [sandbox-propagate-breaks #f] 444 [sandbox-namespace-specs 445 (append (sandbox-namespace-specs) 446 (if pretty-print? 447 '(racket/pretty) 448 '()) 449 '(file/convertible 450 racket/serialize 451 scribble/private/serialize))]) 452 (let ([e (apply make-evaluator lang ips)]) 453 (when pretty-print? 454 (call-in-sandbox-context e 455 (lambda () 456 (current-print (dynamic-require 'racket/pretty 'pretty-print-handler))))) 457 e))))) 458 459(define (make-base-eval-factory mod-paths 460 #:lang [lang '(begin)] 461 #:pretty-print? [pretty-print? #t] . ips) 462 (parameterize ([sandbox-namespace-specs 463 (cons (λ () (let ([ns 464 ;; This namespace-creation choice needs to be consistent 465 ;; with the sandbox (i.e., with `make-base-eval') 466 (if gui? 467 ((gui-dynamic-require 'make-gui-empty-namespace)) 468 (make-base-empty-namespace))]) 469 (parameterize ([current-namespace ns]) 470 (for ([mod-path (in-list mod-paths)]) 471 (dynamic-require mod-path #f)) 472 (when pretty-print? (dynamic-require 'racket/pretty #f))) 473 ns)) 474 (append mod-paths (if pretty-print? '(racket/pretty) '())))]) 475 (lambda () 476 (let ([ev (apply make-base-eval #:lang lang #:pretty-print? #f ips)]) 477 (when pretty-print? 478 (call-in-sandbox-context ev 479 (lambda () 480 (current-print (dynamic-require 'racket/pretty 'pretty-print-handler))))) 481 ev)))) 482 483(define (make-eval-factory mod-paths 484 #:lang [lang '(begin)] 485 #:pretty-print? [pretty-print? #t] . ips) 486 (let ([base-factory (apply make-base-eval-factory mod-paths #:lang lang #:pretty-print? pretty-print? ips)]) 487 (lambda () 488 (let ([ev (base-factory)]) 489 (call-in-sandbox-context 490 ev 491 (lambda () 492 (for ([mod-path (in-list mod-paths)]) 493 (namespace-require mod-path)))) 494 ev)))) 495 496(define (make-log-based-eval logfile mode) 497 (case mode 498 ((record) (make-eval/record logfile)) 499 ((replay) (make-eval/replay logfile)))) 500 501(define (make-eval/record logfile) 502 (let* ([ev (make-base-eval)] 503 [super-cust (current-custodian)] 504 [out (parameterize ((current-custodian (get-user-custodian ev))) 505 (open-output-file logfile #:exists 'replace))]) 506 (display ";; This file was created by make-log-based-eval\n" out) 507 (flush-output out) 508 (call-in-sandbox-context ev 509 (lambda () 510 ;; Required for serialization to work. 511 (namespace-attach-module (namespace-anchor->namespace anchor) 'racket/serialize) 512 (let ([old-eval (current-eval)] 513 [init-out-p (current-output-port)] 514 [init-err-p (current-error-port)] 515 [out-p (open-output-bytes)] 516 [err-p (open-output-bytes)]) 517 (current-eval 518 (lambda (x) 519 (let* ([x (syntax->datum (datum->syntax #f x))] 520 [x (if (and (pair? x) (eq? (car x) '#%top-interaction)) (cdr x) x)] 521 [result 522 (with-handlers ([exn? values]) 523 (call-with-values (lambda () 524 (parameterize ((current-eval old-eval) 525 (current-custodian (make-custodian)) 526 (current-output-port out-p) 527 (current-error-port err-p)) 528 (begin0 (old-eval x) 529 (wait-for-threads (current-custodian) super-cust)))) 530 list))] 531 [out-s (get-output-bytes out-p #t)] 532 [err-s (get-output-bytes err-p #t)]) 533 (let ([result* (serialize (cond [(list? result) (cons 'values result)] 534 [(exn? result) (list 'exn (exn-message result))]))]) 535 (pretty-write (list x result* out-s err-s) out) 536 (flush-output out)) 537 (display out-s init-out-p) 538 (display err-s init-err-p) 539 (cond [(list? result) (apply values result)] 540 [(exn? result) (raise result)]))))))) 541 ev)) 542 543;; Wait for threads created by evaluation so that the evaluator catches output 544;; they generate, etc. 545;; FIXME: see what built-in scribble evaluators do 546(define (wait-for-threads sub-cust super-cust) 547 (let ([give-up-evt (alarm-evt (+ (current-inexact-milliseconds) 200.0))]) 548 ;; find a thread to wait on 549 (define (find-thread cust) 550 (let* ([managed (custodian-managed-list cust super-cust)] 551 [thds (filter thread? managed)] 552 [custs (filter custodian? managed)]) 553 (cond [(pair? thds) (car thds)] 554 [else (ormap find-thread custs)]))) 555 ;; keep waiting on threads (one at a time) until time to give up 556 (define (wait-loop cust) 557 (let ([thd (find-thread cust)]) 558 (when thd 559 (cond [(eq? give-up-evt (sync thd give-up-evt)) (void)] 560 [else (wait-loop cust)])))) 561 (wait-loop sub-cust))) 562 563(define (make-eval/replay logfile) 564 (let* ([ev (make-base-eval)] 565 [evaluations (file->list logfile)]) 566 (call-in-sandbox-context ev 567 (lambda () 568 (namespace-attach-module (namespace-anchor->namespace anchor) 'racket/serialize) 569 (let ([old-eval (current-eval)] 570 [init-out-p (current-output-port)] 571 [init-err-p (current-error-port)]) 572 (current-eval 573 (lambda (x) 574 (let* ([x (syntax->datum (datum->syntax #f x))] 575 [x (if (and (pair? x) (eq? (car x) '#%top-interaction)) (cdr x) x)]) 576 (unless (and (pair? evaluations) (equal? x (car (car evaluations)))) 577 ;; TODO: smarter resync 578 ;; - can handle *additions* by removing next set! 579 ;; - can handle *deletions* by searching forward (but may jump to far 580 ;; if terms occur more than once, eg for stateful code) 581 ;; For now, just fail early and often. 582 (set! evaluations null) 583 (error 'eval "unable to replay evaluation of ~.s" x)) 584 (let* ([evaluation (car evaluations)] 585 [result (parameterize ((current-eval old-eval)) 586 (deserialize (cadr evaluation)))] 587 [result (case (car result) 588 ((values) (cdr result)) 589 ((exn) (make-exn (cadr result) (current-continuation-marks))))] 590 [output (caddr evaluation)] 591 [error-output (cadddr evaluation)]) 592 (set! evaluations (cdr evaluations)) 593 (display output init-out-p #| (current-output-port) |#) 594 (display error-output init-err-p #| (current-error-port) |#) 595 (cond [(exn? result) (raise result)] 596 [(list? result) (apply values result)])))))))) 597 ev)) 598 599(define (close-eval e) 600 (kill-evaluator e) 601 "") 602 603(define (do-plain-eval ev s catching-exns?) 604 (parameterize ([sandbox-propagate-breaks #f]) 605 (call-with-values 606 (lambda () 607 ((scribble-eval-handler) 608 ev 609 catching-exns? 610 (let ([s (strip-comments s)]) 611 (cond [(syntax? s) 612 (syntax-case s (module) 613 [(module . _rest) (syntax->datum s)] 614 [_else s])] 615 ;; a sandbox treats strings and byte strings as code 616 ;; streams, so protect them as syntax objects: 617 [(string? s) (datum->syntax #f s)] 618 [(bytes? s) (datum->syntax #f s)] 619 [else s])))) 620 list))) 621 622(define-syntax-parameter quote-expr-preserve-source? #f) 623 624(define-syntax (with-eval-preserve-source-locations stx) 625 (syntax-case stx () 626 [(with-eval-preserve-source-locations e ...) 627 (syntax/loc stx 628 (splicing-syntax-parameterize ([quote-expr-preserve-source? #t]) 629 e ...))])) 630 631;; Quote an expression to be evaluated or wrap as escaped: 632(define-syntax quote-expr 633 (syntax-rules (eval:alts eval:result eval:results) 634 [(_ (eval:alts e1 e2)) (quote-expr e2)] 635 [(_ (eval:result e)) (make-eval-result (list e) "" "")] 636 [(_ (eval:result e out)) (make-eval-result (list e) out "")] 637 [(_ (eval:result e out err)) (make-eval-result (list e) out err)] 638 [(_ (eval:results es)) (make-eval-results es "" "")] 639 [(_ (eval:results es out)) (make-eval-results es out "")] 640 [(_ (eval:results es out err)) (make-eval-results es out err)] 641 [(_ e) (base-quote-expr e)])) 642 643(define orig-stx (read-syntax 'orig (open-input-string "()"))) 644 645(define-syntax (base-quote-expr stx) 646 (syntax-case stx () 647 [(_ e) 648 (cond [(syntax-parameter-value #'quote-expr-preserve-source?) 649 ;; Preserve source; produce an expression resulting in a 650 ;; syntax object with no lexical context (like strip-context) 651 ;; but with (quotable) source locations. 652 ;; Also preserve syntax-original?, since that seems important 653 ;; to some syntax-based code (eg redex term->pict). 654 (define (get-source-location e) 655 (let* ([src (build-source-location-list e)] 656 [old-source (source-location-source src)] 657 [new-source 658 (cond [(path? old-source) ;; not quotable/writable 659 ;;(path->string old-source) ;; don't leak build paths 660 'eval] 661 [(or (string? old-source) 662 (symbol? old-source)) 663 ;; Okay? Or should this be replaced also? 664 old-source] 665 [else #f])]) 666 (update-source-location src #:source new-source))) 667 (let loop ([e #'e]) 668 (cond [(syntax? e) 669 (let ([src (get-source-location e)] 670 [original? (syntax-original? (syntax-local-introduce e))]) 671 #`(syntax-property 672 (datum->syntax #f 673 #,(loop (syntax-e e)) 674 (quote #,src) 675 #,(if original? #'orig-stx #'#f)) 676 'paren-shape 677 (quote #,(syntax-property e 'paren-shape))))] 678 [(pair? e) 679 #`(cons #,(loop (car e)) #,(loop (cdr e)))] 680 [(vector? e) 681 #`(list->vector #,(loop (vector->list e)))] 682 [(box? e) 683 #`(box #,(loop (unbox e)))] 684 [(prefab-struct-key e) 685 => (lambda (key) 686 #`(apply make-prefab-struct 687 (quote #,key) 688 #,(loop (struct->list e))))] 689 [else 690 #`(quote #,e)]))] 691 [else 692 ;; Using quote means that sandbox evaluation works on 693 ;; sexprs; to get it to work on syntaxes, use 694 ;; (strip-context (quote-syntax e))) 695 ;; while importing 696 ;; (require syntax/strip-context) 697 #'(quote e)])])) 698 699(define (do-interaction-eval ev es) 700 (for/fold ([ev ev]) ([e (in-list es)]) 701 (extract-to-evaluate 702 e 703 ev 704 (lambda (ev e expect error-expected?/ignored promptless?/ignored) 705 (cond 706 [(nothing-to-eval? e) ev] 707 [else 708 (parameterize ([current-command-line-arguments #()]) 709 (let ([ev (or ev (make-base-eval))]) 710 (do-plain-eval ev e #f) 711 ev))])))) 712 "") 713 714(define-syntax interaction-eval 715 (syntax-rules () 716 [(_ #:eval ev e ...) (do-interaction-eval ev (list (quote-expr e) ...))] 717 [(_ e ...) (do-interaction-eval #f (list (quote-expr e) ...))])) 718 719(define (show-val v) 720 (elem #:style result-color 721 (to-element/no-color v #:expr? (print-as-expression)))) 722 723(define (do-interaction-eval-show ev es) 724 (parameterize ([current-command-line-arguments #()]) 725 (let ([ev (or ev (make-base-eval))]) 726 (show-val (car (for/fold ([v (list #f)]) ([e (in-list es)]) 727 (extract-to-evaluate 728 e 729 v 730 (lambda (prev-v e expect error-expected?/ignored promptless?/ignored) 731 (do-plain-eval ev e #f))))))))) 732 733(define-syntax interaction-eval-show 734 (syntax-rules () 735 [(_ #:eval ev e ...) (do-interaction-eval-show ev (list (quote-expr e) ...))] 736 [(_ e ...) (do-interaction-eval-show #f (list (quote-expr e) ...))])) 737 738(define-syntax racketinput* 739 (syntax-rules (eval:alts code:comment eval:check eval:no-prompt eval:error eval:result eval:results) 740 [(_ #:escape id (code:comment . rest)) (racketblock0 #:escape id (code:comment . rest))] 741 [(_ #:escape id (eval:alts a b)) (racketinput* #:escape id a)] 742 [(_ #:escape id (eval:result a . _)) (racketinput* #:escape id a)] 743 [(_ #:escape id (eval:results a . _)) (racketinput* #:escape id a)] 744 [(_ #:escape id (eval:check a b)) (racketinput* #:escape id a)] 745 [(_ #:escape id (eval:error a)) (racketinput* #:escape id a)] 746 [(_ #:escape id (eval:no-prompt a ...)) (racketblock* #:escape id (code:line a ...))] 747 [(_ #:escape id e) (racketinput0 #:escape id e)])) 748 749(define-syntax racketblock* 750 (syntax-rules (eval:alts code:comment eval:check eval:no-prompt eval:error eval:result eval:results) 751 [(_ #:escape id (code:comment . rest)) (racketblock0 #:escape id (code:comment . rest))] 752 [(_ #:escape id (eval:alts a b)) (racketblock* #:escape id a)] 753 [(_ #:escape id (eval:result a . _)) (racketinputblock #:escape id a)] 754 [(_ #:escape id (eval:results a . _)) (racketinputblock #:escape id a)] 755 [(_ #:escape id (eval:check a b)) (racketblock #:escape id a)] 756 [(_ #:escape id (eval:no-prompt a ...)) (racketblock #:escape id (code:line a ...))] 757 [(_ #:escape id (eval:error a)) (racketblock #:escape id a)] 758 [(_ #:escape id e) (racketblock0 #:escape id e)])) 759 760(define-code racketblock0+line (to-paragraph/prefix "" "" (list " "))) 761 762(define-syntax (racketdefinput* stx) 763 (syntax-case stx (define define-values define-struct) 764 [(_ #:escape id (define . rest)) 765 (syntax-case stx () 766 [(_ #:escape _ e) #'(racketblock0+line #:escape id e)])] 767 [(_ #:escape id (define-values . rest)) 768 (syntax-case stx () 769 [(_ #:escape _ e) #'(racketblock0+line #:escape id e)])] 770 [(_ #:escape id (define-struct . rest)) 771 (syntax-case stx () 772 [(_ #:escape _ e) #'(racketblock0+line #:escape id e)])] 773 [(_ #:escape id (code:line (define . rest) . rest2)) 774 (syntax-case stx () 775 [(_ #:escape _ e) #'(racketblock0+line #:escape id e)])] 776 [(_ #:escape id e) #'(racketinput* #:escape id e)])) 777 778(define (do-titled-interaction who inset? no-errors? ev t shows evals) 779 (interleave inset? t shows (map (do-eval ev who no-errors?) evals))) 780 781(define-syntax titled-interaction 782 (syntax-rules () 783 [(_ who inset? t racketinput* 784 #:eval ev #:escape unsyntax-id #:no-errors? no-errors? 785 e ...) 786 (do-titled-interaction 787 'who inset? no-errors? ev t 788 (list (racketinput* #:escape unsyntax-id e) ...) 789 (list (quote-expr e) ...))] 790 791 [(_ who inset? t racketinput* 792 #:eval ev #:escape unsyntax-id 793 e ...) 794 (titled-interaction 795 who inset? t racketinput* 796 #:eval ev #:escape unsyntax-id #:no-errors? #f 797 e ...)] 798 [(_ who inset? t racketinput* 799 #:eval ev #:no-errors? no-errors? 800 e ...) 801 (titled-interaction 802 who inset? t racketinput* 803 #:eval ev #:escape unsyntax #:no-errors? no-errors? 804 e ...)] 805 [(_ who inset? t racketinput* 806 #:escape unsyntax-id #:no-errors? no-errors? 807 e ...) 808 (titled-interaction 809 who inset? t racketinput* 810 #:eval (make-base-eval) #:escape unsyntax-id #:no-errors? no-errors? 811 e ...)] 812 [(_ who inset? t racketinput* 813 #:eval ev 814 e ...) 815 (titled-interaction 816 who inset? t racketinput* 817 #:eval ev #:escape unsyntax #:no-errors? #f 818 e ...)] 819 [(_ who inset? t racketinput* 820 #:escape unsyntax-id 821 e ...) 822 (titled-interaction 823 who inset? t racketinput* 824 #:eval (make-base-eval) #:escape unsyntax-id 825 e ...)] 826 [(_ who inset? t racketinput* 827 #:no-errors? no-errors? 828 e ...) 829 (titled-interaction 830 who inset? t racketinput* 831 #:eval (make-base-eval) #:escape unsyntax #:no-errors? no-errors? 832 e ...)] 833 [(_ who inset? t racketinput* e ...) 834 (titled-interaction 835 who inset? t racketinput* 836 #:eval (make-base-eval) #:escape unsyntax #:no-errors? #f 837 e ...)])) 838 839(define-syntax (-interaction stx) 840 (syntax-case stx () 841 [(_ who e ...) 842 (syntax/loc stx 843 (titled-interaction who #f #f racketinput* e ...))])) 844 845(define-syntax (interaction stx) 846 (syntax-case stx () 847 [(H e ...) (syntax/loc stx (code-inset (-interaction H e ...)))])) 848 849(define-syntax (interaction/no-prompt stx) 850 (syntax-case stx () 851 [(H e ...) 852 (syntax/loc stx 853 (code-inset (titled-interaction who #f #f racketblock* e ...)))])) 854 855(define-syntax (interaction0 stx) 856 (syntax-case stx () 857 [(H e ...) (syntax/loc stx (-interaction H e ...))])) 858 859(define-syntax racketblockX+eval 860 (syntax-rules () 861 [(_ racketblock #:eval ev #:escape unsyntax-id e ...) 862 (let ([eva ev]) 863 (#%expression 864 (begin (interaction-eval #:eval eva e ...) 865 (racketblock #:escape unsyntax-id e ...))))] 866 [(_ racketblock #:eval ev e ...) 867 (racketblockX+eval racketblock #:eval ev #:escape unsyntax e ...)] 868 [(_ racketblock #:escape unsyntax-id e ...) 869 (racketblockX+eval racketblock #:eval (make-base-eval) #:escape unsyntax-id e ...)] 870 [(_ racketblock e ...) 871 (racketblockX+eval racketblock #:eval (make-base-eval) #:escape unsyntax e ...)])) 872 873(define-syntax racketblock+eval 874 (syntax-rules () 875 [(_ e ...) 876 (racketblockX+eval racketblock e ...)])) 877 878(define-syntax racketblock0+eval 879 (syntax-rules () 880 [(_ e ...) 881 (racketblockX+eval racketblock0 e ...)])) 882 883(define-syntax racketmod+eval 884 (syntax-rules () 885 [(_ #:eval ev #:escape unsyntax-id name e ...) 886 (let ([eva ev]) 887 (#%expression 888 (begin (interaction-eval #:eval eva e ...) 889 (racketmod #:escape unsyntax-id name e ...))))] 890 [(_ #:eval ev name e ...) 891 (racketmod+eval #:eval ev #:escape unsyntax name e ...)] 892 [(_ #:escape unsyntax-id name e ...) 893 (racketmod+eval #:eval (make-base-eval) #:escape unsyntax-id name e ...)] 894 [(_ name e ...) 895 (racketmod+eval #:eval (make-base-eval) #:escape unsyntax name e ...)])) 896 897(define-syntax (defs+int stx) 898 (syntax-case stx () 899 [(H #:eval ev #:escape unsyntax-id [def ...] e ...) 900 (syntax/loc stx 901 (let ([eva ev]) 902 (column (list (racketblock0+eval #:eval eva #:escape unsyntax-id def ...) 903 blank-line 904 (-interaction H #:eval eva #:escape unsyntax-id e ...)))))] 905 [(H #:eval ev [def ...] e ...) 906 (syntax/loc stx (defs+int #:eval ev #:escape unsyntax [def ...] e ...))] 907 [(_ #:escape unsyntax-id [def ...] e ...) 908 (syntax/loc stx (defs+int #:eval (make-base-eval) #:escape unsyntax-id [def ...] e ...))] 909 [(_ [def ...] e ...) 910 (syntax/loc stx (defs+int #:eval (make-base-eval) [def ...] e ...))])) 911 912(define-syntax def+int 913 (syntax-rules () 914 [(H #:eval ev #:escape unsyntax-id def e ...) 915 (defs+int #:eval ev #:escape unsyntax-id [def] e ...)] 916 [(H #:eval ev def e ...) 917 (defs+int #:eval ev [def] e ...)] 918 [(H #:escape unsyntax-id def e ...) 919 (defs+int #:escape unsyntax-id [def] e ...)] 920 [(H def e ...) 921 (defs+int [def] e ...)])) 922 923(define example-title 924 (make-paragraph (list "Example:"))) 925(define examples-title 926 (make-paragraph (list "Examples:"))) 927 928(define-syntax pick-example-title 929 (syntax-rules () 930 [(_ e) example-title] 931 [(_ #:eval ev e) example-title] 932 [(_ #:escape id e) example-title] 933 [(_ #:eval ev #:escape id e) example-title] 934 [(_ . _) examples-title])) 935 936(define-syntax (examples stx) 937 (syntax-case stx () 938 [(H e ...) 939 (syntax/loc stx 940 (titled-interaction 941 H #t (pick-example-title e ...) racketinput* e ...))])) 942(define-syntax (examples* stx) 943 (syntax-case stx () 944 [(H example-title e ...) 945 (syntax/loc stx 946 (titled-interaction H #t example-title racketinput* e ...))])) 947(define-syntax (defexamples stx) 948 (syntax-case stx () 949 [(H e ...) 950 (syntax/loc stx 951 (titled-interaction 952 H #t (pick-example-title e ...) racketdefinput* e ...))])) 953(define-syntax (defexamples* stx) 954 (syntax-case stx () 955 [(H example-title e ...) 956 (syntax/loc stx 957 (titled-interaction H #t example-title racketdefinput* e ...))])) 958 959(define blank-line (make-paragraph (list 'nbsp))) 960 961(define (column l) 962 (code-inset (make-table #f (map list.flow.list l)))) 963 964(define (do-splice l) 965 (cond [(null? l) null] 966 [(splice? (car l)) `(,@(splice-run (car l)) ,@(do-splice (cdr l)))] 967 [else (cons (car l) (do-splice (cdr l)))])) 968 969(define as-examples 970 (case-lambda 971 [(t) (as-examples examples-title t)] 972 [(example-title t) 973 (if example-title 974 (compound-paragraph 975 plain 976 (list 977 (if (block? example-title) 978 example-title 979 (make-paragraph (list example-title))) 980 t)) 981 t)])) 982