forked from google-deepmind/deepmind-research
-
Notifications
You must be signed in to change notification settings - Fork 0
/
interact.rkt
95 lines (89 loc) · 4.45 KB
/
interact.rkt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#lang racket/base
;***************************************************************************************;
;**** User Interaction Commands ****;
;***************************************************************************************;
(require (for-syntax racket/base syntax/parse)
racket/format
racket/list
racket/match
racket/port)
(provide (all-defined-out))
;; Notice: variables set via eval or only set locally, in the local namespace,
;; and not in the main namespace.
;; variables set via the (list 'var val) pattern are set in the main namespace.
;; Even though the namespace is at the module level, the variables
;; are set in the namespace with their value so they can be used with eval.
(define-syntax (interact stx)
(syntax-parse stx
#:literals (list)
[(_ (~alt (~optional (~seq #:prompt prompt:expr)) ; must evaluate to a string, default "> "
(~optional (~seq #:command command:expr))
(~optional (~seq #:namespace-anchor ns-anchor:expr)) ; default #false
(~optional (~seq #:variables (var:id ...))) ; must be bound identifiers
(~optional (~seq #:readline? readline?:expr))) ; start with readline enabled? (#false)
...
[(list pat ...) help-string body ...+] ...) ; match patterns
(with-syntax ([(var ...) #'(~? (var ...) ())])
#'(begin
(define names (list 'var ...))
(define nsa (~? ns-anchor #false))
(define ns (and nsa (namespace-anchor->namespace nsa)))
(when (~? readline? #false) (eval '(require readline) ns))
(when ns
(namespace-set-variable-value! 'var var #false ns) ...
(void)) ; to avoid bad 'when' form if no variable
(define the-prompt (~? prompt "> "))
(let loop ()
(with-handlers ([exn:fail? (λ (e)
(displayln (exn-message e))
(loop))])
(define cmd (~? command #false))
(when (and cmd (not (string? cmd)))
(error "command must be a string"))
(unless cmd (display the-prompt))
(define cmd-str (or cmd (read-line)))
(unless (eof-object? cmd-str)
(define cmd (with-input-from-string (string-append "(" cmd-str ")") read))
(match cmd
['() (void)]
['(help)
(unless (empty? names)
(printf "Available variables: ~a\n" names))
(displayln "Other commands:")
(parameterize ([print-reader-abbreviations #true]
[print-as-expression #false])
(void)
(begin
(displayln (string-append " " (apply ~v '(pat ...) #:separator " ")))
(displayln (string-append " " help-string)))
...)
(when ns
(displayln " eval expr")
(displayln
" Evaluate expr in a namespace that is local to this interaction loop."))
(loop)]
[(list 'eval cmd)
(if ns
(call-with-values (λ () (eval cmd ns))
(λ l (if (= 1 (length l))
(unless (void? (first l))
(displayln (first l)))
(for-each displayln l))))
(displayln "Cannot use eval without a namespace-anchor"))
(loop)]
['(var) (println var) (loop)] ...
[(list 'var val) (set! var val) (loop)] ...
[(list pat ...) body ... (loop)] ...
[else (printf "Unknown command: ~a\n" cmd)
(loop)]))))))]))
;; For manual testing in DrRacket
(module+ drracket
(define-namespace-anchor ns-anchor) ; optional, to use the eval command
(let ([x 3] [y 'a])
(interact
#:prompt ">> "
#:namespace-anchor ns-anchor
#:variables (x y)
;; All patterns must be of the form (list ....)
[(list 'yo) "prints yo" (displayln "yo")]
[(list 'yo (? number? n)) "prints multiple yos" (displayln (make-list n 'yo))])))