Add support for &rest match for non-list sequences (like . for improper lists)

master
Matus Goljer 12 years ago
parent 8f9fc4113d
commit 947ffdaa1a
  1. 5
      README.md
  2. 60
      dash.el
  3. 7
      dev/examples.el

@ -1763,6 +1763,11 @@ Vectors:
If the `pattern` is longer than `source`, an `error` is
thrown.
[a1 a2 a3 ... &rest rest] ) - as above, but bind the rest of
the sequence to `rest`. This is
conceptually the same as improper list
matching (a1 a2 ... aN . rest)
Key/value stores:
(&plist key0 a0 ... keyN aN) - bind value mapped by keyK in the

@ -1208,9 +1208,17 @@ SOURCE is a proper or improper list."
(t
(list (list match-form s)))))
;; TODO: add support to match the "rest" of the sequence, so that we
;; can break apart strings for example
;; (-let (([h &rest tail] "fobar")) (list h tail)) => (102 "obar")
(defun dash--vector-tail (seq start)
"Return the tail of SEQ starting at START."
(cond
((vectorp seq)
(let* ((re-length (- (length seq) start))
(re (make-vector re-length 0)))
(--dotimes re-length (aset re it (aref seq (+ it start))))
re))
((stringp seq)
(substring seq start))))
(defun dash--match-vector (match-form source)
"Setup a vector matching environment and call the real matcher."
(let ((s (make-symbol "--dash-source--")))
@ -1223,22 +1231,41 @@ MATCH-FORM is a vector. Each element of MATCH-FORM is either a
symbol, which gets bound to the respective value in source or
another match form which gets destructured recursively.
If second-from-last place in MATCH-FORM is the symbol &rest, the
next element of the MATCH-FORM is matched against the tail of
SOURCE, starting at index of the &rest symbol. This is
conceptually the same as the (head . tail) match for improper
lists, where dot plays the role of &rest.
SOURCE is a vector.
If the MATCH-FORM vector is shorter than SOURCE vector, only
the (length MATCH-FORM) places are bound, the rest of the SOURCE
is discarded."
(let ((i 0))
(-flatten-n 1 (--map
(let ((m (aref match-form i)))
(prog1 (cond
((and (symbolp m)
;; do not match symbols starting with _
(not (eq (aref (symbol-name m) 0) ?_)))
(list (list m `(aref ,source ,i))))
(t (dash--match m `(aref ,source ,i))))
(setq i (1+ i))))
match-form))))
(let ((i 0)
(l (length match-form))
(re))
(while (< i l)
(let ((m (aref match-form i)))
(push (cond
((and (symbolp m)
(eq m '&rest))
;; the reversing here is necessary, because we reverse
;; `re' in the end. That would then incorrectly
;; reorder sub-expression matches
(prog1 (nreverse
(dash--match
(aref match-form (1+ i))
`(dash--vector-tail ,source ,i)))
(setq i l)))
((and (symbolp m)
;; do not match symbols starting with _
(not (eq (aref (symbol-name m) 0) ?_)))
(list (list m `(aref ,source ,i))))
(t (nreverse (dash--match m `(aref ,source ,i)))))
re)
(setq i (1+ i))))
(nreverse (-flatten-n 1 re))))
(defun dash--match-kv (match-form source)
"Setup a kv matching environment and call the real matcher.
@ -1381,6 +1408,11 @@ Vectors:
If the PATTERN is longer than SOURCE, an `error' is
thrown.
[a1 a2 a3 ... &rest rest] ) - as above, but bind the rest of
the sequence to REST. This is
conceptually the same as improper list
matching (a1 a2 ... aN . rest)
Key/value stores:
(&plist key0 a0 ... keyN aN) - bind value mapped by keyK in the

@ -720,14 +720,19 @@ new list."
(list bar face inv)) => '(2 foo-face t)
(-let [(a (b c) d) (list 1 (list 2 3) 4 5 6)] (list a b c d)) => '(1 2 3 4)
(-let [[a _ c] [1 2 3 4]] (list a c)) => '(1 3)
(-let [[a (b c) d] [1 (2 3) 4]] (list a b c d)) => '(1 2 3 4)
(-let [[a b c] (string ?f ?o ?b ?a ?r)] (list a b c)) => '(?f ?o ?b)
(-let [[a b c] "abcdef"] (list a b c)) => '(?a ?b ?c)
(-let [[a (b [c]) d] [1 (2 [3 4]) 5 6]] (list a b c d)) => '(1 2 3 5)
(-let [(a b c d) (list 1 2 3 4 5 6)] (list a b c d)) => '(1 2 3 4)
;; d is bound to nil. I don't think we want to error in such a case.
;; After all (car nil) => nil
(-let [(a b c d) (list 1 2 3)] (list a b c d)) => '(1 2 3 nil)
(-let [[a b c] [1 2 3 4]] (list a b c)) => '(1 2 3)
;; here we error, because "vectors" are rigit, immutable structures,
(-let [[a b &rest c] "abcdef"] (list a b c)) => '(?a ?b "cdef")
(-let [[a b &rest c] [1 2 3 4 5 6]] (list a b c)) => '(1 2 [3 4 5 6])
(-let [[a b &rest [c d]] [1 2 3 4 5 6]] (list a b c d)) => '(1 2 3 4)
;; here we error, because "vectors" are rigid, immutable structures,
;; so we should know how many elements there are
(condition-case nil
(-let [[a b c d] [1 2 3]]

Loading…
Cancel
Save