|
87 | 87 |
|
88 | 88 | (def matcher? (some-fn constant-matcher? regex-matcher? existence-matcher? options-matcher? predicate-matcher?))
|
89 | 89 |
|
90 |
| -(defn matcher->value |
91 |
| - "Takes a `matcher` and returns the value/s it matches on. |
92 |
| - `::not-nil` symbolizes the existence matcher." |
93 |
| - [matcher] |
94 |
| - (fn [binding] |
95 |
| - (cond |
96 |
| - (constant-matcher? matcher) |
97 |
| - (constant-matcher-value matcher) |
98 |
| - |
99 |
| - (regex-matcher? matcher) |
100 |
| - (regex-matcher-regex matcher) |
101 |
| - |
102 |
| - (options-matcher? matcher) |
103 |
| - (cons :or (options-matcher-options matcher)) |
104 |
| - |
105 |
| - (existence-matcher? matcher) ; matches on everything. |
106 |
| - ::not-nil |
107 |
| - |
108 |
| - (predicate-matcher? matcher) |
109 |
| - (list binding :guard (predicate-matcher-predicate matcher)) |
110 |
| - |
111 |
| - :else |
112 |
| - (c/assertion-violation `matcher->value "not a matcher" matcher)))) |
113 |
| - |
114 | 90 | (defn matcher-default-value
|
115 | 91 | "Returns the default value of a matcher, if any."
|
116 | 92 | [matcher]
|
|
280 | 256 |
|
281 | 257 | (s/def ::regex regex?)
|
282 | 258 | (s/def ::compare-fn-token #{:compare-fn})
|
283 |
| -(s/def ::compare-fn (s/cat :compare-fn ::compare-fn-token :fn ifn?)) |
| 259 | +(s/def ::fn? #(or (ifn? %) (ifn? (eval %)))) |
| 260 | +(s/def ::compare-fn (s/cat :compare-fn ::compare-fn-token :fn ::fn?)) |
284 | 261 |
|
285 | 262 | (s/def ::or-token #{:or})
|
286 | 263 |
|
|
423 | 400 | (if (optional? mode) (opt clause) clause))))))))
|
424 | 401 |
|
425 | 402 | (defn parse-pattern
|
426 |
| - "Parse the argument to `defpattern` as a [[Pattern]]" |
427 |
| - [name p] |
428 |
| - (if (pattern? p) |
429 |
| - p |
430 |
| - (make-pattern name (mapv parse-clause p)))) |
| 403 | + "Parse the argument to `defpattern` as a [[Pattern]]. |
| 404 | + Optionally accepts a `name` (String) that names the pattern. If none is |
| 405 | + provided, automatically assigns a name." |
| 406 | + ([p] |
| 407 | + (parse-pattern (str "pattern-" (gensym)) p)) |
| 408 | + ([name p] |
| 409 | + (if (pattern? p) |
| 410 | + p |
| 411 | + (make-pattern name (mapv parse-clause p))))) |
431 | 412 |
|
432 | 413 | ;; Match
|
433 | 414 |
|
434 | 415 | (defn convert-path-element
|
435 | 416 | [path-element]
|
436 | 417 | (if (symbol? path-element) (str path-element) path-element))
|
437 | 418 |
|
438 |
| -(defn key-exists-clause->match |
439 |
| - [clause] |
440 |
| - (let [key (key-exists-clause-key clause) |
441 |
| - binding (key-exists-clause-binding clause)] |
442 |
| - ;; ignore the binding if it is the same as the key |
443 |
| - {(convert-path-element key) binding})) |
444 |
| - |
445 | 419 | (defn key-exists-clause->rhs-match
|
446 | 420 | [message clause]
|
447 | 421 | (let [key (key-exists-clause-key clause)
|
448 | 422 | binding (key-exists-clause-binding clause)]
|
449 | 423 | `[~binding (get-in ~message [~(convert-path-element key)])]))
|
450 | 424 |
|
451 |
| -(defn path-exists-clause->match |
452 |
| - [clause] |
453 |
| - (let [path (path-exists-clause-path clause) |
454 |
| - binding (path-exists-clause-binding clause)] |
455 |
| - (assoc-in {} (map convert-path-element path) binding))) |
456 |
| - |
457 | 425 | (defn path-exists-clause->rhs-match
|
458 | 426 | [message clause]
|
459 | 427 | (let [key (path-exists-clause-path clause)
|
460 | 428 | binding (path-exists-clause-binding clause)]
|
461 | 429 | `[~binding (get-in ~message ~(mapv convert-path-element key))]))
|
462 | 430 |
|
463 |
| -(defn key-matches-clause->lhs-match |
464 |
| - [clause] |
465 |
| - (let [key (key-matches-clause-key clause) |
466 |
| - match-value (matcher->value (key-matches-clause-matcher clause)) |
467 |
| - binding (key-matches-clause-binding clause)] |
468 |
| - {key (match-value binding)})) |
469 |
| - |
470 | 431 | (defn key-matches-clause->rhs-match
|
471 | 432 | [message clause]
|
472 | 433 | (let [key (key-matches-clause-key clause)
|
473 | 434 | match-value (matcher-default-value (key-matches-clause-matcher clause))
|
474 | 435 | binding (key-matches-clause-binding clause)]
|
475 | 436 | `[~binding (get-in ~message [~(convert-path-element key)] ~match-value)]))
|
476 | 437 |
|
477 |
| -(defn path-matches-clause->lhs-match |
478 |
| - [clause] |
479 |
| - (let [path (path-matches-clause-path clause) |
480 |
| - match-value (matcher->value (path-matches-clause-matcher clause)) |
481 |
| - binding (path-matches-clause-binding clause)] |
482 |
| - (assoc-in {} (map convert-path-element path) (match-value binding)))) |
483 |
| - |
484 | 438 | (defn path-matches-clause->rhs-match
|
485 | 439 | [message clause]
|
486 | 440 | (let [path (path-matches-clause-path clause)
|
487 | 441 | match-value (matcher-default-value (path-matches-clause-matcher clause))
|
488 | 442 | binding (path-matches-clause-binding clause)]
|
489 | 443 | `[~binding (get-in ~message ~(mapv convert-path-element path) ~match-value)]))
|
490 | 444 |
|
| 445 | +(defn matcher->value |
| 446 | + "Takes a `matcher` and returns the value/s it matches on. |
| 447 | + `::not-nil` symbolizes the existence matcher." |
| 448 | + [matcher] |
| 449 | + (fn [message path] |
| 450 | + (cond |
| 451 | + (constant-matcher? matcher) |
| 452 | + (constant-matcher-value matcher) |
| 453 | + |
| 454 | + (regex-matcher? matcher) |
| 455 | + (regex-matcher-regex matcher) |
| 456 | + |
| 457 | + (options-matcher? matcher) |
| 458 | + (cons :or (options-matcher-options matcher)) |
| 459 | + |
| 460 | + (existence-matcher? matcher) ; matches on everything. |
| 461 | + ::not-nil |
| 462 | + |
| 463 | + (predicate-matcher? matcher) |
| 464 | + [`(constantly (~(predicate-matcher-predicate matcher) |
| 465 | + (get-in ~message ~path)))] |
| 466 | + |
| 467 | + :else |
| 468 | + (c/assertion-violation `matcher->value "not a matcher" matcher)))) |
| 469 | + |
| 470 | +(defn fold-path |
| 471 | + [path match] |
| 472 | + (let [path* (->> path |
| 473 | + (mapv convert-path-element) |
| 474 | + reverse)] |
| 475 | + (reduce (fn [m path-element] |
| 476 | + {path-element m}) |
| 477 | + {(first path*) match} |
| 478 | + (rest path*)))) |
| 479 | + |
| 480 | +(defn clause->lhs |
| 481 | + [message clause] |
| 482 | + (cond |
| 483 | + (key-exists-clause? clause) |
| 484 | + (let [key (key-exists-clause-key clause) |
| 485 | + binding (key-exists-clause-binding clause)] |
| 486 | + ;; ignore the binding if it is the same as the key |
| 487 | + {(convert-path-element key) binding}) |
| 488 | + |
| 489 | + (path-exists-clause? clause) |
| 490 | + (let [path (path-exists-clause-path clause) |
| 491 | + binding (path-exists-clause-binding clause)] |
| 492 | + (assoc-in {} (map convert-path-element path) binding)) |
| 493 | + |
| 494 | + (key-matches-clause? clause) |
| 495 | + (let [key (key-matches-clause-key clause) |
| 496 | + matcher (key-matches-clause-matcher clause) |
| 497 | + match-value (matcher->value matcher)] |
| 498 | + (if (predicate-matcher? matcher) |
| 499 | + `({~key ~'_} :guard ~(match-value message [key])) |
| 500 | + `{~key ~(match-value message [key])})) |
| 501 | + |
| 502 | + (path-matches-clause? clause) |
| 503 | + (let [path (path-matches-clause-path clause) |
| 504 | + matcher (path-matches-clause-matcher clause) |
| 505 | + match-value (matcher->value matcher)] |
| 506 | + (if (predicate-matcher? matcher) |
| 507 | + `(~(fold-path path '_) :guard ~(match-value message path)) |
| 508 | + (fold-path path (match-value message path)))) |
| 509 | + |
| 510 | + (optional-clause? clause) |
| 511 | + {})) |
| 512 | + |
491 | 513 | (defn deep-merge-with
|
492 | 514 | "Like merge-with, but merges maps recursively, applying the given fn
|
493 | 515 | only when there's a non-map at a particular level.
|
|
503 | 525 | maps))
|
504 | 526 |
|
505 | 527 | (defn pattern->lhs
|
506 |
| - [pattern] |
| 528 | + [message pattern] |
507 | 529 | (let [clauses (pattern-clauses pattern)]
|
508 |
| - (apply deep-merge-with merge |
509 |
| - (mapv (fn [clause] |
510 |
| - (cond |
511 |
| - (key-exists-clause? clause) (key-exists-clause->match clause) |
512 |
| - (path-exists-clause? clause) (path-exists-clause->match clause) |
513 |
| - (key-matches-clause? clause) (key-matches-clause->lhs-match clause) |
514 |
| - (path-matches-clause? clause) (path-matches-clause->lhs-match clause) |
515 |
| - (optional-clause? clause) {})) |
516 |
| - clauses)))) |
| 530 | + (->> pattern |
| 531 | + pattern-clauses |
| 532 | + (mapv (partial clause->lhs message)) |
| 533 | + (apply deep-merge-with merge)))) |
517 | 534 |
|
518 | 535 | (defn clause->rhs
|
519 | 536 | [message bindings clause]
|
|
653 | 670 | (parse-pattern (gensym) lhs)
|
654 | 671 | lhs)]
|
655 | 672 | (concat code
|
656 |
| - [(pattern->lhs pattern) |
| 673 | + [(pattern->lhs message pattern) |
657 | 674 | (pattern->rhs message pattern rhs)])))))
|
658 | 675 | nil
|
659 | 676 | (partition 2 args))]
|
|
663 | 680 |
|
664 | 681 | (defmacro defpattern
|
665 | 682 | [binding pattern]
|
666 |
| - `(def ~binding ~(parse-pattern (gensym) pattern))) |
| 683 | + `(def ~binding ~(parse-pattern binding pattern))) |
667 | 684 |
|
668 | 685 | (defmacro matcher
|
669 | 686 | [& args]
|
|
0 commit comments