Skip to content

mksub return value should be the whole collection #8

@miner

Description

@miner

The return value from mksub is not the full matching collection. This caused problems for me especially when matching vectors of one item.

user=> (require '[squarepeg.core :as sp])
nil
user=> (def v1 (sp/mksub (sp/mklit 1)))

'user/v1

user=> (v1 [[1]] {} {} {})
{:i (), :b {}, :r 1, :s [1], :m {}}
; expected :r [1]

user=> (def v12 (sp/mkseq (sp/mksub (sp/mklit 1)) (sp/mksub (sp/mklit 2))))

'user/v12

user=> (v12 [[1] [2]] {} {} {})
{:i (), :b {}, :r [1 2], :s [1 2], :m {}}
; expected :r [[1] [2]]

I'm now using a modified version of mksub which returns the full matching collection and tries to maintain the seq/vector distinction of the input. Also, I wanted to match exact sequences so I added sp/end to the sub-rule so that it would reject any extra stuff. I take an optional predicate so that I can restrict my match to vector? or seq? in cases where that matters. You're welcome to use any or all of these changes if you like them.

(require '[squarepeg.core :as sp])
(defn mk-sequential
  ([rule] (mk-sequential sequential? rule))
  ([pred rule]
     (let [coll-rule (if rule (sp/mkseq rule sp/end) sp/end)]
       (fn [input bindings context memo]
         (if (and (seq input) (pred (first input)))
           (let [r (coll-rule (first input) bindings context memo)]
             (if (sp/success? r)
               ;; try to maintain seq/vector distinction of input, :s value is vector
               (let [res (if (seq? (first input)) (seq (:s r)) (:s r))]
                 (sp/succeed res [res] (rest input) (:b r) (:m r)))
               r))
           (sp/fail "Input not a seq." memo))))))

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions