|
4 | 4 | (require "../common.rkt" "../conversions.rkt" "../errors.rkt" "../interface.rkt" "syntax.rkt") |
5 | 5 | (provide assert-program!) |
6 | 6 |
|
7 | | -(define (check-expression* stx vars error!) |
8 | | - (match stx |
9 | | - [#`,(? number?) (void)] |
10 | | - [#`,(? constant-operator?) (void)] |
11 | | - [#`,(? variable? var) |
12 | | - (unless (set-member? vars stx) |
13 | | - (error! stx "Unknown variable ~a" var))] |
14 | | - [#`(let* ((#,vars* #,vals) ...) #,body) |
15 | | - (define bindings |
16 | | - (for/fold ([vars vars]) ([var vars*] [val vals]) |
17 | | - (unless (identifier? var) |
18 | | - (error! var "Invalid variable name ~a" var)) |
19 | | - (check-expression* val vars error!) |
20 | | - (bound-id-set-union vars (immutable-bound-id-set (list var))))) |
21 | | - (check-expression* body bindings error!)] |
22 | | - [#`(let ((#,vars* #,vals) ...) #,body) |
23 | | - ;; These are unfolded by desugaring |
24 | | - (for ([var vars*] [val vals]) |
25 | | - (unless (identifier? var) |
26 | | - (error! var "Invalid variable name ~a" var)) |
27 | | - (check-expression* val vars error!)) |
28 | | - (check-expression* body (bound-id-set-union vars (immutable-bound-id-set vars*)) error!)] |
29 | | - [#`(let #,varlist #,body) |
30 | | - (error! stx "Invalid `let` expression variable list ~a" (syntax->datum varlist)) |
31 | | - (check-expression* body vars error!)] |
32 | | - [#`(let #,args ...) |
33 | | - (error! stx "Invalid `let` expression with ~a arguments (expects 2)" (length args)) |
34 | | - (unless (null? args) (check-expression* (last args) vars error!))] |
35 | | - [#`(if #,cond #,ift #,iff) |
36 | | - (check-expression* cond vars error!) |
37 | | - (check-expression* ift vars error!) |
38 | | - (check-expression* iff vars error!)] |
39 | | - [#`(if #,args ...) |
40 | | - (error! stx "Invalid `if` expression with ~a arguments (expects 3)" (length args)) |
41 | | - (unless (null? args) (check-expression* (last args) vars error!))] |
42 | | - [#`(! #,props ... #,body) |
43 | | - (check-properties* props '() body) |
44 | | - (check-expression* body vars error!)] |
45 | | - [#`(,(? (curry set-member? '(+ - * / and or = != < > <= >=))) #,args ...) |
46 | | - ;; These expand by associativity so we don't check the number of arguments |
47 | | - (for ([arg args]) (check-expression* arg vars error!))] |
48 | | - [#`(#,f-syntax #,args ...) |
49 | | - (define f (syntax->datum f-syntax)) |
50 | | - (cond |
51 | | - [(operator-exists? f) |
| 7 | + |
| 8 | + |
| 9 | +(define (check-expression* stx vars error! deprecated-ops) |
| 10 | + (let loop ([stx stx] [vars vars]) |
| 11 | + (match stx |
| 12 | + [#`,(? number?) (void)] |
| 13 | + [#`,(? constant-operator?) (void)] |
| 14 | + [#`,(? variable? var) |
| 15 | + (unless (set-member? vars stx) |
| 16 | + (error! stx "Unknown variable ~a" var))] |
| 17 | + [#`(let* ((#,vars* #,vals) ...) #,body) |
| 18 | + (define bindings |
| 19 | + (for/fold ([vars vars]) ([var vars*] [val vals]) |
| 20 | + (unless (identifier? var) |
| 21 | + (error! var "Invalid variable name ~a" var)) |
| 22 | + (loop val vars) |
| 23 | + (bound-id-set-union vars (immutable-bound-id-set (list var))))) |
| 24 | + (loop body bindings)] |
| 25 | + [#`(let ((#,vars* #,vals) ...) #,body) |
| 26 | + ;; These are unfolded by desugaring |
| 27 | + (for ([var vars*] [val vals]) |
| 28 | + (unless (identifier? var) |
| 29 | + (error! var "Invalid variable name ~a" var)) |
| 30 | + (loop val vars)) |
| 31 | + (loop body (bound-id-set-union vars (immutable-bound-id-set vars*)))] |
| 32 | + [#`(let #,varlist #,body) |
| 33 | + (error! stx "Invalid `let` expression variable list ~a" (syntax->datum varlist)) |
| 34 | + (loop body vars)] |
| 35 | + [#`(let #,args ...) |
| 36 | + (error! stx "Invalid `let` expression with ~a arguments (expects 2)" (length args)) |
| 37 | + (unless (null? args) (loop (last args) vars))] |
| 38 | + [#`(if #,cond #,ift #,iff) |
| 39 | + (loop cond vars) |
| 40 | + (loop ift vars) |
| 41 | + (loop iff vars)] |
| 42 | + [#`(if #,args ...) |
| 43 | + (error! stx "Invalid `if` expression with ~a arguments (expects 3)" (length args)) |
| 44 | + (unless (null? args) (loop (last args) vars))] |
| 45 | + [#`(! #,props ... #,body) |
| 46 | + (check-properties* props '() body deprecated-ops) |
| 47 | + (loop body vars)] |
| 48 | + [#`(,(? (curry set-member? '(+ - * / and or = != < > <= >=))) #,args ...) |
| 49 | + ;; These expand by associativity so we don't check the number of arguments |
| 50 | + (for ([arg args]) (loop arg vars))] |
| 51 | + [#`(#,f-syntax #,args ...) |
| 52 | + (define f (syntax->datum f-syntax)) |
| 53 | + (cond |
| 54 | + [(operator-exists? f) |
52 | 55 | (define arity (length (real-operator-info f 'itype))) |
53 | 56 | (unless (= arity (length args)) |
54 | 57 | (error! stx "Operator ~a given ~a arguments (expects ~a)" |
55 | | - f (length args) arity))] |
56 | | - [(hash-has-key? (*functions*) f) |
| 58 | + f (length args) arity)) |
| 59 | + (when (operator-deprecated? f) |
| 60 | + (set-add! deprecated-ops f))] |
| 61 | + [(hash-has-key? (*functions*) f) |
57 | 62 | (match-define (list vars _ _) (hash-ref (*functions*) f)) |
58 | 63 | (unless (= (length vars) (length args)) |
59 | 64 | (error! stx "Function ~a given ~a arguments (expects ~a)" |
60 | 65 | f (length args) (length vars)))] |
61 | | - [else |
| 66 | + [else |
62 | 67 | (error! stx "Unknown operator ~a" f)]) |
63 | | - (for ([arg args]) (check-expression* arg vars error!))] |
64 | | - [_ (error! stx "Unknown syntax ~a" (syntax->datum stx))])) |
| 68 | + (for ([arg args]) (loop arg vars))] |
| 69 | + [_ (error! stx "Unknown syntax ~a" (syntax->datum stx))]))) |
65 | 70 |
|
66 | 71 | (define (check-property* prop error!) |
67 | 72 | (unless (identifier? prop) |
|
70 | 75 | (unless (equal? (substring name 0 1) ":") |
71 | 76 | (error! prop "Invalid property name ~a" prop))) |
72 | 77 |
|
73 | | -(define (check-properties* props vars error!) |
| 78 | +(define (check-properties* props vars error! deprecated-ops) |
74 | 79 | (define prop-dict |
75 | 80 | (let loop ([props props] [out '()]) |
76 | 81 | (match props |
|
108 | 113 | (error! cite "Invalid :cite ~a; must be a list" cite))) |
109 | 114 |
|
110 | 115 | (when (dict-has-key? prop-dict ':pre) |
111 | | - (check-expression* (dict-ref prop-dict ':pre) vars error!)) |
| 116 | + (check-expression* (dict-ref prop-dict ':pre) vars error! deprecated-ops)) |
112 | 117 |
|
113 | 118 | (when (dict-has-key? prop-dict ':herbie-target) |
114 | | - (check-expression* (dict-ref prop-dict ':herbie-target) vars error!)) |
| 119 | + (check-expression* (dict-ref prop-dict ':herbie-target) vars error! deprecated-ops)) |
115 | 120 |
|
116 | 121 | (when (dict-has-key? prop-dict ':herbie-conversions) |
117 | 122 | (define conversion-stx (dict-ref prop-dict ':herbie-conversions)) |
|
138 | 143 | (error! stx "Argument ~a is not a variable name" var)) |
139 | 144 | (when (check-duplicate-identifier vars*) |
140 | 145 | (error! stx "Duplicate argument name ~a" (check-duplicate-identifier vars*)))) |
141 | | - (check-properties* props (immutable-bound-id-set vars*) error!) |
142 | | - (check-expression* body (immutable-bound-id-set vars*) error!)) |
| 146 | + (define deprecated-ops (mutable-set)) |
| 147 | + (check-properties* props (immutable-bound-id-set vars*) error! deprecated-ops) |
| 148 | + (check-expression* body (immutable-bound-id-set vars*) error! deprecated-ops) |
| 149 | + (for ([op (in-set deprecated-ops)]) |
| 150 | + (define message |
| 151 | + (format (syntax->error-format-string stx) |
| 152 | + (format "operator `~a` is deprecated." op))) |
| 153 | + (warn 'deprecated #:url "faq.html#deprecated-ops" message))) |
143 | 154 |
|
144 | 155 | (define (check-fpcore* stx error!) |
145 | 156 | (match stx |
|
154 | 165 | [_ |
155 | 166 | (error! stx "Not an FPCore: ~a" stx)])) |
156 | 167 |
|
157 | | -(define (assert-expression! stx vars) |
158 | | - (define errs |
159 | | - (reap [sow] |
160 | | - (define (error! stx fmt . args) |
161 | | - (define args* (map (λ (x) (if (syntax? x) (syntax->datum x) x)) args)) |
162 | | - (sow (cons stx (apply format fmt args*)))) |
163 | | - (check-expression* stx vars error!))) |
164 | | - (unless (null? errs) |
165 | | - (raise-herbie-syntax-error "Invalid expression" #:locations errs))) |
166 | | - |
167 | 168 | (define (assert-program! stx) |
168 | 169 | (define errs |
169 | 170 | (reap [sow] |
|
172 | 173 | (sow (cons stx (apply format fmt args*)))) |
173 | 174 | (check-fpcore* stx error!))) |
174 | 175 | (unless (null? errs) |
175 | | - (raise-herbie-syntax-error "Invalid program" #:locations errs))) |
| 176 | + (raise-herbie-syntax-error "Invalid program" #:locations errs)) |
| 177 | + (print-warnings)) |
176 | 178 |
|
177 | 179 | ;; testing FPCore format |
178 | 180 | (module+ test |
|
0 commit comments