|
84 | 84 | [(list arg ...) (map loop arg)] |
85 | 85 | [_ ctx]))) |
86 | 86 |
|
87 | | -(define/contract (create-operator-impl! name |
88 | | - ctx |
89 | | - spec |
90 | | - #:impl [fl-proc #f] |
91 | | - #:fpcore [fpcore #f] |
92 | | - #:cost [cost #f]) |
93 | | - (->* (symbol? context? any/c) |
94 | | - (#:impl (or/c procedure? generator? #f) #:fpcore any/c #:cost (or/c #f real? procedure?)) |
95 | | - operator-impl?) |
96 | | - ;; check specification |
97 | | - (check-spec! name ctx spec) |
98 | | - ;; synthesize operator (if the spec contains exactly one operator) |
99 | | - (define op |
100 | | - (match spec |
101 | | - [(list op (or (? number?) (? symbol?)) ...) op] |
102 | | - [_ #f])) |
103 | | - ;; check FPCore translation |
104 | | - (match (fpcore-parameterize (or fpcore spec)) |
| 87 | +(define (check-fpcore! name fpcore) |
| 88 | + (match (fpcore-parameterize fpcore) |
105 | 89 | [`(! ,props ... (,op ,args ...)) |
106 | 90 | (unless (even? (length props)) |
107 | | - (error 'create-operator-impl! "~a: umatched property in ~a" name fpcore)) |
| 91 | + (error 'define-operation "~a: unmatched property in ~a" name fpcore)) |
108 | 92 | (unless (symbol? op) |
109 | | - (error 'create-operator-impl! "~a: expected symbol `~a`" name op)) |
| 93 | + (error 'define-operation "~a: expected symbol `~a`" name op)) |
110 | 94 | (for ([arg (in-list args)] |
111 | 95 | #:unless (or (symbol? arg) (number? arg))) |
112 | | - (error 'create-operator-impl! "~a: expected terminal `~a`" name arg))] |
| 96 | + (error 'define-operation "~a: expected terminal `~a`" name arg))] |
113 | 97 | [`(,op ,args ...) |
114 | 98 | (unless (symbol? op) |
115 | | - (error 'create-operator-impl! "~a: expected symbol `~a`" name op)) |
| 99 | + (error 'define-operation "~a: expected symbol `~a`" name op)) |
116 | 100 | (for ([arg (in-list args)] |
117 | 101 | #:unless (or (symbol? arg) (number? arg))) |
118 | | - (error 'create-operator-impl! "~a: expected terminal `~a`" name arg))] |
| 102 | + (error 'define-operation "~a: expected terminal `~a`" name arg))] |
119 | 103 | [(? symbol?) (void)] |
120 | | - [_ (error 'create-operator-impl! "Invalid fpcore for ~a: ~a" name fpcore)]) |
121 | | - ;; check or synthesize floating-point operation |
| 104 | + [_ (error 'define-operation "Invalid fpcore for ~a: ~a" name fpcore)])) |
| 105 | + |
| 106 | +(define (check-fl-proc! name ctx fl-proc spec) |
122 | 107 | (define fl-proc* |
123 | 108 | (match fl-proc |
124 | 109 | [(? generator?) ((generator-gen fl-proc) spec ctx)] |
125 | | - [(? procedure?) fl-proc] |
126 | | - [#f (error 'create-operator-impl! "fl-proc is not provided for `~a` implementation" name)])) |
| 110 | + [(? procedure?) fl-proc])) |
127 | 111 | (unless (procedure-arity-includes? fl-proc* (length (context-vars ctx)) #t) |
128 | | - (error 'arity-check |
| 112 | + (error 'define-operation |
129 | 113 | "Procedure `~a` accepts ~a arguments, but ~a is provided" |
130 | 114 | name |
131 | 115 | (procedure-arity fl-proc*) |
132 | 116 | (length (context-vars ctx)))) |
133 | | - (define-values (cost* aggregate*) |
134 | | - (cond |
135 | | - [(number? cost) (values cost +)] |
136 | | - [(procedure? cost) (values 0 cost)] |
137 | | - [else (values cost +)])) |
138 | | - (operator-impl name ctx spec (fpcore-parameterize (or fpcore spec)) fl-proc* cost* aggregate*)) |
| 117 | + fl-proc*) |
| 118 | + |
| 119 | +(define (check-cost! name cost) |
| 120 | + (match cost |
| 121 | + [(? number?) (values cost +)] |
| 122 | + [(? procedure?) (values 0 cost)] |
| 123 | + [_ (error 'define-operation "Invalid cost for ~a: ~a" name cost)])) |
| 124 | + |
| 125 | +(define/contract (create-operator-impl! name |
| 126 | + ctx |
| 127 | + #:spec spec |
| 128 | + #:impl fl-proc |
| 129 | + #:fpcore fpcore |
| 130 | + #:cost cost) |
| 131 | + (-> symbol? |
| 132 | + context? |
| 133 | + #:spec any/c |
| 134 | + #:impl (or/c procedure? generator?) |
| 135 | + #:fpcore any/c |
| 136 | + #:cost (or/c real? procedure?) |
| 137 | + operator-impl?) |
| 138 | + (check-spec! name ctx spec) |
| 139 | + (check-fpcore! name fpcore) |
| 140 | + (define fl-proc* (check-fl-proc! name ctx fl-proc spec)) |
| 141 | + (define-values (cost* aggregate*) (check-cost! name cost)) |
| 142 | + (operator-impl name ctx spec (fpcore-parameterize fpcore) fl-proc* cost* aggregate*)) |
| 143 | + |
| 144 | +;; Generic keyword parser for syntax (at compile-time) |
| 145 | +(begin-for-syntax |
| 146 | + (define (parse-keyword-fields stx fields-stx allowed-keywords op-name) |
| 147 | + (define (oops! why [sub-stx #f]) |
| 148 | + (raise-syntax-error op-name why stx sub-stx)) |
| 149 | + |
| 150 | + (define result-hash (make-hasheq)) |
| 151 | + |
| 152 | + (let loop ([fields fields-stx]) |
| 153 | + (syntax-case fields () |
| 154 | + [() result-hash] |
| 155 | + [(kw val rest ...) |
| 156 | + (keyword? (syntax-e #'kw)) |
| 157 | + (let ([kw-sym (string->symbol (keyword->string (syntax-e #'kw)))]) |
| 158 | + (unless (member kw-sym allowed-keywords) |
| 159 | + (oops! (format "unknown keyword ~a" (syntax-e #'kw)) #'kw)) |
| 160 | + (when (hash-has-key? result-hash kw-sym) |
| 161 | + (oops! (format "multiple ~a clauses" (syntax-e #'kw)) #'kw)) |
| 162 | + (hash-set! result-hash kw-sym #'val) |
| 163 | + (loop #'(rest ...)))] |
| 164 | + [(kw) |
| 165 | + (keyword? (syntax-e #'kw)) |
| 166 | + (oops! (format "expected value after keyword ~a" (syntax-e #'kw)) #'kw)] |
| 167 | + [_ (oops! "bad syntax" fields)])))) |
139 | 168 |
|
140 | 169 | (define-syntax (make-operator-impl stx) |
141 | 170 | (define (oops! why [sub-stx #f]) |
142 | 171 | (raise-syntax-error 'make-operator-impl why stx sub-stx)) |
143 | 172 | (syntax-case stx (:) |
144 | | - [(_ (id [var : repr] ...) rtype fields ...) |
145 | | - (let ([id #'id] |
146 | | - [vars (syntax->list #'(var ...))] |
147 | | - [fields #'(fields ...)]) |
148 | | - (unless (identifier? id) |
149 | | - (oops! "expected identifier" id)) |
| 173 | + [(_ (id [var : repr] ...) rtype . fields) |
| 174 | + (let ([op-name #'id] |
| 175 | + [vars (syntax->list #'(var ...))]) |
| 176 | + (unless (identifier? op-name) |
| 177 | + (oops! "expected identifier" op-name)) |
150 | 178 | (for ([var (in-list vars)] |
151 | 179 | #:unless (identifier? var)) |
152 | 180 | (oops! "expected identifier" var)) |
153 | | - (define spec #f) |
154 | | - (define core #f) |
155 | | - (define fl-expr #f) |
156 | | - (define op-cost #f) |
157 | | - |
158 | | - (let loop ([fields fields]) |
159 | | - (syntax-case fields () |
160 | | - [() |
161 | | - (unless spec |
162 | | - (oops! "missing `#:spec` keyword")) |
163 | | - (with-syntax ([id id] |
164 | | - [spec spec] |
165 | | - [core core] |
166 | | - [fl-expr fl-expr] |
167 | | - [op-cost op-cost]) |
168 | | - #'(create-operator-impl! 'id |
169 | | - (context '(var ...) rtype (list repr ...)) |
170 | | - 'spec |
171 | | - #:impl fl-expr |
172 | | - #:fpcore 'core |
173 | | - #:cost op-cost))] |
174 | | - [(#:spec expr rest ...) |
175 | | - (cond |
176 | | - [spec (oops! "multiple #:spec clauses" stx)] |
177 | | - [else |
178 | | - (set! spec #'expr) |
179 | | - (loop #'(rest ...))])] |
180 | | - [(#:spec) (oops! "expected value after keyword `#:spec`" stx)] |
181 | | - [(#:fpcore expr rest ...) |
182 | | - (cond |
183 | | - [core (oops! "multiple #:fpcore clauses" stx)] |
184 | | - [else |
185 | | - (set! core #'expr) |
186 | | - (loop #'(rest ...))])] |
187 | | - [(#:fpcore) (oops! "expected value after keyword `#:fpcore`" stx)] |
188 | | - [(#:impl expr rest ...) |
189 | | - (cond |
190 | | - [fl-expr (oops! "multiple #:fl clauses" stx)] |
191 | | - [else |
192 | | - (set! fl-expr #'expr) |
193 | | - (loop #'(rest ...))])] |
194 | | - [(#:impl) (oops! "expected value after keyword `#:fl`" stx)] |
195 | | - [(#:cost cost rest ...) |
196 | | - (cond |
197 | | - [op-cost (oops! "multiple #:cost clauses" stx)] |
198 | | - [else |
199 | | - (set! op-cost #'cost) |
200 | | - (loop #'(rest ...))])] |
201 | | - [(#:cost) (oops! "expected value after keyword `#:cost`" stx)] |
202 | | - |
203 | | - ; bad |
204 | | - [_ (oops! "bad syntax" fields)])))] |
| 181 | + |
| 182 | + (define keywords (parse-keyword-fields stx #'fields '(spec fpcore impl cost) op-name)) |
| 183 | + |
| 184 | + (unless (hash-has-key? keywords 'spec) |
| 185 | + (raise-syntax-error op-name "missing `#:spec` keyword" stx)) |
| 186 | + (unless (hash-has-key? keywords 'impl) |
| 187 | + (raise-syntax-error op-name "missing `#:impl` keyword" stx)) |
| 188 | + (unless (hash-has-key? keywords 'cost) |
| 189 | + (raise-syntax-error op-name "missing `#:cost` keyword" stx)) |
| 190 | + |
| 191 | + ;; Build argument list for create-operator-impl! |
| 192 | + ;; Quote spec and fpcore, leave impl and cost unquoted |
| 193 | + ;; Default fpcore to spec if not provided |
| 194 | + (with-syntax ([spec-val (hash-ref keywords 'spec)] |
| 195 | + [fpcore-val (hash-ref keywords 'fpcore (hash-ref keywords 'spec))] |
| 196 | + [impl-val (hash-ref keywords 'impl)] |
| 197 | + [cost-val (hash-ref keywords 'cost)]) |
| 198 | + #'(create-operator-impl! 'id |
| 199 | + (context '(var ...) rtype (list repr ...)) |
| 200 | + #:spec 'spec-val |
| 201 | + #:impl impl-val |
| 202 | + #:fpcore 'fpcore-val |
| 203 | + #:cost cost-val)))] |
205 | 204 | [_ (oops! "bad syntax")])) |
206 | 205 |
|
207 | 206 | ;; Platform registration functions moved from platform.rkt |
|
0 commit comments