diff --git a/README.md b/README.md index 5b425ac..a2f3bf0 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/dash.el b/dash.el index 7efe2b7..de2122e 100644 --- a/dash.el +++ b/dash.el @@ -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 diff --git a/dev/examples.el b/dev/examples.el index d78bccc..55f3572 100644 --- a/dev/examples.el +++ b/dev/examples.el @@ -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]]