; Copyright (c) Christophe Grand. All rights reserved. ; The use and distribution terms for this software are covered by the ; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) ; which can be found in the file epl-v10.html at the root of this distribution. ; By using this software in any fashion, you are agreeing to be bound by ; the terms of this license. ; You must not remove this notice, or any other, from this software. (ns net.cgrand.parsley.fold (:require [net.cgrand.parsley.util :as u]) (:refer-clojure :exclude [cat])) (defprotocol EphemeralFolding (unexpected! [this s]) (node! [this tag n]) (leaf! [this s]) (cat! [this pfq])) ; nodes is a collection of nodes (returned by make-node) ; pending is an alternate collection of tag, coll of nodes, integer (deftype FoldingQueue [^java.util.ArrayList pending ^java.util.ArrayList nodes ^java.util.ArrayList offsets make-node make-leaf make-unexpected] EphemeralFolding (unexpected! [this s] (when make-unexpected (.add nodes (make-unexpected s)) this)) (node! [this tag N] (let [n (.size offsets)] (u/cond (or (> N n) (neg? N)) (do (doto pending (.add tag) (.add (vec nodes)) (.add (- N n))) (.clear nodes) (.clear offsets)) :let [m (- n N) offset (.get offsets m) _ (-> offsets (.subList (inc m) n) .clear)] tag (let [tail (.subList nodes offset (.size nodes)) children (vec (.toArray tail))] (.clear tail) (.add nodes (make-node tag children)))) this)) (leaf! [this s] (let [leaf (if make-leaf (make-leaf s) s) offset (.size nodes)] (.add offsets offset) (.add nodes leaf) this)) (cat! [this pfq] (doseq [[tag pnodes n] (partition 3 (:pending pfq))] (.addAll nodes pnodes) (.node! this tag n)) (let [n (.size nodes)] (.addAll nodes (:nodes pfq)) (doseq [offset (:offsets pfq)] (.add offsets (+ n offset)))) this) clojure.lang.IDeref (deref [this] {:pending (vec pending) :nodes (vec nodes) :offsets (vec offsets) :make-node make-node :make-leaf make-leaf :make-unexpected make-unexpected})) (defn folding-queue [{:keys [pending nodes offsets make-node make-leaf make-unexpected] :or {pending [] nodes [] offsets []}}] (FoldingQueue. (java.util.ArrayList. ^java.util.Collection pending) (java.util.ArrayList. ^java.util.Collection nodes) (java.util.ArrayList. ^java.util.Collection offsets) make-node make-leaf make-unexpected)) (defn cat [a b] @(cat! (folding-queue a) b)) (defn finish [pfq] (u/cond :let [{:keys [pending nodes offsets make-node]} pfq] (and (seq nodes) (seq pending)) nil [[x & xs] (seq nodes)] (when-not xs x) [[[tag pnodes n] & xs] (seq (partition 3 pending))] (when (and (not xs) (neg? n) tag) (make-node tag pnodes)))) (defn stitchability "Returns :full, or a number (the number of states on A stack which remains untouched) when rebasing is possible or nil." [a b] (u/cond :let [[a-end a-watermark a-events a-start] a [b-end b-watermark b-events b-start] b] (= a-end b-start) :full :let [[a-stack a-rem] a-end [b-stack b-rem] b-start] :when (= a-rem b-rem) :let [b-tail (subvec b-stack b-watermark) n (- (count a-stack) (count b-tail))] :when (not (neg? n)) (= b-tail (subvec a-stack n)) n)) (defn rebase [b a] (u/cond :when-let [st (stitchability a b)] (= :full st) b ; if it's not full, it's partial :let [[a-end] a [b-end b-watermark b-events] b [a-stack] a-end [b-stack b-rem] b-end b-tail (subvec b-stack b-watermark) a-stub (subvec a-stack 0 st)] [[(into (vec a-stub) b-tail) b-rem] st b-events a-end])) (defn stitch [a b] (when (and a b) (let [[a-end a-watermark a-events a-start] a [b-end b-watermark b-events b-start] b] [b-end (min a-watermark b-watermark) (cat a-events b-events) a-start])))