Skip to content

Commit 7153649

Browse files
committed
Rename pattern to match, replacing the old match implementation.
1 parent 0c41429 commit 7153649

File tree

6 files changed

+116
-341
lines changed

6 files changed

+116
-341
lines changed

src/active/clojure/pattern.clj renamed to src/active/clojure/match.clj

Lines changed: 114 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
(ns active.clojure.pattern
1+
(ns active.clojure.match
2+
"Syntactic sugar for map matching around `core.match`."
23
(:require [active.clojure.condition :as c]
34
[active.clojure.functions :as f]
45
[active.clojure.lens :as lens]
@@ -8,6 +9,10 @@
89
[clojure.core.match :as match]
910
[clojure.core.match.regex]))
1011

12+
(defmethod match/to-source ::match/regex
13+
[pat ocr]
14+
`(and (not= ~ocr ::match/not-found) (re-matches ~(:regex pat) ~ocr)))
15+
1116
(define-record-type Pattern
1217
(make-pattern name clauses) pattern?
1318
[name pattern-name
@@ -521,7 +526,105 @@
521526
~rhs)))
522527

523528
(defmacro map-matcher
529+
"Construct a map matcher. Syntactic sugar for `core.match`.
530+
531+
`map-matcher´ accepts two kinds of inputs:
532+
533+
1. A sequence of alternating patterns and consequents (see below).
534+
2. A sequence of alternating Pattern objects and consequents.
535+
536+
The syntax is `(map-matcher <pattern> <consequent> ... :else <alternative>)` where
537+
`<pattern>` is a vector of clauses `[<clause>+]` where `clause` is one of the following:
538+
539+
- `(<key> <value> :as <name>)` which requires the key `<key>` to be
540+
mapped to `<value>` in the map and binds `<name>` to `<value>`.
541+
542+
- `(<key-and-name> <value>)` which requires the key `<key-and-name>`
543+
to be mapped to `<value>` in the map and binds `<key-and-name>` to
544+
`<value>`.
545+
546+
- `(<key> :as <name>)` which requires `<key>` to be present in the map
547+
and binds `<name>` to its value.
548+
549+
- `<key-and-name>` which requires `<key-and-name>` to be present in
550+
the map and binds `<key-and-name>` to its value
551+
552+
The map matcher also supports optional keys:
553+
554+
- `(? <key> <default> :as <name>)` binds `<name>` to to the value of
555+
`<key>` in the map or to `<default>` if `<key>` is not in the map.
556+
557+
- `(? <key-and-name> <default>)` binds `<key-and-name>` to the value of
558+
`<key-and-name>` in the map or to `<default>` if `<key-and-name>` is not
559+
in the map.
560+
561+
- `(? <key> :as <name>)` binds `<name>` to to the value of `<key>`
562+
in the map or to `nil` if `<key>` is not in the map.
563+
564+
- `(? <key-and-name>)` binds `<key-and-name>` to the value of
565+
`<key-and-name>` in the map or to `nil` if `<key-and-name>` is not
566+
in the map.
567+
568+
Access to nested values is also possible. Use `[<key>+]` to access
569+
a nested value, where `[<key>+]` is a sequence of keys. When no
570+
`:as <name>` clause is given, the last `<key>` of the sequence of
571+
keys is used as a name to bind the value.
572+
573+
`<key>` and `<key-and-name>` can be either a symbol or a keyword.
574+
If `<key-and-name>` is a symbol, it is converted to a string when
575+
used as a key (and used as symbol for binding the value). If
576+
`<key-and-name>` is a keyword, it is converted to a name for binding
577+
the value (and usesd as keyword when used as a key).
578+
579+
`<value>` can be:
580+
581+
- any value, regular expressions are also possible (only in Clojure, though,
582+
`core.match` does not support regex matching in ClojureScript).
583+
584+
- a list of alternative values in the form of: `(:or <value> <value>*)`.
585+
586+
- a custom compare function in the form of:
587+
`(:compare-fn <compare-fn>)` where `<compare-fn>` accepts the value that
588+
is mapped to `<key>` or `<key-and-name>`.
589+
590+
`<value>` also can be a list of alternative values in the form of:
591+
`(:or <value> <value>*)`.
592+
593+
`map-matcher` returns a function that accepts a map and evaluates
594+
`<consequent>` with all the `<name>`s bound when the message matches
595+
the given `<clause>`s, otherwise it evaluates `<alternative>`. or
596+
throws `IllegalArgumentException` if `<clause>` matches and no
597+
`<alternative>` is given.
598+
599+
Example:
600+
601+
(def example-map-matcher
602+
(map-matcher
603+
[(:x \"x\" :as x)
604+
(:y \"y\")
605+
(:z :as z)
606+
:w]
607+
(println x y z w)
608+
[(:a \"a\" :as a)
609+
(:b \"b\")
610+
(:c :as c)
611+
([:d Z] 42 :as Z)
612+
([:d Y] :as Y)
613+
([:d X] 65)
614+
[:d W foo]]
615+
(println a b c Z Y X foo)
616+
:else false))
617+
618+
(example-map-matcher {:a \"a\" :b \"b\" :c \"c\"
619+
:d {\"Z\" 42 \"Y\" 23 \"X\" 65
620+
\"W\" {\"foo\" \"bar\"}}})
621+
622+
prints
623+
624+
\"a b c d 42 23 65 bar\""
524625
[& args]
626+
(when-not (even? (count args))
627+
(throw (IllegalArgumentException. (str "expecting an even number of arguments " *ns* " " (meta &form)))))
525628
(let [message `message#
526629
patterns+consequents (reduce
527630
(fn [code [lhs* rhs]]
@@ -544,6 +647,16 @@
544647
[binding pattern]
545648
`(def ~binding ~(parse-pattern (gensym) pattern)))
546649

650+
(defmacro matcher
651+
[& args]
652+
(let [event `event#]
653+
`(fn [~event]
654+
((map-matcher ~@args) ~event))))
655+
656+
(defmacro match
657+
[event & args]
658+
`((matcher ~@args) ~event))
659+
547660
(define-record-type Dependency
548661
(make-dependency path matcher for-pattern) dependency?
549662
[^{:doc "The path that this [[Dependency]] has a restriction on."}

src/active/clojure/match.cljc

Lines changed: 0 additions & 207 deletions
This file was deleted.

test/active/clojure/pattern_test.clj renamed to test/active/clojure/match_test.clj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
(ns active.clojure.pattern-test
2-
(:require [active.clojure.pattern :as p]
1+
(ns active.clojure.match-test
2+
(:require [active.clojure.match :as p]
33
[active.clojure.functions :as f]
44
[clojure.core.match.regex]
55
[clojure.test :as t]))

0 commit comments

Comments
 (0)