File: mod.rkt

package info (click to toggle)
racket-mode 20210916git0-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 1,076 kB
  • sloc: lisp: 10,354; makefile: 58
file content (152 lines) | stat: -rw-r--r-- 5,814 bytes parent folder | download
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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
#lang at-exp racket/base

(require (for-syntax racket/base
                     syntax/parse)
         racket/contract/base
         racket/contract/region
         racket/format
         racket/match
         racket/string
         syntax/location
         "util.rkt")

(provide relative-module-path?
         (struct-out mod)
         ->mod/existing
         maybe-mod->dir/file/rmp
         maybe-mod->prompt-string
         maybe-warn-about-submodules)

(module+ test
  (require rackunit))

;; The subset of module-path? with a relative filename
(define (relative-module-path? v)
  (define (rel-path? v) ;real predicate taking any/c, unlike relative-path?
    (and (path-string? v) (relative-path? v)))
  (and (module-path? v)
       (match v
         [(? rel-path?) #t]
         [(list 'submod (? rel-path?) (? symbol?) ..1) #t]
         [_ #f])))

(module+ test
  (check-true (relative-module-path? "f.rkt"))
  (check-true (relative-module-path? '(submod "f.rkt" a b)))
  (check-false (relative-module-path? "/path/to/f.rkt"))
  (check-false (relative-module-path? '(submod "/path/to/f.rkt" a b)))
  (check-false (relative-module-path? 'racket/base))
  (check-false (relative-module-path? '(submod 'racket/base a b))))

(define-struct/contract mod
  ([dir  absolute-path?]         ;#<path:/path/to/>
   [file relative-path?]         ;#<path:foo.rkt>
   [rmp  relative-module-path?]) ;#<path:f.rkt> or '(submod <path:f.rkt> bar)
  #:transparent)

(define/contract (->mod/simple v)
  (-> any/c (or/c #f mod?))
  (match v
    [(? symbol? s) (->mod/simple (~a s))] ;treat 'file.rkt as "file.rkt"
    [(or (? path? ap) (? path-string? ap))
     (let*-values ([(dir file _) (split-path (simplify-path ap))]
                   [(dir) (match dir ['relative (current-directory)][dir dir])])
       (mod dir file file))]
    [_ #f]))

(define/contract (->mod v)
  (-> any/c (or/c #f mod?))
  (define-match-expander mm
    (syntax-parser
      [(_ dir:id file:id rmp:id)
       #'(app ->mod/simple (mod dir file rmp))]))
  (match v
    [(list 'submod
           (mm d f _) (? symbol? ss) ..1) (mod d f (list* 'submod f ss))]
    [(list (mm d f _) (? symbol? ss) ..1) (mod d f (list* 'submod f ss))]
    [(list (mm d f mp))                   (mod d f mp)]
    [(mm d f mp)                          (mod d f mp)]
    [_                                    #f]))

(module+ test
  (define-syntax-rule (= x y) (check-equal? x y))
  (define f.rkt (string->path "f.rkt"))
  ;; rel path
  (let ([dir (current-directory)])
    (= (->mod "f.rkt") (mod dir f.rkt f.rkt))
    (= (->mod 'f.rkt)  (mod dir f.rkt f.rkt))
    (= (->mod '(submod "f.rkt" a b)) (mod dir f.rkt `(submod ,f.rkt a b)))
    (= (->mod '(submod f.rkt a b))   (mod dir f.rkt `(submod ,f.rkt a b)))
    (= (->mod '("f.rkt" a b)) (mod dir f.rkt `(submod ,f.rkt a b)))
    (= (->mod '(f.rkt a b))   (mod dir f.rkt `(submod ,f.rkt a b)))
    (= (->mod '("f.rkt")) (mod dir f.rkt f.rkt))
    (= (->mod '(f.rkt))   (mod dir f.rkt f.rkt)))
  ;; abs path
  (let* ([top (case (system-type) [(windows) "\\"] [(unix macosx) "/"])]
         [dir (path->directory-path (build-path top "p" "t"))])
    (= (->mod (build-path "/" "p" "t" "f.rkt")) (mod dir f.rkt f.rkt))
    (= (->mod '/p/t/f.rkt)  (mod dir f.rkt f.rkt))
    (= (->mod '(submod "/p/t/f.rkt" a b)) (mod dir f.rkt `(submod ,f.rkt a b)))
    (= (->mod '(submod /p/t/f.rkt a b))   (mod dir f.rkt `(submod ,f.rkt a b)))
    (= (->mod '("/p/t/f.rkt" a b)) (mod dir f.rkt `(submod ,f.rkt a b)))
    (= (->mod '(/p/t/f.rkt a b))   (mod dir f.rkt `(submod ,f.rkt a b)))
    (= (->mod '("/p/t/f.rkt")) (mod dir f.rkt f.rkt))
    (= (->mod '(/p/t/f.rkt))   (mod dir f.rkt f.rkt)))
  ;; nonsense input => #f
  (= (->mod 42)                #f)
  (= (->mod '(42 'bar))        #f)
  (= (->mod '(submod 42 'bar)) #f)
  (= (->mod '(submod (submod "f.rkt" foo) bar)) #f))

(define/contract (->mod/existing v)
  (-> any/c (or/c #f mod?))
  (match (->mod v)
    [(and v (mod dir file mp))
     (define path (build-path dir file))
     (cond [(file-exists? path) v]
           [else (display-commented (format "~a does not exist" path))
                 #f])]
    [_ #f]))

(define/contract (maybe-mod->dir/file/rmp maybe-mod)
  (-> (or/c #f mod?) (values absolute-path?
                             (or/c #f relative-path?)
                             (or/c #f relative-module-path?)))
  (match maybe-mod
    [(mod d f mp) (values d f mp)]
    [#f           (values (current-directory) #f #f)]))

(define/contract (maybe-mod->prompt-string m)
  (-> (or/c #f mod?) string?)
  (match m
    [(mod _ _ (? path? file))     (~a file)]
    [(mod _ _ (list* 'submod xs)) (string-join (map ~a xs) "/")]
    [#f                           ""]))

;; Check whether Racket is new enough (newer than 6.2.1) that
;; module->namespace works with module+ and (module* _ #f __)
;; forms when errortrace is enabled.
(module+ check
  (define x 42))
(define (can-enter-module+-namespace?)
  (define mp (quote-module-path check))
  (dynamic-require mp #f)
  (with-handlers ([exn:fail? (λ _ #f)])
    (eval 'x (module->namespace mp))
    #t))

(define warned? #f)
(define/contract (maybe-warn-about-submodules mp context)
  (-> (or/c #f module-path?) symbol? any)
  (unless (or warned?
              (not (pair? mp)) ;not submodule
              (memq context '(low medium))
              (can-enter-module+-namespace?))
    (set! warned? #t)
    (display-commented
     @~a{Note: @~v[@mp] will be evaluated.
               However your Racket version is old. You will be unable to
               use the REPL to examine definitions in the body of a module+
               or (module* _ #f ___) form when errortrace is enabled. Either
               upgrade Racket, or, set the Emacs variable racket-error-context
               to 'low or 'medium.})))