|
4 | 4 |
|
5 | 5 | (ns noahtheduke.splint.pattern |
6 | 6 | (:require |
| 7 | + [clojure.string :as str] |
7 | 8 | [noahtheduke.splint.clojure-ext.core :refer [postwalk* vary-meta*]] |
8 | 9 | [noahtheduke.splint.utils :refer [drop-quote simple-type]])) |
9 | 10 |
|
|
71 | 72 | `(throw (ex-info "default" {:type ~(read-dispatch ctx pattern form) |
72 | 73 | :pattern '~pattern}))) |
73 | 74 |
|
| 75 | +(defmethod read-form :?* |
| 76 | + read-form--?* |
| 77 | + [_ctx _pattern _form] |
| 78 | + (throw (IllegalArgumentException. "`:?*` must be used in a surrounding sequence"))) |
| 79 | + |
| 80 | +(defmethod read-form :?+ |
| 81 | + read-form--?* |
| 82 | + [_ctx _pattern _form] |
| 83 | + (throw (IllegalArgumentException. "`:?+` must be used in a surrounding sequence"))) |
| 84 | + |
| 85 | +(defmethod read-form :?? |
| 86 | + read-form--?* |
| 87 | + [_ctx _pattern _form] |
| 88 | + (throw (IllegalArgumentException. "`:??` must be used in a surrounding sequence"))) |
| 89 | + |
| 90 | +(defmethod read-form :?| |
| 91 | + read-form--?* |
| 92 | + [_ctx _pattern _form] |
| 93 | + (throw (IllegalArgumentException. "`:?|` must be used in a surrounding sequence"))) |
| 94 | + |
74 | 95 | (defmethod read-form :any |
75 | 96 | read-form--any |
76 | 97 | [ctx _pattern _form] |
|
137 | 158 | `(if (#{'~'_ '~'?_} '~bind) |
138 | 159 | ~ctx |
139 | 160 | (let [~children-form ~form] |
140 | | - (if-let [existing# ^clojure.lang.MapEntry (find ~ctx '~bind)] |
| 161 | + (if-let [^clojure.lang.MapEntry existing# (find ~ctx '~bind)] |
141 | 162 | (when (= (val existing#) ~children-form) |
142 | 163 | ~ctx) |
143 | 164 | (assoc ~ctx '~bind ~children-form)))))) |
144 | 165 |
|
145 | 166 | (defn match-pred |
146 | 167 | [ctx bind form pred] |
147 | | - (let [pred-name (name pred) |
148 | | - pred (or (requiring-resolve (symbol (or (namespace pred) (str *ns*)) pred-name)) |
149 | | - (resolve (symbol "clojure.core" pred-name)) |
150 | | - (requiring-resolve (symbol "noahtheduke.splint.rules.helpers" pred-name))) |
151 | | - children-form (gensym "pred-form-")] |
| 168 | + (let [children-form (gensym "pred-form-")] |
152 | 169 | `(let [~children-form ~form] |
153 | 170 | (when (~pred ~children-form) |
154 | 171 | ~(match-binding ctx bind children-form))))) |
155 | 172 |
|
| 173 | +(defn coerce-bind |
| 174 | + [bind] |
| 175 | + (if (str/starts-with? (str bind) "?") |
| 176 | + bind |
| 177 | + (symbol (str "?" bind)))) |
| 178 | + |
156 | 179 | (defmethod read-form :? |
157 | 180 | read-form--? |
158 | 181 | [ctx pattern form] |
159 | | - (let [[_?sym bind & [pred]] pattern] |
| 182 | + (let [pattern (if (symbol? pattern) ['? pattern] pattern) |
| 183 | + [_?sym ?bind pred] pattern |
| 184 | + bind (coerce-bind ?bind)] |
160 | 185 | (cond |
| 186 | + (not (#{2 3} (count pattern))) |
| 187 | + (throw (IllegalArgumentException. "? only accepts 1 or 2 arguments")) |
| 188 | + (and (= 3 (count pattern)) (not (symbol? pred))) |
| 189 | + (throw (IllegalArgumentException. "? pred must be a symbol")) |
161 | 190 | (nil? pred) |
162 | | - (match-binding ctx (symbol (str "?" bind)) form) |
| 191 | + (match-binding ctx bind form) |
163 | 192 | (symbol? pred) |
164 | | - (match-pred ctx (symbol (str "?" bind)) form pred) |
| 193 | + (match-pred ctx bind form pred) |
165 | 194 | :else |
166 | 195 | (throw (ex-info "Predicate must be a symbol" {:pred pred}))))) |
167 | 196 |
|
|
170 | 199 | (let [[_?sym bind pred] pattern |
171 | 200 | body-form (gensym "star-rest-form-") |
172 | 201 | pred-check (if pred `(every? ~pred ~body-form) true)] |
| 202 | + (when-not (#{2 3} (count pattern)) |
| 203 | + (throw (IllegalArgumentException. "?* only accepts 1 or 2 arguments"))) |
| 204 | + (when (and (= 3 (count pattern)) (not (symbol? pred))) |
| 205 | + (throw (IllegalArgumentException. "?* pred must be a symbol"))) |
173 | 206 | [(gensym "star-rest-fn-") |
174 | 207 | `(fn [~ctx form# cont#] |
175 | 208 | (let [~body-form (vary-meta (vec form#) assoc ::rest true)] |
176 | 209 | (when ~pred-check |
177 | | - (let [~ctx ~(match-binding ctx (symbol (str "?" bind)) body-form)] |
| 210 | + (let [~ctx ~(match-binding ctx (coerce-bind bind) body-form)] |
178 | 211 | (cont# ~ctx nil)))))])) |
179 | 212 |
|
180 | 213 | (defn match-star |
181 | 214 | [ctx pattern] |
182 | 215 | (let [[_?sym bind pred] pattern |
183 | 216 | body-form (gensym "star-form-") |
184 | 217 | pred-check (if pred `(every? ~pred ~body-form) true)] |
| 218 | + (when-not (#{2 3} (count pattern)) |
| 219 | + (throw (IllegalArgumentException. "?* only accepts 1 or 2 arguments"))) |
| 220 | + (when (and (= 3 (count pattern)) (not (symbol? pred))) |
| 221 | + (throw (IllegalArgumentException. "?* pred must be a symbol"))) |
185 | 222 | [(gensym "star-fn-") |
186 | 223 | `(fn [~ctx form# cont#] |
187 | 224 | (let [max-len# (count form#)] |
188 | 225 | (loop [i# 0 |
189 | 226 | ~body-form (vary-meta (vec (take i# form#)) assoc ::rest true)] |
190 | 227 | (when (<= i# max-len#) |
191 | 228 | (or (and ~pred-check |
192 | | - (let [~ctx ~(match-binding ctx (symbol (str "?" bind)) body-form)] |
| 229 | + (let [~ctx ~(match-binding ctx (coerce-bind bind) body-form)] |
193 | 230 | (cont# ~ctx (drop i# form#)))) |
194 | 231 | (recur (inc i#) |
195 | 232 | (if (< (count ~body-form) max-len#) |
|
201 | 238 | (let [[_?sym bind pred] pattern |
202 | 239 | body-form (gensym "plus-rest-form-") |
203 | 240 | pred-check (if pred `(every? ~pred ~body-form) true)] |
| 241 | + (when-not (#{2 3} (count pattern)) |
| 242 | + (throw (IllegalArgumentException. "?+ only accepts 1 or 2 arguments"))) |
| 243 | + (when (and (= 3 (count pattern)) (not (symbol? pred))) |
| 244 | + (throw (IllegalArgumentException. "?+ pred must be a symbol"))) |
204 | 245 | [(gensym "plus-rest-fn-") |
205 | 246 | `(fn [~ctx form# cont#] |
206 | 247 | (when (seq form#) |
207 | 248 | (let [~body-form (vary-meta (vec form#) assoc ::rest true)] |
208 | 249 | (when ~pred-check |
209 | | - (let [~ctx ~(match-binding ctx (symbol (str "?" bind)) body-form)] |
| 250 | + (let [~ctx ~(match-binding ctx (coerce-bind bind) body-form)] |
210 | 251 | (cont# ~ctx nil))))))])) |
211 | 252 |
|
212 | 253 | (defn match-plus |
213 | 254 | [ctx pattern] |
214 | 255 | (let [[_?sym bind pred] pattern |
215 | 256 | body-form (gensym "plus-form-") |
216 | 257 | pred-check (if pred `(every? ~pred ~body-form) true)] |
| 258 | + (when-not (#{2 3} (count pattern)) |
| 259 | + (throw (IllegalArgumentException. "?+ only accepts 1 or 2 arguments"))) |
| 260 | + (when (and (= 3 (count pattern)) (not (symbol? pred))) |
| 261 | + (throw (IllegalArgumentException. "?+ pred must be a symbol"))) |
217 | 262 | [(gensym "plus-fn-") |
218 | 263 | `(fn [~ctx form# cont#] |
219 | 264 | (let [max-len# (count form#)] |
220 | 265 | (loop [i# 1 |
221 | 266 | ~body-form (vary-meta (vec (take i# form#)) assoc ::rest true)] |
222 | 267 | (when (<= i# max-len#) |
223 | 268 | (or (and ~pred-check |
224 | | - (let [~ctx ~(match-binding ctx (symbol (str "?" bind)) body-form)] |
| 269 | + (let [~ctx ~(match-binding ctx (coerce-bind bind) body-form)] |
225 | 270 | (cont# ~ctx (drop i# form#)))) |
226 | 271 | (recur (inc i#) |
227 | 272 | (if (< (count ~body-form) max-len#) |
|
243 | 288 | `(fn [~ctx form# cont#] |
244 | 289 | (let [~body-form (vary-meta () assoc ::rest true)] |
245 | 290 | (or (and ~pred-check |
246 | | - (let [ctx# ~(match-binding ctx (symbol (str "?" bind)) body-form)] |
| 291 | + (let [ctx# ~(match-binding ctx (coerce-bind bind) body-form)] |
247 | 292 | (cont# ctx# form#))) |
248 | 293 | (when (seq form#) |
249 | 294 | (let [~body-form (vary-meta (vec (take 1 form#)) assoc ::rest true)] |
250 | 295 | (when ~pred-check |
251 | | - (let [ctx# ~(match-binding ctx (symbol (str "?" bind)) body-form)] |
| 296 | + (let [ctx# ~(match-binding ctx (coerce-bind bind) body-form)] |
252 | 297 | (cont# ctx# (drop 1 form#)))))))))])) |
253 | 298 |
|
254 | 299 | (defn match-alt |
255 | 300 | [ctx pattern] |
256 | 301 | (let [[_?sym bind alts] pattern] |
| 302 | + (when-not (= 3 (count pattern)) |
| 303 | + (throw (IllegalArgumentException. "?| only accepts 2 arguments"))) |
257 | 304 | (when-not (and (vector? alts) |
| 305 | + (seq alts) |
258 | 306 | (every? #(literal? (read-dispatch %)) alts)) |
259 | 307 | (throw (IllegalArgumentException. "?| alts must be a vector of literals"))) |
260 | 308 | (let [temp-ctx (gensym "temp-ctx-") |
261 | 309 | body-form (gensym "alt-body-form-") |
262 | 310 | binds [temp-ctx |
263 | 311 | `(let [~body-form (first ~body-form)] |
264 | 312 | (when ((quote ~(set alts)) ~body-form) |
265 | | - ~(match-binding ctx (symbol (str "?" bind)) body-form))) |
| 313 | + ~(match-binding ctx (coerce-bind bind) body-form))) |
266 | 314 | body-form |
267 | 315 | `(if ~temp-ctx |
268 | 316 | (next ~body-form) |
|
279 | 327 | [ctx children-form items] |
280 | 328 | (mapcat |
281 | 329 | (fn [item] |
282 | | - [ctx `(when ~ctx |
283 | | - (when (seq ~children-form) |
284 | | - ~(read-form ctx item `(first ~children-form)))) |
| 330 | + [ctx `(when (and ~ctx (seq ~children-form)) |
| 331 | + ~(read-form ctx item `(first ~children-form))) |
285 | 332 | children-form `(when ~ctx |
286 | 333 | (next ~children-form))]) |
287 | 334 | items)) |
|
345 | 392 | (mapcat #(match-optional ctx %) patterns) |
346 | 393 | :?| |
347 | 394 | (mapcat #(match-alt ctx %) patterns) |
348 | | - ; else |
| 395 | + ; else |
349 | 396 | (match-single ctx patterns))))) |
350 | 397 | (mapcat identity)) |
351 | 398 | fn-names (take-nth 2 fns) |
|
384 | 431 | (defn non-coll? |
385 | 432 | "Is a given simple-type a non-collection?" |
386 | 433 | [t] |
387 | | - (case t |
388 | | - (:nil :boolean :char :number :keyword :string :symbol) true |
389 | | - false)) |
| 434 | + (#{:nil :boolean :char :number :keyword :string :symbol} t)) |
390 | 435 |
|
391 | 436 | (defmethod read-form :map |
392 | 437 | read-form--map |
|
467 | 512 | (case special-type |
468 | 513 | (:symbol :any) obj |
469 | 514 | :? (let [sym-name (str obj)] |
470 | | - (cond |
471 | | - (= 1 (count sym-name)) obj |
472 | | - ;; If given `?_`, short-circuit to just _ |
473 | | - (.equals "?_" sym-name) '_ |
474 | | - :else |
475 | | - (list '? (symbol (subs sym-name 1))))) |
| 515 | + ;; If given `?_`, short-circuit to just _ |
| 516 | + (if (.equals "?_" sym-name) |
| 517 | + '_ |
| 518 | + obj)) |
476 | 519 | :?+ (let [sym-name (str obj)] |
477 | 520 | (if (= 2 (count sym-name)) |
478 | 521 | obj |
|
0 commit comments