emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[ELPA-diffs] /srv/bzr/emacs/elpa r206: Add trie.el


From: Toby S. Cubitt
Subject: [ELPA-diffs] /srv/bzr/emacs/elpa r206: Add trie.el
Date: Sun, 29 Apr 2012 13:45:19 +0200
User-agent: Bazaar (2.3.1)

------------------------------------------------------------
revno: 206
committer: Toby S. Cubitt <address@hidden>
branch nick: elpa
timestamp: Sun 2012-04-29 13:45:19 +0200
message:
  Add trie.el
added:
  packages/trie/
  packages/trie/trie.el
=== added directory 'packages/trie'
=== added file 'packages/trie/trie.el'
--- a/packages/trie/trie.el     1970-01-01 00:00:00 +0000
+++ b/packages/trie/trie.el     2012-04-29 11:45:19 +0000
@@ -0,0 +1,1951 @@
+;;; trie.el --- trie package
+
+
+;; Copyright (C) 2008-2010, 2012  Free Software Foundation, Inc
+
+;; Author: Toby Cubitt <address@hidden>
+;; Version: 0.2.5
+;; Keywords: extensions, matching, data structures
+;;           trie, ternary search tree, tree, completion, regexp
+;; Package-Requires: ((emacs "24.1") (tNFA "0.1.1") (heap "0.3"))
+;; URL: http://www.dr-qubit.org/emacs.php
+;; Repository: http://www.dr-qubit.org/git/predictive.git
+
+;; This file is part of Emacs.
+;;
+;; GNU Emacs is free software: you can redistribute it and/or modify it under
+;; the terms of the GNU General Public License as published by the Free
+;; Software Foundation, either version 3 of the License, or (at your option)
+;; any later version.
+;;
+;; GNU Emacs is distributed in the hope that it will be useful, but WITHOUT
+;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+;; FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+;; more details.
+;;
+;; You should have received a copy of the GNU General Public License along
+;; with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
+
+
+;;; Commentary:
+;;
+;; Quick Overview
+;; --------------
+;; A trie is a data structure used to store keys that are ordered sequences of
+;; elements (vectors, lists or strings in Elisp; strings are by far the most
+;; common), in such a way that both storage and retrieval are space- and
+;; time-efficient. But, more importantly, a variety of more advanced queries
+;; can also be performed efficiently: for example, returning all strings with
+;; a given prefix, searching for keys matching a given wildcard pattern or
+;; regular expression, or searching for all keys that match any of the above
+;; to within a given Lewenstein distance (though this last is not yet
+;; implemented in this package - code contributions welcome!).
+;;
+;; You create a trie using `make-trie', create an association using
+;; `trie-insert', retrieve an association using `trie-lookup', and map over a
+;; trie using `trie-map', `trie-mapc', `trie-mapcar', or `trie-mapf'. You can
+;; find completions of a prefix sequence using `trie-complete', or search for
+;; keys matching a regular expression using `trie-regexp-search'. Using
+;; `trie-stack', you can create an object that allows the contents of the trie
+;; to be used like a stack, useful for building other algorithms on top of
+;; tries; `trie-stack-pop' pops elements off the stack one-by-one, in
+;; "lexical" order, whilst `trie-stack-push' pushes things onto the
+;; stack. Similarly, `trie-complete-stack', and `trie-regexp-stack' create
+;; "lexically-ordered" stacks of query results.
+;;
+;; Note that there are two uses for a trie: as a lookup table, in which case
+;; only the presence or absence of a key in the trie is significant, or as an
+;; associative array, in which case each key carries some associated
+;; data. Libraries for other data structure often only implement lookup
+;; tables, leaving it up to you to implement an associative array on top of
+;; this (by storing key+data pairs in the data structure's keys, then defining
+;; a comparison function that only compares the key part). For a trie,
+;; however, the underlying data structures naturally support associative
+;; arrays at no extra cost, so this package does the opposite: it implements
+;; associative arrays, and leaves it up to you to use them as lookup tables if
+;; you so desire.
+;;
+;;
+;; Different Types of Trie
+;; -----------------------
+;; There are numerous ways to implement trie data structures internally, each
+;; with its own time- and space-efficiency trade-offs. By viewing a trie as a
+;; tree whose nodes are themselves lookup tables for key elements, this
+;; package is able to support all types of trie in a uniform manner. This
+;; relies on there existing (or you writing!) an Elisp implementation of the
+;; corresponding type of lookup table. The best type of trie to use will
+;; depend on what trade-offs are appropriate for your particular
+;; application. The following gives an overview of the advantages and
+;; disadvantages of various types of trie. (Not all of the underlying lookup
+;; tables have been implemented in Elisp yet, so using some of the trie types
+;; described below would require writing the missing Elisp package!)
+;;
+;;
+;; One of the most effective all-round implementations of a trie is a ternary
+;; search tree, which can be viewed as a tree of binary trees. If basic binary
+;; search trees are used for the nodes of the trie, we get a standard ternary
+;; search tree. If self-balancing binary trees are used (e.g. AVL or red-black
+;; trees), we get a self-balancing ternary search tree. If splay trees are
+;; used, we get yet another self-organising variant of a ternary search
+;; tree. All ternary search trees have, in common, good space-efficiency. The
+;; time-efficiency of the various trie operations is also good, assuming the
+;; underlying binary trees are balanced. Under that assumption, all variants
+;; of ternary search trees described below have the same asymptotic
+;; time-complexity for all trie operations.
+;;
+;; Self-balancing trees ensure the underlying binary trees are always close to
+;; perfectly balanced, with the usual trade-offs between the different the
+;; types of self-balancing binary tree: AVL trees are slightly more efficient
+;; for lookup operations than red-black trees, at a cost of slightly less
+;; efficienct insertion operations, and less efficient deletion
+;; operations. Splay trees give good average-case complexity and are simpler
+;; to implement than AVL or red-black trees (which can mean they're faster in
+;; practice!), at the expense of poor worst-case complexity.
+;;
+;; If your tries are going to be static (i.e. created once and rarely
+;; modified), then using perfectly balanced binary search trees might be
+;; appropriate. Perfectly balancing the binary trees is very inefficient, but
+;; it only has to be when the trie is first created or modified. Lookup
+;; operations will then be as efficient as possible for ternary search trees,
+;; and the implementation will also be simpler (so probably faster) than a
+;; self-balancing tree, without the space and time overhead required to keep
+;; track of rebalancing.
+;;
+;; On the other hand, adding data to a binary search tree in a random order
+;; usually results in a reasonably balanced tree. If this is the likely
+;; scenario, using a basic binary tree without bothering to balance it at all
+;; might be quite efficient, and, being even simpler to implement, could be
+;; quite fast overall.
+;;
+;;
+;; A digital trie is a different implementation of a trie, which can be viewed
+;; as a tree of arrays, and has different space- and time-complexities than a
+;; ternary search tree. Roughly speaking, a digital trie has worse
+;; space-complexity, but better time-complexity. Using hash tables instead of
+;; arrays for the nodes gives something similar to a digital trie, potentially
+;; with better space-complexity and the same amortised time-complexity, but at
+;; the expense of occasional significant inefficiency when inserting and
+;; deleting (whenever a hash table has to be resized). Indeed, an array can be
+;; viewed as a perfect hash table, but as such it requires the number of
+;; possible values to be known in advance.
+;;
+;; Finally, if you really need optimal efficiency from your trie, you could
+;; even write a custom type of underlying lookup table, optimised for your
+;; specific needs.
+;;
+;; This package uses the AVL tree package avl-tree.el, the tagged NFA package
+;; tNFA.el, and the heap package heap.el.
+
+
+;;; Change Log:
+;;
+;; Version 0.2.5
+;; * removed `trie--avl-transform-for-print' and
+;;   `trie--avl-transform-from-read', since Emacs has supported printing and
+;;   reading circular data structures for a long time now, rendering these
+;;   transormers obsolete (note that `print-circle' *must* be enabled now when
+;;   printing an avl trie)
+;;
+;; Version 0.2.4
+;; * minor bug-fix to `trie--edebug-pretty-print' to print "nil" instead
+;;   of "()"
+;;
+;; Version 0.2.3
+;; * bug-fix in `trie--edebug-pretty-print'
+;;
+;; Version 0.2.2
+;; * added `edebug-prin1' and `edebug-prin1-to-string' advice to prevent
+;;   edebug hanging whilst printing large tries
+;;
+;; Version 0.2.1
+;; * bug-fix to result accumulation in `trie--do-regexp-search'
+;;
+;; Version 0.2
+;; * Replaced wildcard searches with regexp searches, using the tNFA.el tagged
+;;   non-deterministic finite state automata library. This is both more
+;;   general *and* more efficient.
+;; * bug fix in `trie--do-regexp-search'
+;;
+;; Version 0.1
+;; * Initial release (complete rewrite from scratch of tstree.el!)
+;; * Ternary search trees are now implemented as a tree of avl trees, which
+;;   has numerous advantages: self-balancing trees guarantee O(log n)
+;;   complexity regardless of how the tree is built; deletion is now done
+;;   properly.
+;; * Unlike tstree.el, trie.el is general enough to implement all sorts of
+;;   tries, not just ternary search trees (though these remain the default).
+;; * Up to "tstree"->"trie" renaming, many functions are drop-in replacements
+;;   for tstree.el functions. However, insertion and rank functions are no
+;;   longer stored in the data structure, so corresponidng arguments are no
+;;   longer optional. A single `trie-complete' function now deals with sorting
+;;   completions in both lexical or arbitrary order, the ranking function
+;;   being passed as an optional argument in the latter case. And functions
+;;   can no longer operate over multiple data structures at once; i.e. they no
+;;   longer accept lists of trees as arguments. (These features belong in
+;;   higher level libraries, and the efficiency loss is negligible.)
+;; * `trie-wildcard-search' implements efficient shell-glob-like wildcard
+;;   searches of tries!
+
+
+
+;;; Code:
+
+(eval-when-compile (require 'cl))
+(require 'avl-tree)
+(require 'heap)
+(require 'tNFA)
+
+
+
+;;; ================================================================
+;;;                   Pre-defined trie types
+
+(defconst trie--types '(avl))
+
+
+;; --- avl-tree ---
+(put 'avl :trie-createfun
+     (lambda (cmpfun seq) (avl-tree-create cmpfun)))
+(put 'avl :trie-insertfun 'avl-tree-enter)
+(put 'avl :trie-deletefun 'avl-tree-delete)
+(put 'avl :trie-lookupfun 'avl-tree-member)
+(put 'avl :trie-mapfun 'avl-tree-mapc)
+(put 'avl :trie-emptyfun 'avl-tree-empty)
+(put 'avl :trie-stack-createfun 'avl-tree-stack)
+(put 'avl :trie-stack-popfun 'avl-tree-stack-pop)
+(put 'avl :trie-stack-emptyfun 'avl-tree-stack-empty-p)
+
+
+
+;;; ================================================================
+;;;           Internal utility functions and macros
+
+;;; ----------------------------------------------------------------
+;;;           Functions and macros for handling a trie.
+
+;; symbol used to denote a trie leaf node
+(defconst trie--terminator '--trie--terminator)
+
+(defstruct
+  (trie-
+   :named
+   (:constructor nil)
+   (:constructor trie--create
+                (comparison-function &optional (type 'avl)
+                 &aux
+                 (dummy
+                  (or (memq type trie--types)
+                      (error "trie--create: unknown trie TYPE, %s" type)))
+                 (createfun (get type :trie-createfun))
+                 (insertfun (get type :trie-insertfun))
+                 (deletefun (get type :trie-deletefun))
+                 (lookupfun (get type :trie-lookupfun))
+                 (mapfun (get type :trie-mapfun))
+                 (emptyfun (get type :trie-emptyfun))
+                 (stack-createfun (get type :trie-stack-createfun))
+                 (stack-popfun (get type :trie-stack-popfun))
+                 (stack-emptyfun (get type :trie-stack-emptyfun))
+                 (transform-for-print (get type :trie-transform-for-print))
+                 (transform-from-read (get type :trie-transform-from-read))
+                 (cmpfun (trie--wrap-cmpfun comparison-function))
+                 (root (trie--node-create-root createfun cmpfun))
+                 ))
+   (:constructor trie--create-custom
+                (comparison-function
+                 &key
+                 (createfun 'avl-tree-create-bare)
+                 (insertfun 'avl-tree-enter)
+                 (deletefun 'avl-tree-delete)
+                 (lookupfun 'avl-tree-member)
+                 (mapfun 'avl-tree-mapc)
+                 (emptyfun 'avl-tree-empty)
+                 (stack-createfun 'avl-tree-stack)
+                 (stack-popfun 'avl-tree-stack-pop)
+                 (stack-emptyfun 'avl-tree-stack-empty-p)
+                 (transform-for-print nil)
+                 (transform-from-read nil)
+                 &aux
+                 (cmpfun (trie--wrap-cmpfun comparison-function))
+                 (root (trie--node-create-root createfun cmpfun))
+                 ))
+   (:copier nil))
+  root comparison-function cmpfun
+  createfun insertfun deletefun lookupfun mapfun emptyfun
+  stack-createfun stack-popfun stack-emptyfun
+  transform-for-print transform-from-read print-form)
+
+
+(defun trie--wrap-cmpfun (cmpfun)
+  ;; wrap CMPFUN for use in a subtree
+  `(lambda (a b)
+     (setq a (trie--node-split a)
+          b (trie--node-split b))
+     (cond ((eq a trie--terminator)
+           (if (eq b trie--terminator) nil t))
+          ((eq b trie--terminator) nil)
+          (t (,cmpfun a b)))))
+
+
+(defun trie--construct-equality-function (comparison-function)
+  ;; create equality function from trie comparison function
+  `(lambda (a b)
+     (and (not (,comparison-function a b))
+         (not (,comparison-function b a)))))
+
+
+
+;;; ----------------------------------------------------------------
+;;;          Functions and macros for handling a trie node.
+
+(defstruct
+  (trie--node
+   (:type vector)
+   (:constructor nil)
+   (:constructor trie--node-create
+                (split seq trie
+                 &aux (subtree (funcall (trie--createfun trie)
+                                        (trie--cmpfun trie) seq))))
+   (:constructor trie--node-create-data
+                (data &aux (split trie--terminator) (subtree data)))
+   (:constructor trie--node-create-dummy
+                (split &aux (subtree nil)))
+   (:constructor trie--node-create-root
+                (createfun cmpfun
+                 &aux
+                 (split nil)
+                 (subtree (funcall createfun cmpfun []))))
+   (:copier nil))
+   split subtree)
+
+;; data is stored in the subtree cell of a terminal node
+(defalias 'trie--node-data 'trie--node-subtree)
+
+(defsetf trie--node-data (node) `(setf (trie--node-subtree ,node)))
+
+(defmacro trie--node-data-p (node)
+  ;; Return t if NODE is a data node, nil otherwise.
+  `(eq (trie--node-split ,node) trie--terminator))
+
+(defmacro trie--node-p (node)
+  ;; Return t if NODE is a TRIE trie--node, nil otherwise.  Have to
+  ;; define this ourselves, because we created a defstruct without any
+  ;; identifying tags (i.e. (:type vector)) for efficiency, but this
+  ;; means we can only perform a rudimentary and very unreliable test.
+  `(and (vectorp ,node) (= (length ,node) 2)))
+
+
+(defun trie--node-find (node seq lookupfun)
+  ;; Returns the node below NODE corresponding to SEQ, or nil if none
+  ;; found.
+  (let ((len (length seq))
+       (i -1))
+    ;; descend trie until we find SEQ or run out of trie
+    (while (and node (< (incf i) len))
+      (setq node
+           (funcall lookupfun
+                    (trie--node-subtree node)
+                    (trie--node-create-dummy (elt seq i))
+                    nil)))
+    node))
+
+
+(defmacro trie--find-data-node (node lookupfun)
+  ;; Return data node from NODE's subtree, or nil if NODE has no data
+  ;; node in its subtree.
+  `(funcall ,lookupfun
+           (trie--node-subtree ,node)
+           (trie--node-create-dummy trie--terminator)
+           nil))
+
+
+(defmacro trie--find-data (node lookupfun)
+  ;; Return data associated with sequence corresponding to NODE, or nil
+  ;; if sequence has no associated data.
+  `(let ((node (trie--find-data-node ,node ,lookupfun)))
+     (when node (trie--node-data node))))
+
+
+
+;;; ----------------------------------------------------------------
+;;;              print/read transformation functions
+
+(defun trie-transform-for-print (trie)
+  "Transform TRIE to print form."
+  (when (trie--transform-for-print trie)
+    (if (trie--print-form trie)
+       (warn "Trie has already been transformed to print-form")
+      (funcall (trie--transform-for-print trie) trie)
+      (setf (trie--print-form trie) t))))
+
+
+(defun trie-transform-from-read (trie)
+  "Transform TRIE from print form."
+  (when (trie--transform-from-read trie)
+    (if (not (trie--print-form trie))
+       (warn "Trie is not in print-form")
+      (funcall (trie--transform-from-read trie) trie)
+      (setf (trie--print-form trie) nil))))
+
+
+(defmacro trie-transform-from-read-warn (trie)
+  "Transform TRIE from print form, with warning."
+  `(when (trie--print-form ,trie)
+     (warn (concat "Attempt to operate on trie in print-form;\
+ converting to normal form"))
+     (trie-transform-from-read ,trie)))
+
+
+(defun trie--avl-transform-for-print (trie)
+  ;; transform avl-tree based TRIE to print form.
+  (trie-mapc-internal
+   (lambda (avl seq) (setf (avl-tree--cmpfun avl) nil))
+   trie))
+
+
+(defun trie--avl-transform-from-read (trie)
+  ;; transform avl-tree based TRIE from print form."
+  (let ((--trie-avl-transform--cmpfun (trie--cmpfun trie)))
+    (trie-mapc-internal
+     (lambda (avl seq)
+       (setf (avl-tree--cmpfun avl) --trie-avl-transform--cmpfun))
+     trie)))
+
+
+
+;;; ----------------------------------------------------------------
+;;;                Replacements for CL functions
+
+;; copied from cl-extra.el
+(defun trie--subseq (seq start &optional end)
+  "Return the subsequence of SEQ from START to END.
+If END is omitted, it defaults to the length of the sequence.
+If START or END is negative, it counts from the end."
+  (if (stringp seq) (substring seq start end)
+    (let (len)
+      (and end (< end 0) (setq end (+ end (setq len (length seq)))))
+      (when (< start 0)
+       (setq start (+ start (or len (setq len (length seq))))))
+      (cond ((listp seq)
+            (if (> start 0) (setq seq (nthcdr start seq)))
+            (if end
+                (let ((res nil))
+                  (while (>= (setq end (1- end)) start)
+                    (push (pop seq) res))
+                  (nreverse res))
+              (copy-sequence seq)))
+           (t
+            (or end (setq end (or len (length seq))))
+            (let ((res (make-vector (max (- end start) 0) nil))
+                  (i 0))
+              (while (< start end)
+                (aset res i (aref seq start))
+                (setq i (1+ i) start (1+ start)))
+              res))))))
+
+
+(defun trie--position (item list)
+  "Find the first occurrence of ITEM in LIST.
+Return the index of the matching item, or nil of not found.
+Comparison is done with 'equal."
+  (let ((i 0))
+    (catch 'found
+      (while (progn
+              (when (equal item (car list)) (throw 'found i))
+              (setq i (1+ i))
+              (setq list (cdr list))))
+      nil)))
+
+
+(defsubst trie--seq-append (seq el)
+  "Append EL to the end of sequence SEQ."
+  (cond
+   ((stringp seq) (concat seq (string el)))
+   ((vectorp seq) (vconcat seq (vector el)))
+   ((listp seq)          (append seq (list el)))))
+
+
+(defsubst trie--seq-concat (seq &rest sequences)
+  "Concatenate SEQ and SEQUENCES, and make the result the same
+type of sequence as SEQ."
+  (cond
+   ((stringp seq) (apply 'concat  seq sequences))
+   ((vectorp seq) (apply 'vconcat seq sequences))
+   ((listp seq)          (apply 'append  seq sequences))))
+
+
+
+
+;;; ================================================================
+;;;                     Basic trie operations
+
+;;;###autoload
+(defalias 'make-trie 'trie--create
+  "Return a new trie that uses comparison function COMPARISON-FUNCTION.
+
+A trie stores sequences (strings, vectors or lists) along with
+associated data. COMPARISON-FUNCTEION should accept two
+arguments, each being an element of such a sequence, and return t
+if the first is strictly smaller than the second.
+
+The optional argument TYPE specifies the type of trie to
+create. However, the only one that is currently implemented is
+the default, so this argument is useless for now.
+
+(See also `make-trie-custom'.)")
+
+
+;;;###autoload
+(defalias 'trie-create 'make-trie)
+
+
+;;;###autoload
+(defalias 'make-trie-custom 'trie--create-custom
+  "Return a new trie that uses comparison function COMPARISON-FUNCTION.
+
+A trie stores sequences (strings, vectors or lists) along with
+associated data. COMPARISON-FUNCTION should accept two arguments,
+each being an element of such a sequence, and return t if the
+first is strictly smaller than the second.
+
+The remaining keyword arguments: :CREATEFUN, :INSERTFUN, :DELETEFUN,
+:LOOKUPFUN, :MAPFUN, :EMPTYFUN, :STACK-CREATEFUN, :STACK-POPFUN,
+:STACK-EMPTYFUN, :TRANSFORM-FOR-PRINT and :TRANSFORM-FROM-READ
+determine the type of trie that is created.
+
+CREATEFUN is called as follows:
+
+  (CREATEFUN COMPARISON-FUNCTION SEQ)
+
+and should return a data structure (\"ARRAY\") that can be used
+as an associative array, where two elements A and B are equal if
+the following is non-nil:
+
+  (and (COMPARISON-FUNCTION b a)
+       (COMPARISON-FUNCTION b a))
+
+The SEQ argument is a vector containing the sequence that will
+correspond to the newly created array in the trie. For most types
+of trie, this value is ignored. It is passed to CREATEFUN only in
+order to allow the creation of \"hybrid\" trie structures, in
+which different types of associative array are used in different
+parts of the trie. For example, the type of associative array
+could be chosen based on the depth in the trie, given by \(length
+SEQ\). (Note that all the other functions described below must be
+able to correctly handle *any* of the types of associate array
+that might be created by CREATEFUN.)
+
+INSERTFUN, DELETEFUN, LOOKUPFUN, MAPFUN and EMPTYFUN should
+insert, delete, lookup, map over, and check-if-there-exist-any
+elements in an associative array. They are called as follows:
+
+  (INSERTFUN array element &optional updatefun)
+  (DELETEFUN array element &optional predicate nilflag)
+  (LOOKUPFUN array element &optional nilflag)
+  (MAPFUN function array &optional reverse)
+  (EMPTYFUN array)
+
+INSERTFUN should insert ELEMENT into ARRAY and return the new
+element, which will be ELEMENT itself unless UPDATEFUN is
+specified. In that case, if and only if an element matching
+ELEMENT already exists in the associative array, INSERTFUN should
+instead pass ELEMENT and the matching element as arguments to
+UPDATEFUN, replace the matching element with the return value,
+and return that return value.
+
+DELETEFUN should delete the element in the associative array that
+matches ELEMENT, and return the deleted element. However, if
+PREDICATE is specified and a matching element exists in ARRAY,
+DELETEFUN should first pass the matching element as an argument
+to PREDICATE before deleting, and should only delete the element
+if PREDICATE returns non-nil. DELETEFUN should return NILFLAG if
+no element was deleted (either becuase no matching element was
+found, or because TESTFUN returned nil).
+
+LOOKUPFUN should return the element from the associative array
+that matches ELEMENT, or NILFLAG if no matching element exists.
+
+MAPFUN should map FUNCTION over all elements in the order defined by
+COMPARISON-FUNCTION, or in reverse order if REVERSE is non-nil.
+
+
+STACK-CREATEFUN, STACK-POPFUN and STACK-EMPTYFUN should allow the
+associative array to be used as a stack. STACK-CREATEFUN is
+called as follows:
+
+  (STACK-CREATEFUN array)
+
+and should return a data structure (\"STACK\") that behaves like
+a sorted stack of all elements in the associative array. I.e.
+successive calls to
+
+  (STACK-POPFUN stack)
+
+should return elements from the associative array in the order
+defined by COMPARISON-FUNCTION, and
+
+  (STACK-EMPTYFUN stack)
+
+should return non-nil if the stack is empty, nil otherwise.
+
+The stack functions are optional, in that all trie operations
+other than the stack-related ones will work correctly. However,
+any code that makes use of trie-stacks will complain if supplied
+with this type of trie.
+
+
+The :TRANSFORM-FOR-PRINT and :TRANSFORM-FROM-READ arguments are
+optional. If supplied, they can be used to transform the trie
+into a format suitable for passing to Elisp's `print'
+functions (typically used to persistently store the trie by
+writing it to file), and transform from that format back to the
+original usable form.
+
+
+Warning: to avoid nasty dynamic scoping bugs, the supplied
+functions must *never* bind any variables with names commencing
+\"--\".")
+
+
+;;;###autoload
+(defalias 'trie-create-custom 'make-trie-custom)
+
+
+
+(defalias 'trie-comparison-function 'trie--comparison-function
+  "Return the comparison function for TRIE.")
+
+
+(defalias 'trie-p 'trie--p
+  "Return t if argument is a trie, nil otherwise.")
+
+
+(defun trie-empty (trie)
+  "Return t if the TRIE is empty, nil otherwise."
+  (trie-transform-from-read-warn trie)
+  (funcall (trie--emptyfun trie)
+          (trie--node-subtree (trie--root trie))))
+
+
+(defun trie-construct-sortfun (cmpfun &optional reverse)
+  "Construct function to compare key sequences, based on a CMPFUN
+that compares individual elements of the sequence. Order is
+reversed if REVERSE is non-nil."
+  (if reverse
+      `(lambda (a b)
+        (let (cmp)
+          (catch 'compared
+            (dotimes (i (min (length a) (length b)))
+              (cond ((,cmpfun (elt b i) (elt a i))
+                     (throw 'compared t))
+                    ((,cmpfun (elt a i) (elt b i))
+                     (throw 'compared nil))))
+            (< (length a) (length b)))))
+    `(lambda (a b)
+       (let (cmp)
+        (catch 'compared
+          (dotimes (i (min (length a) (length b)))
+            (cond ((,cmpfun (elt a i) (elt b i))
+                   (throw 'compared t))
+                  ((,cmpfun (elt b i) (elt a i))
+                   (throw 'compared nil))))
+          (< (length a) (length b)))))))
+
+
+
+;; ----------------------------------------------------------------
+;;                        Inserting data
+
+(defun trie-insert (trie key &optional data updatefun)
+  "Associate DATA with KEY in TRIE.
+
+If KEY already exists in TRIE, then DATA replaces the existing
+association, unless UPDATEFUN is supplied. Note that if DATA is
+*not* supplied, this means that the existing association of KEY
+will be replaced by nil.
+
+If UPDATEFUN is supplied and KEY already exists in TRIE,
+UPDATEFUN is called with two arguments: DATA and the existing
+association of KEY. Its return value becomes the new association
+for KEY.
+
+Returns the new association of KEY.
+
+Note: to avoid nasty dynamic scoping bugs, UPDATEFUN must *not*
+bind any variables with names commencing \"--\"."
+
+  ;; convert trie from print-form if necessary
+  (trie-transform-from-read-warn trie)
+
+  ;; absurd variable names are an attempt to avoid dynamic scoping bugs
+  (let ((--trie-insert--updatefun updatefun)
+       --trie-insert--old-node-flag
+       (node (trie--root trie))
+       (len (length key))
+       (i -1))
+    ;; Descend trie, adding nodes for non-existent elements of KEY. The
+    ;; update function passed to `trie--insertfun' ensures that existing
+    ;; nodes are left intact.
+    (while (< (incf i) len)
+      (setq --trie-insert--old-node-flag nil)
+      (setq node (funcall (trie--insertfun trie)
+                         (trie--node-subtree node)
+                         (trie--node-create (elt key i) key trie)
+                         (lambda (a b)
+                           (setq --trie-insert--old-node-flag t) b))))
+    ;; Create or update data node.
+    (setq node (funcall (trie--insertfun trie)
+                       (trie--node-subtree node)
+                       (trie--node-create-data data)
+                       ;; if using existing data node, wrap UPDATEFUN
+                       ;; if any was supplied
+                       (when (and --trie-insert--old-node-flag
+                                  --trie-insert--updatefun)
+                         (lambda (new old)
+                           (setf (trie--node-data old)
+                                 (funcall --trie-insert--updatefun
+                                          (trie--node-data new)
+                                          (trie--node-data old)))
+                           old))))
+    (trie--node-data node)))  ; return new data
+
+
+
+;; ----------------------------------------------------------------
+;;                        Deleting data
+
+(defun trie-delete (trie key &optional test)
+  "Delete KEY and its associated data from TRIE.
+
+If KEY was deleted, a cons cell containing KEY and its
+association is returned. Returns nil if KEY does not exist in
+TRIE.
+
+If TEST is supplied, it should be a function that accepts two
+arguments: the key being deleted, and its associated data. The
+key will then only be deleted if TEST returns non-nil.
+
+Note: to avoid nasty dynamic scoping bugs, TEST must *not* bind
+any variables with names commencing \"--\"."
+  ;; convert trie from print-form if necessary
+  (trie-transform-from-read-warn trie)
+  ;; set up deletion (real work is done by `trie--do-delete'
+  (let (--trie-deleted--node
+       (--trie-delete--key key))
+    (declare (special --trie-deleted--node)
+            (special --trie-delete--key))
+    (trie--do-delete (trie--root trie) key test
+                    (trie--deletefun trie)
+                    (trie--emptyfun trie)
+                    (trie--cmpfun trie))
+    (when --trie-deleted--node
+      (cons key (trie--node-data --trie-deleted--node)))))
+
+
+(defun trie--do-delete (node --trie--do-delete--seq
+                            --trie--do-delete--test
+                            --trie--do-delete--deletefun
+                            --trie--do-delete--emptyfun
+                            --trie--do-delete--cmpfun)
+  ;; Delete --TRIE--DO-DELETE--SEQ starting from trie node NODE, and
+  ;; return non-nil if we deleted a node. If --TRIE--DO-DELETE--TEST is
+  ;; supplied, it is called with two arguments, the key being deleted
+  ;; and the associated data, and the deletion is only carried out if it
+  ;; returns non-nil.
+
+  ;; The absurd argument names are to lessen the likelihood of dynamical
+  ;; scoping bugs caused by a supplied function binding a variable with
+  ;; the same name as one of the arguments, which would cause a nasty
+  ;; bug when the lambda's (below) are called.
+  (declare (special --trie-deleted--node)
+          (special --trie-delete--key))
+  ;; if --TRIE--DO-DELETE--SEQ is empty, try to delete data node and
+  ;; return non-nil if we did (return value of
+  ;; --TRIE--DO-DELETE--DELETEFUN is the deleted data, which is always
+  ;; non-nil for a trie)
+  (if (= (length --trie--do-delete--seq) 0)
+      (setq --trie-deleted--node
+           (funcall --trie--do-delete--deletefun
+                    (trie--node-subtree node)
+                    (trie--node-create-dummy trie--terminator)
+                    (when --trie--do-delete--test
+                      (lambda (n)
+                        (funcall --trie--do-delete--test
+                                 --trie-delete--key (trie--node-data n))))
+                    nil))
+    ;; otherwise, delete on down (return value of
+    ;; --TRIE--DO-DELETE--DELETEFUN is the deleted data, which is always
+    ;; non-nil for a trie)
+    (funcall --trie--do-delete--deletefun
+            (trie--node-subtree node)
+            (trie--node-create-dummy (elt --trie--do-delete--seq 0))
+            (lambda (n)
+              (and (trie--do-delete
+                    n (trie--subseq --trie--do-delete--seq 1)
+                    --trie--do-delete--test
+                    --trie--do-delete--deletefun
+                    --trie--do-delete--emptyfun
+                    --trie--do-delete--cmpfun)
+                   (funcall --trie--do-delete--emptyfun
+                            (trie--node-subtree n))))
+            nil)))
+
+
+
+;; ----------------------------------------------------------------
+;;                       Retrieving data
+
+(defun trie-lookup (trie key &optional nilflag)
+  "Return the data associated with KEY in the TRIE,
+or nil if KEY does not exist in TRIE.
+
+Optional argument NILFLAG specifies a value to return instead of
+nil if KEY does not exist in TRIE. This allows a non-existent KEY
+to be distinguished from an element with a null association. (See
+also `trie-member-p', which does this for you.)"
+  ;; convert trie from print-form if necessary
+  (trie-transform-from-read-warn trie)
+  ;; find node corresponding to key, then find data node, then return
+  ;; data
+  (let (node)
+    (or (and (setq node (trie--node-find (trie--root trie) key
+                                        (trie--lookupfun trie)))
+            (trie--find-data node (trie--lookupfun trie)))
+       nilflag)))
+
+(defalias 'trie-member 'trie-lookup)
+
+
+(defun trie-member-p (trie key)
+  "Return t if KEY exists in TRIE, nil otherwise."
+  ;; convert trie from print-form if necessary
+  (trie-transform-from-read-warn trie)
+  (let ((flag '(nil)))
+    (not (eq flag (trie-member trie key flag)))))
+
+
+
+
+;;; ================================================================
+;;;                      Mapping over tries
+
+(defun trie--mapc (--trie--mapc--function --trie--mapc--mapfun
+                  --trie--mapc--root --trie--mapc--seq
+                  &optional --trie--mapc--reverse)
+  ;; Apply TRIE--MAPC--FUNCTION to all elements in a trie beneath
+  ;; TRIE--MAPC--ROOT, which should correspond to the sequence
+  ;; TRIE--MAPC--SEQ. TRIE--MAPC--FUNCTION is passed two arguments: the
+  ;; trie node itself and the sequence it corresponds to. It is applied
+  ;; in ascending order, or descending order if TRIE--MAPC--REVERSE is
+  ;; non-nil.
+
+  ;; The absurd argument names are to lessen the likelihood of dynamical
+  ;; scoping bugs caused by a supplied function binding a variable with
+  ;; the same name as one of the arguments.
+  (funcall
+   --trie--mapc--mapfun
+   (lambda (--trie--mapc--node)
+     ;; data node: apply function
+     (if (trie--node-data-p --trie--mapc--node)
+        (funcall --trie--mapc--function
+                 --trie--mapc--node
+                 --trie--mapc--seq)
+       ;; internal node: append split value to seq and keep descending
+       (trie--mapc --trie--mapc--function
+                  --trie--mapc--mapfun
+                  --trie--mapc--node
+                  (trie--seq-append
+                   (copy-sequence --trie--mapc--seq)
+                   (trie--node-split --trie--mapc--node))
+                  --trie--mapc--reverse)))
+   ;; --TRIE--MAPC--MAPFUN target
+   (trie--node-subtree --trie--mapc--root)
+   --trie--mapc--reverse))
+
+
+(defun trie-mapc-internal (function trie &optional type)
+  "Apply FUNCTION to all internal associative arrays within TRIE.
+FUNCTION is passed two arguments: an associative array, and the
+sequence it corresponds to.
+
+Optional argument TYPE (one of the symbols vector, lisp or
+string) sets the type of sequence passed to FUNCTION. Defaults to
+vector."
+  (trie--mapc-internal function (trie--mapfun trie) (trie--root trie)
+                      (cond ((eq type 'string) "")
+                            ((eq type 'lisp) ())
+                            (t []))))
+
+
+(defun trie--mapc-internal (--trie--mapc-internal--function
+                            --trie--mapc-internal--mapfun
+                            --trie--mapc-internal--root
+                            --trie--mapc-internal--seq)
+  (funcall
+   --trie--mapc-internal--mapfun
+   (lambda (--trie--mapc-internal--node)
+     ;; data node
+     (unless (trie--node-data-p --trie--mapc-internal--node)
+       (funcall --trie--mapc-internal--function
+               (trie--node-subtree --trie--mapc-internal--node)
+               --trie--mapc-internal--seq)
+       (trie--mapc-internal
+       --trie--mapc-internal--function
+       --trie--mapc-internal--mapfun
+       --trie--mapc-internal--node
+       (trie--seq-append
+        (copy-sequence --trie--mapc-internal--seq)
+        (trie--node-split --trie--mapc-internal--node)))))
+   (trie--node-subtree --trie--mapc-internal--root)))
+
+
+(defun trie-map (function trie &optional type reverse)
+  "Modify all elements in TRIE by applying FUNCTION to them.
+
+FUNCTION should take two arguments: a sequence stored in the trie
+and its associated data. Its return value replaces the existing
+data.
+
+Optional argument TYPE (one of the symbols vector, lisp or
+string) sets the type of sequence passed to FUNCTION. Defaults to
+vector.
+
+FUNCTION is applied in ascending order, or descending order if
+REVERSE is non-nil.
+
+Note: to avoid nasty dynamic scoping bugs, FUNCTION must *not*
+bind any variables with names commencing \"--\"."
+  ;; convert from print-form if necessary
+  (trie-transform-from-read-warn trie)
+  ;; map FUNCTION over TRIE
+  (let ((--trie-map--function function)) ; avoid dynamic scoping bugs
+    (trie--mapc
+     (lambda (node seq)
+       (setf (trie--node-data node)
+            (funcall --trie-map--function seq (trie--node-data node))))
+     (trie--mapfun trie)
+     (trie--root trie)
+     (cond ((eq type 'string) "") ((eq type 'lisp) ()) (t []))
+     reverse)))
+
+
+(defun trie-mapc (function trie &optional type reverse)
+  "Apply FUNCTION to all elements in TRIE for side effect only.
+
+FUNCTION should take two arguments: a sequence stored in the trie
+and its associated data.
+
+Optional argument TYPE (one of the symbols vector, lisp or
+string) sets the type of sequence passed to FUNCTION. Defaults to
+vector.
+
+FUNCTION is applied in ascending order, or descending order if
+REVERSE is non-nil.
+
+Note: to avoid nasty dynamic scoping bugs, FUNCTION must *not*
+bind any variables with names commencing \"--\"."
+  ;; convert from print-form if necessary
+  (trie-transform-from-read-warn trie)
+  ;; map FUNCTION over TRIE
+  (let ((--trie-mapc--function function)) ; avoid dynamic scoping bugs
+    (trie--mapc
+     (lambda (node seq)
+       (funcall --trie-mapc--function seq (trie--node-data node)))
+     (trie--mapfun trie)
+     (trie--root trie)
+     (cond ((eq type 'string) "") ((eq type 'lisp) ()) (t []))
+     reverse)))
+
+
+(defun trie-mapf (function combinator trie &optional type reverse)
+  "Apply FUNCTION to all elements in TRIE, and combine the results
+using COMBINATOR.
+
+FUNCTION should take two arguments: a sequence stored in the
+trie, and its associated data.
+
+Optional argument TYPE (one of the symbols vector, lisp or
+string; defaults to vector) sets the type of sequence passed to
+FUNCTION. If TYPE is 'string, it must be possible to apply the
+function `string' to the individual elements of key sequences
+stored in TRIE.
+
+The FUNCTION is applied and the results combined in ascending
+order, or descending order if REVERSE is non-nil.
+
+Note: to avoid nasty dynamic scoping bugs, FUNCTION and
+COMBINATOR must *not* bind any variables with names
+commencing \"--\"."
+  ;; convert from print-form if necessary
+  (trie-transform-from-read-warn trie)
+  ;; map FUNCTION over TRIE, combining results with COMBINATOR
+  (let ((--trie-mapf--function function) ; avoid dynamic scoping bugs
+       --trie-mapf--accumulate)
+    (trie--mapc
+     (lambda (node seq)
+       (setq --trie-mapf--accumulate
+            (funcall combinator
+                     (funcall --trie-mapf--function
+                              seq (trie--node-data node))
+                     --trie-mapf--accumulate)))
+     (trie--mapfun trie)
+     (trie--root trie)
+     (cond ((eq type 'string) "") ((eq type 'lisp) ()) (t []))
+     reverse)
+    --trie-mapf--accumulate))
+
+
+(defun trie-mapcar (function trie &optional type reverse)
+  "Apply FUNCTION to all elements in TRIE,
+and make a list of the results.
+
+FUNCTION should take two arguments: a sequence stored in the trie
+and its associated data.
+
+Optional argument TYPE (one of the symbols vector, lisp or
+string) sets the type of sequence passed to FUNCTION. Defaults to
+vector.
+
+The FUNCTION is applied and the list constructed in ascending
+order, or descending order if REVERSE is non-nil.
+
+Note that if you don't care about the order in which FUNCTION is
+applied, just that the resulting list is in the correct order,
+then
+
+  (trie-mapf function 'cons trie type (not reverse))
+
+is more efficient.
+
+Note: to avoid nasty dynamic scoping bugs, FUNCTION must *not*
+bind any variables with names commencing \"--\"."
+  ;; convert from print-form if necessary
+  (trie-transform-from-read-warn trie)
+  ;; map FUNCTION over TRIE and accumulate in a list
+  (nreverse (trie-mapf function 'cons trie type reverse)))
+
+
+
+
+;;; ================================================================
+;;;                    Using tries as stacks
+
+(defstruct (trie--stack
+           (:constructor nil)
+           (:constructor
+            trie--stack-create
+            (trie
+             &optional
+             (type 'vector)
+             reverse
+             &aux
+             (comparison-function (trie--comparison-function trie))
+             (lookupfun (trie--lookupfun trie))
+             (stack-createfun (trie--stack-createfun trie))
+             (stack-popfun (trie--stack-popfun trie))
+             (stack-emptyfun (trie--stack-emptyfun trie))
+             (repopulatefun 'trie--stack-repopulate)
+             (store
+              (if (trie-empty trie)
+                  nil
+                (trie--stack-repopulate
+                 (list (cons
+                        (cond ((eq type 'list) ())
+                              ((eq type 'string) "")
+                              (t []))
+                        (funcall
+                         stack-createfun
+                         (trie--node-subtree (trie--root trie))
+                         reverse)))
+                 reverse
+                 comparison-function lookupfun
+                 stack-createfun stack-popfun stack-emptyfun)))
+             (pushed '())
+             ))
+           (:constructor
+            trie--completion-stack-create
+            (trie prefix
+             &optional
+             reverse
+             &aux
+             (comparison-function (trie--comparison-function trie))
+             (lookupfun (trie--lookupfun trie))
+             (stack-createfun (trie--stack-createfun trie))
+             (stack-popfun (trie--stack-popfun trie))
+             (stack-emptyfun (trie--stack-emptyfun trie))
+             (repopulatefun 'trie--stack-repopulate)
+             (store (trie--completion-stack-construct-store
+                     trie prefix reverse))
+             (pushed '())
+             ))
+           (:constructor
+            trie--regexp-stack-create
+            (trie regexp
+             &optional
+             reverse
+             &aux
+             (comparison-function (trie--comparison-function trie))
+             (lookupfun (trie--lookupfun trie))
+             (stack-createfun (trie--stack-createfun trie))
+             (stack-popfun (trie--stack-popfun trie))
+             (stack-emptyfun (trie--stack-emptyfun trie))
+             (repopulatefun 'trie--regexp-stack-repopulate)
+             (store (trie--regexp-stack-construct-store
+                     trie regexp reverse))
+             (pushed '())
+             ))
+           (:copier nil))
+  reverse comparison-function lookupfun
+  stack-createfun stack-popfun stack-emptyfun
+  repopulatefun store pushed)
+
+
+(defun trie-stack (trie &optional type reverse)
+  "Return an object that allows TRIE to be accessed as a stack.
+
+The stack is sorted in \"lexical\" order, i.e. the order defined
+by the trie's comparison function, or in reverse order if REVERSE
+is non-nil. Calling `trie-stack-pop' pops the top element (a key
+and its associated data) from the stack.
+
+Optional argument TYPE (one of the symbols vector, lisp or
+string) sets the type of sequence used for the keys.
+
+Note that any modification to TRIE *immediately* invalidates all
+trie-stacks created before the modification (in particular,
+calling `trie-stack-pop' will give unpredictable results).
+
+Operations on trie-stacks are significantly more efficient than
+constructing a real stack from the trie and using standard stack
+functions. As such, they can be useful in implementing efficient
+algorithms on tries. However, in cases where mapping functions
+`trie-mapc', `trie-mapcar' or `trie-mapf' would be sufficient, it
+is better to use one of those instead."
+  ;; convert trie from print-form if necessary
+  (trie-transform-from-read-warn trie)
+  ;; if stack functions aren't defined for trie type, throw error
+  (if (not (functionp (trie--stack-createfun trie)))
+      (error "Trie type does not support stack operations")
+    ;; otherwise, create and initialise a stack
+    (trie--stack-create trie type reverse)))
+
+
+(defun trie-stack-pop (trie-stack &optional nilflag)
+  "Pop the first element from TRIE-STACK.
+
+Returns nil if the stack is empty, or NILFLAG if specified. (The
+latter allows an empty stack to be distinguished from a null
+element stored in the trie.)"
+  ;; return nilflag if stack is empty
+  (if (trie-stack-empty-p trie-stack)
+      nilflag
+    ;; if elements have been pushed onto the stack, pop those first
+    (if (trie--stack-pushed trie-stack)
+       (pop (trie--stack-pushed trie-stack))
+      ;; otherwise, pop first element from trie-stack and repopulate it
+      (prog1
+         (pop (trie--stack-store trie-stack))
+       (setf (trie--stack-store trie-stack)
+             (funcall (trie--stack-repopulatefun trie-stack)
+                      (trie--stack-store trie-stack)
+                      (trie--stack-reverse trie-stack)
+                      (trie--stack-comparison-function trie-stack)
+                      (trie--stack-lookupfun trie-stack)
+                      (trie--stack-stack-createfun trie-stack)
+                      (trie--stack-stack-popfun trie-stack)
+                      (trie--stack-stack-emptyfun trie-stack)))))))
+
+
+(defun trie-stack-push (element trie-stack)
+  "Push ELEMENT onto TRIE-STACK."
+  (push element (trie--stack-pushed trie-stack)))
+
+
+(defun trie-stack-first (trie-stack &optional nilflag)
+  "Return the first element from TRIE-STACK, without removing it
+from the stack.
+
+Returns nil if the stack is empty, or NILFLAG if specified. (The
+latter allows an empty stack to be distinguished from a null
+element stored in the trie.)"
+  ;; return nilflag if stack is empty
+  (if (trie-stack-empty-p trie-stack)
+      nilflag
+    ;; if elements have been pushed onto the stack, return first of
+    ;; those
+    (if (trie--stack-pushed trie-stack)
+       (car (trie--stack-pushed trie-stack))
+      ;; otherwise, return first element from trie-stack
+      (car (trie--stack-store trie-stack)))))
+
+
+(defalias 'trie-stack-p 'trie--stack-p
+  "Return t if argument is a trie-stack, nil otherwise.")
+
+
+(defun trie-stack-empty-p (trie-stack)
+  "Return t if TRIE-STACK is empty, nil otherwise."
+  (and (null (trie--stack-store trie-stack))
+       (null (trie--stack-pushed trie-stack))))
+
+
+(defun trie--stack-repopulate
+  (store reverse comparison-function lookupfun
+        stack-createfun stack-popfun stack-emptyfun)
+  ;; Recursively push children of the node at the head of STORE onto the
+  ;; front of STORE, until a data node is reached.
+
+  ;; nothing to do if stack is empty
+  (when store
+    (let ((node (funcall stack-popfun (cdar store)))
+         (seq (caar store)))
+      (when (funcall stack-emptyfun (cdar store))
+       ;; (pop store) here produces irritating compiler warnings
+       (setq store (cdr store)))
+
+      (while (not (trie--node-data-p node))
+       (push
+        (cons (trie--seq-append seq (trie--node-split node))
+              (funcall stack-createfun
+                       (trie--node-subtree node) reverse))
+        store)
+       (setq node (funcall stack-popfun (cdar store))
+             seq (caar store))
+       (when (funcall stack-emptyfun (cdar store))
+         ;; (pop store) here produces irritating compiler warnings
+         (setq store (cdr store))))
+
+      (push (cons seq (trie--node-data node)) store))))
+
+
+
+
+;; ================================================================
+;;                   Query-building utility macros
+
+;; Implementation Note
+;; -------------------
+;; For queries ranked in anything other than lexical order, we use a
+;; partial heap-sort to find the k=MAXNUM highest ranked matches among
+;; the n possibile matches. This has worst-case time complexity
+;; O(n log k), and is both simple and elegant. An optimal algorithm
+;; (e.g. partial quick-sort discarding the irrelevant partition at each
+;; step) would have complexity O(n + k log k), but is probably not worth
+;; the extra coding effort, and would have worse space complexity unless
+;; coded to work "in-place", which would be highly non-trivial. (I
+;; haven't done any benchmarking, though, so feel free to do so and let
+;; me know the results!)
+
+(defmacro trie--construct-accumulator (maxnum filter resultfun)
+  ;; Does what it says on the tin! | sed -e 's/tin/macro name/'
+  `(cond
+    ;; filter, maxnum, resultfun
+    ((and ,filter ,maxnum ,resultfun)
+     (lambda (seq data)
+       (when (funcall ,filter seq data)
+        (aset trie--accumulate 0
+              (cons (funcall ,resultfun seq data)
+                    (aref trie--accumulate 0)))
+        (and (>= (length (aref trie--accumulate 0)) ,maxnum)
+             (throw 'trie-accumulate--done nil)))))
+    ;; filter, maxnum, !resultfun
+    ((and ,filter ,maxnum (not ,resultfun))
+     (lambda (seq data)
+       (when (funcall ,filter seq data)
+        (aset trie--accumulate 0
+              (cons (cons seq data)
+                    (aref trie--accumulate 0)))
+        (and (>= (length (aref trie--accumulate 0)) ,maxnum)
+             (throw 'trie-accumulate--done nil)))))
+    ;; filter, !maxnum, resultfun
+    ((and ,filter (not ,maxnum) ,resultfun)
+     (lambda (seq data)
+       (when (funcall ,filter seq data)
+        (aset trie--accumulate 0
+              (cons (funcall ,resultfun seq data)
+                    (aref trie--accumulate 0))))))
+    ;; filter, !maxnum, !resultfun
+    ((and ,filter (not ,maxnum) (not ,resultfun))
+     (lambda (seq data)
+       (when (funcall ,filter seq data)
+        (aset trie--accumulate 0
+              (cons (cons seq data)
+                    (aref trie--accumulate 0))))))
+    ;; !filter, maxnum, resultfun
+    ((and (not ,filter) ,maxnum ,resultfun)
+     (lambda (seq data)
+       (aset trie--accumulate 0
+            (cons (funcall ,resultfun seq data)
+                  (aref trie--accumulate 0)))
+       (and (>= (length (aref trie--accumulate 0)) ,maxnum)
+           (throw 'trie-accumulate--done nil))))
+    ;; !filter, maxnum, !resultfun
+    ((and (not ,filter) ,maxnum (not ,resultfun))
+     (lambda (seq data)
+       (aset trie--accumulate 0
+            (cons (cons seq data)
+                  (aref trie--accumulate 0)))
+       (and (>= (length (aref trie--accumulate 0)) ,maxnum)
+           (throw 'trie-accumulate--done nil))))
+    ;; !filter, !maxnum, resultfun
+    ((and (not ,filter) (not ,maxnum) ,resultfun)
+     (lambda (seq data)
+       (aset trie--accumulate 0
+            (cons (funcall ,resultfun seq data)
+                  (aref trie--accumulate 0)))))
+    ;; !filter, !maxnum, !resultfun
+    ((and (not ,filter) (not ,maxnum) (not ,resultfun))
+     (lambda (seq data)
+       (aset trie--accumulate 0
+            (cons (cons seq data)
+                  (aref trie--accumulate 0)))))
+    ))
+
+
+
+(defmacro trie--construct-ranked-accumulator (maxnum filter)
+  ;; Does what it says on the tin! | sed -e 's/tin/macro name/'
+  `(cond
+    ;; filter, maxnum
+    ((and ,filter ,maxnum)
+     (lambda (seq data)
+       (when (funcall ,filter seq data)
+        (heap-add trie--accumulate (cons seq data))
+        (and (> (heap-size trie--accumulate) ,maxnum)
+             (heap-delete-root trie--accumulate)))))
+    ;; filter, !maxnum
+    ((and ,filter (not ,maxnum))
+     (lambda (seq data)
+       (when (funcall ,filter seq data)
+        (heap-add trie--accumulate (cons seq data)))))
+    ;; !filter, maxnum
+    ((and (not ,filter) ,maxnum)
+     (lambda (seq data)
+       (heap-add trie--accumulate (cons seq data))
+       (and (> (heap-size trie--accumulate) ,maxnum)
+           (heap-delete-root trie--accumulate))))
+    ;; !filter, !maxnum
+    ((and (not ,filter) (not ,maxnum))
+     (lambda (seq data)
+       (heap-add trie--accumulate (cons seq data))))))
+
+
+
+(defmacro trie--accumulate-results
+  (rankfun maxnum reverse filter resultfun accfun duplicates &rest body)
+  ;; Accumulate results of running BODY code, and return them in
+  ;; appropriate order. BODY should call ACCFUN to accumulate a result,
+  ;; passing it two arguments: a trie data node, and the corresponding
+  ;; sequence. BODY can throw 'trie-accumulate--done to terminate the
+  ;; accumulation and return the results. A non-null DUPLICATES flag
+  ;; signals that the accumulated results might contain duplicates,
+  ;; which should be deleted. Note that DUPLICATES is ignored if RANKFUN
+  ;; is null. The other arguments should be passed straight through from
+  ;; the query function.
+
+  ;; rename functions to help avoid dynamic-scoping bugs
+  `(let* ((--trie-accumulate--rankfun ,rankfun)
+         (--trie-accumulate--filter ,filter)
+         (--trie-accumulate--resultfun ,resultfun)
+         ;; construct structure in which to accumulate results
+         (trie--accumulate
+          (if ,rankfun
+              (heap-create  ; heap order is inverse of rank order
+               (if ,reverse
+                   (lambda (a b)
+                     (funcall --trie-accumulate--rankfun a b))
+                 (lambda (a b)
+                   (not (funcall --trie-accumulate--rankfun a b))))
+               (when ,maxnum (1+ ,maxnum)))
+            (make-vector 1 nil)))
+         ;; construct function to accumulate completions
+         (,accfun
+          (if ,rankfun
+              (trie--construct-ranked-accumulator
+               ,maxnum --trie-accumulate--filter)
+            (trie--construct-accumulator
+             ,maxnum --trie-accumulate--filter
+             --trie-accumulate--resultfun))))
+
+     ;; accumulate results
+     (catch 'trie-accumulate--done ,@body)
+
+     ;; return list of completions
+     (cond
+      ;; for a ranked query, extract completions from heap
+      (,rankfun
+       (let (completions)
+        ;; check for and delete duplicates if flag is set
+        (if ,duplicates
+            (while (not (heap-empty trie--accumulate))
+              (if (equal (car (heap-root trie--accumulate))
+                         (caar completions))
+                  (heap-delete-root trie--accumulate)
+                (push (heap-delete-root trie--accumulate)
+                      completions)))
+          ;; skip duplicate checking if flag is not set
+          (while (not (heap-empty trie--accumulate))
+            (if ,resultfun
+                (let ((res (heap-delete-root trie--accumulate)))
+                  (push (funcall ,resultfun (car res) (cdr res))
+                        completions))
+              (push (heap-delete-root trie--accumulate)
+                    completions))))
+        completions))
+
+      ;; for lexical query, reverse result list if MAXNUM supplied
+      (,maxnum (nreverse (aref trie--accumulate 0)))
+      ;; otherwise, just return list
+      (t (aref trie--accumulate 0)))))
+
+
+
+
+;; ================================================================
+;;                          Completing
+
+(defun trie-complete
+  (trie prefix &optional rankfun maxnum reverse filter resultfun)
+  "Return an alist containing all completions of PREFIX in TRIE
+along with their associated data, in the order defined by
+RANKFUN, defaulting to \"lexical\" order (i.e. the order defined
+by the trie's comparison function). If REVERSE is non-nil, the
+completions are sorted in the reverse order. Returns nil if no
+completions are found.
+
+PREFIX must be a sequence (vector, list or string) containing
+elements of the type used to reference data in the trie. (If
+PREFIX is a string, it must be possible to apply `string' to
+individual elements of the sequences stored in the trie.) The
+completions returned in the alist will be sequences of the same
+type as KEY. If PREFIX is a list of sequences, completions of all
+sequences in the list are included in the returned alist. All
+sequences in the list must be of the same type.
+
+The optional integer argument MAXNUM limits the results to the
+first MAXNUM completions. Otherwise, all completions are
+returned.
+
+If specified, RANKFUN must accept two arguments, both cons
+cells. The car contains a sequence from the trie (of the same
+type as PREFIX), the cdr contains its associated data. It should
+return non-nil if first argument is ranked strictly higher than
+the second, nil otherwise.
+
+The FILTER argument sets a filter function for the
+completions. If supplied, it is called for each possible
+completion with two arguments: the completion, and its associated
+data. If the filter function returns nil, the completion is not
+included in the results, and does not count towards MAXNUM.
+
+RESULTFUN defines a function used to process results before
+adding them to the final result list. If specified, it should
+accept two arguments: a key and its associated data. It's return
+value is what gets added to the final result list, instead of the
+default key-data cons cell."
+
+  ;; convert trie from print-form if necessary
+  (trie-transform-from-read-warn trie)
+  ;; wrap prefix in a list if necessary
+  ;; FIXME: the test for a list of prefixes, below, will fail if the
+  ;;        PREFIX sequence is a list, and the elements of PREFIX are
+  ;;        themselves lists (there might be no easy way to fully fix
+  ;;        this...)
+  (if (or (atom prefix)
+         (and (listp prefix) (not (sequencep (car prefix)))))
+      (setq prefix (list prefix))
+    ;; sort list of prefixes if sorting completions lexically
+    (when (null rankfun)
+      (setq prefix
+           (sort prefix (trie-construct-sortfun
+                         (trie--comparison-function trie))))))
+
+  ;; accumulate completions
+  (let (node)
+    (declare (special accumulator))
+    (trie--accumulate-results
+     rankfun maxnum reverse filter resultfun accumulator nil
+     (mapc (lambda (pfx)
+            (setq node (trie--node-find (trie--root trie) pfx
+                                        (trie--lookupfun trie)))
+            (when node
+              (trie--mapc
+               (lambda (node seq)
+                 (funcall accumulator seq (trie--node-data node)))
+               (trie--mapfun trie) node pfx
+               (if maxnum reverse (not reverse)))))
+          prefix))
+    ))
+
+
+
+(defun trie-complete-stack (trie prefix &optional reverse)
+  "Return an object that allows completions of PREFIX to be accessed
+as if they were a stack.
+
+The stack is sorted in \"lexical\" order, i.e. the order defined
+by TRIE's comparison function, or in reverse order if REVERSE is
+non-nil. Calling `trie-stack-pop' pops the top element (a key and
+its associated data) from the stack.
+
+PREFIX must be a sequence (vector, list or string) that forms the
+initial part of a TRIE key, or a list of such sequences. (If
+PREFIX is a string, it must be possible to apply `string' to
+individual elements of TRIE keys.)  The completions returned in
+the alist will be sequences of the same type as KEY. If PREFIX is
+a list of sequences, completions of all sequences in the list are
+included in the stack. All sequences in the list must be of the
+same type.
+
+Note that any modification to TRIE *immediately* invalidates all
+trie-stacks created before the modification (in particular,
+calling `trie-stack-pop' will give unpredictable results).
+
+Operations on trie-stacks are significantly more efficient than
+constructing a real stack from completions of PREFIX in TRIE and
+using standard stack functions. As such, they can be useful in
+implementing efficient algorithms on tries. However, in cases
+where `trie-complete' or `trie-complete-ordered' is sufficient,
+it is better to use one of those instead."
+  ;; convert trie from print-form if necessary
+  (trie-transform-from-read-warn trie)
+  ;; if stack functions aren't defined for trie type, throw error
+  (if (not (functionp (trie--stack-createfun trie)))
+      (error "Trie type does not support stack operations")
+    ;; otherwise, create and initialise a stack
+    (trie--completion-stack-create trie prefix reverse)))
+
+
+(defun trie--completion-stack-construct-store (trie prefix reverse)
+  ;; Construct store for completion stack based on TRIE.
+  (let (store node)
+    (if (or (atom prefix)
+           (and (listp prefix)
+                (not (sequencep (car prefix)))))
+       (setq prefix (list prefix))
+      (setq prefix
+           (sort prefix
+                 (trie-construct-sortfun
+                  (trie--comparison-function trie)
+                  (not reverse)))))
+    (dolist (pfx prefix)
+      (when (setq node (trie--node-find (trie--root trie) pfx
+                                       (trie--lookupfun trie)))
+       (push (cons pfx (funcall (trie--stack-createfun trie)
+                                (trie--node-subtree node)
+                                reverse))
+             store)))
+    (trie--stack-repopulate
+     store reverse
+     (trie--comparison-function trie)
+     (trie--lookupfun trie)
+     (trie--stack-createfun trie)
+     (trie--stack-popfun trie)
+     (trie--stack-emptyfun trie))))
+
+
+
+
+;; ================================================================
+;;                        Regexp search
+
+(defun trie-regexp-search
+  (trie regexp &optional rankfun maxnum reverse filter resultfun type)
+  "Return an alist containing all matches for REGEXP in TRIE
+along with their associated data, in the order defined by
+RANKFUN, defauling to \"lexical\" order (i.e. the order defined
+by the trie's comparison function).  If REVERSE is non-nil, the
+completions are sorted in the reverse order. Returns nil if no
+completions are found.
+
+REGEXP is a regular expression, but it need not necessarily be a
+string. It must be a sequence (vector, list of string) whose
+elements are either elements of the same type as elements of the
+trie keys (which behave as literals in the regexp), or any of the
+usual regexp special characters and backslash constructs. If
+REGEXP is a string, it must be possible to apply `string' to
+individual elements of the keys stored in the trie. The matches
+returned in the alist will be sequences of the same type as KEY.
+
+Only a subset of the full Emacs regular expression syntax is
+supported. There is no support for regexp constructs that are
+only meaningful for strings (character ranges and character
+classes inside character alternatives, and syntax-related
+backslash constructs). Back-references and non-greedy postfix
+operators are not supported, so `?' after a postfix operator
+loses its special meaning. Also, matches are always anchored, so
+`$' and `^' lose their special meanings (use `.*' at the
+beginning and end of the regexp to get an unanchored match).
+
+If the regexp contains any non-shy grouping constructs, subgroup
+match data is included in the results. In this case, the car of
+each match is no longer just a key. Instead, it is a list whose
+first element is the matching key, and whose remaining elements
+are cons cells whose cars and cdrs give the start and end indices
+of the elements that matched the corresponding groups, in order.
+
+The optional integer argument MAXNUM limits the results to the
+first MAXNUM matches. Otherwise, all matches are returned.
+
+If specified, RANKFUN must accept two arguments, both cons
+cells. The car contains a sequence from the trie (of the same
+type as PREFIX), the cdr contains its associated data. It should
+return non-nil if first argument is ranked strictly higher than
+the second, nil otherwise.
+
+The FILTER argument sets a filter function for the matches. If
+supplied, it is called for each possible match with two
+arguments: the matching key, and its associated data. If the
+filter function returns nil, the match is not included in the
+results, and does not count towards MAXNUM.
+
+RESULTFUN defines a function used to process results before
+adding them to the final result list. If specified, it should
+accept two arguments: a key and its associated data. It's return
+value is what gets added to the final result list, instead of the
+default key-data cons cell."
+
+  ;; convert trie from print-form if necessary
+  (trie-transform-from-read-warn trie)
+
+  ;; massage rankfun to cope with grouping data
+  ;; FIXME: could skip this if REGEXP contains no grouping constructs
+  (when rankfun
+    (setq rankfun
+         `(lambda (a b)
+            ;; if car of argument contains a key+group list rather than
+            ;; a straight key, remove group list
+            ;; FIXME: the test for straight key, below, will fail if
+            ;;        the key is a list, and the first element of the
+            ;;        key is itself a list (there might be no easy way
+            ;;        to fully fix this...)
+            (unless (or (atom (car a))
+                        (and (listp (car a))
+                             (not (sequencep (caar a)))))
+              (setq a (cons (caar a) (cdr a))))
+            (unless (or (atom (car b))
+                        (and (listp (car b))
+                             (not (sequencep (caar b)))))
+              (setq b (cons (caar b) (cdr b))))
+            ;; call rankfun on massaged arguments
+            (,rankfun a b))))
+
+  ;; accumulate completions
+  (declare (special accumulator))
+  (trie--accumulate-results
+   rankfun maxnum reverse filter resultfun accumulator nil
+   (trie--do-regexp-search
+    (trie--root trie)
+    (tNFA-from-regexp regexp :test (trie--construct-equality-function
+                                   (trie--comparison-function trie)))
+    (cond ((stringp regexp) "") ((listp regexp) ()) (t []))  0
+    (or (and maxnum reverse) (and (not maxnum) (not reverse)))
+    (trie--comparison-function trie)
+    (trie--lookupfun trie)
+    (trie--mapfun trie))))
+
+
+
+(defun trie--do-regexp-search
+  (--trie--regexp-search--node tNFA seq pos reverse
+                              comparison-function lookupfun mapfun)
+  ;; Search everything below the node --TRIE--REGEXP-SEARCH-NODE for
+  ;; matches to the regexp encoded in tNFA. SEQ is the sequence
+  ;; corresponding to NODE, POS is it's length. REVERSE is the usual
+  ;; query argument, and the remaining arguments are the corresponding
+  ;; trie functions.
+  (declare (special accumulator))
+
+  ;; if NFA has matched and we're accumulating in normal order, check if
+  ;; trie contains current string
+  (when (and (not reverse) (tNFA-match-p tNFA))
+    (let (node groups)
+      (when (setq node (trie--find-data-node
+                       --trie--regexp-search--node lookupfun))
+       (setq groups (tNFA-group-data tNFA))
+       (funcall accumulator
+                (if groups (cons seq groups) seq)
+                (trie--node-data node)))))
+
+  (cond
+   ;; ;; data node
+   ;; ((trie--node-data-p --trie--regexp-search--node)
+   ;;  (when (tNFA-match-p tNFA)
+   ;;    (let ((groups (tNFA-group-data tNFA)))
+   ;;  (funcall accumulator
+   ;;           (if groups (cons seq groups) seq)
+   ;;           (trie--node-data --trie--regexp-search--node)))))
+
+   ;; wildcard transition: map over all nodes in subtree
+   ((tNFA-wildcard-p tNFA)
+    (let (state groups)
+      (funcall mapfun
+              (lambda (node)
+                (unless (trie--node-data-p node)
+                    ;; (when (tNFA-match-p tNFA)
+                    ;;   (setq groups (tNFA-group-data tNFA))
+                    ;;   (funcall accumulator
+                    ;;                 (if groups (cons seq groups) seq)
+                    ;;                 (trie--node-data node)))
+                  (when (setq state (tNFA-next-state
+                                     tNFA (trie--node-split node) pos))
+                    (trie--do-regexp-search
+                     node state
+                     (trie--seq-append seq (trie--node-split node))
+                     (1+ pos) reverse comparison-function
+                     lookupfun mapfun))))
+              (trie--node-subtree --trie--regexp-search--node)
+              reverse)))
+
+   (t ;; no wildcard transition: loop over all transitions
+    (let (node state)
+      (dolist (chr (sort (tNFA-transitions tNFA)
+                        (if reverse
+                            `(lambda (a b) (,comparison-function b a))
+                          comparison-function)))
+       (when (and (setq node (trie--node-find
+                              --trie--regexp-search--node
+                              (vector chr) lookupfun))
+                  (setq state (tNFA-next-state tNFA chr pos)))
+         (trie--do-regexp-search
+          node state (trie--seq-append seq chr) (1+ pos)
+          reverse comparison-function lookupfun mapfun))))))
+
+  ;; if NFA has matched and we're accumulating in reverse order, check if
+  ;; trie contains current string
+  (when (and reverse (tNFA-match-p tNFA))
+    (let (node groups)
+      (when (setq node (trie--find-data-node
+                       --trie--regexp-search--node lookupfun))
+       (setq groups (tNFA-group-data tNFA))
+       (funcall accumulator
+                (if groups (cons seq groups) seq)
+                (trie--node-data node))))))
+
+
+
+(defun trie-regexp-stack  (trie regexp &optional reverse)
+  "Return an object that allows matches to REGEXP to be accessed
+as if they were a stack.
+
+The stack is sorted in \"lexical\" order, i.e. the order defined
+by TRIE's comparison function, or in reverse order if REVERSE is
+non-nil. Calling `trie-stack-pop' pops the top element (a cons
+cell containing a key and its associated data) from the stack.
+
+REGEXP is a regular expression, but it need not necessarily be a
+string. It must be a sequence (vector, list of string) whose
+elements are either elements of the same type as elements of the
+trie keys (which behave as literals in the regexp), or any of the
+usual regexp special characters and backslash constructs. If
+REGEXP is a string, it must be possible to apply `string' to
+individual elements of the keys stored in the trie. The matches
+returned in the alist will be sequences of the same type as KEY.
+
+Back-references and non-greedy postfix operators are *not*
+supported, and the matches are always anchored, so `$' and `^'
+lose their special meanings.
+
+If the regexp contains any non-shy grouping constructs, subgroup
+match data is included in the results. In this case, the car of
+each match (as returned by a call to `trie-stack-pop' is no
+longer just a key. Instead, it is a list whose first element is
+the matching key, and whose remaining elements are cons cells
+whose cars and cdrs give the start and end indices of the
+elements that matched the corresponding groups, in order."
+
+  ;; convert trie from print-form if necessary
+  (trie-transform-from-read-warn trie)
+  ;; if stack functions aren't defined for trie type, throw error
+  (if (not (functionp (trie--stack-createfun trie)))
+      (error "Trie type does not support stack operations")
+    ;; otherwise, create and initialise a regexp stack
+    (trie--regexp-stack-create trie regexp reverse)))
+
+
+(defun trie--regexp-stack-construct-store
+  (trie regexp &optional reverse)
+  ;; Construct store for regexp stack based on TRIE.
+  (let ((seq (cond ((stringp regexp) "") ((listp regexp) ()) (t [])))
+       store)
+    (push (list seq (trie--root trie)
+               (tNFA-from-regexp
+                regexp :test (trie--construct-equality-function
+                              (trie--comparison-function trie)))
+               0)
+         store)
+    (trie--regexp-stack-repopulate
+     store reverse
+     (trie--comparison-function trie)
+     (trie--lookupfun trie)
+     (trie--stack-createfun trie)
+     (trie--stack-popfun trie)
+     (trie--stack-emptyfun trie))))
+
+
+(defun trie--regexp-stack-repopulate
+  (store reverse comparison-function lookupfun
+        stack-createfun stack-popfun stack-emptyfun)
+  ;; Recursively push matching children of the node at the head of STORE
+  ;; onto STORE, until a data node is reached. REVERSE is the usual
+  ;; query argument, and the remaining arguments are the corresponding
+  ;; trie functions.
+  (let (state seq node pos groups n s)
+    (while
+       (progn
+         (setq pos (pop store)
+               seq (nth 0 pos)
+               node (nth 1 pos)
+               state (nth 2 pos)
+               pos (nth 3 pos))
+         (cond
+          ;; if stack is empty, we're done
+          ((null node) nil)
+
+          ;; if stack element is a trie node...
+          ((trie--node-p node)
+           (cond
+            ;; matching data node: add data to the stack and we're done
+            ((trie--node-data-p node)
+             (when (tNFA-match-p state)
+               (setq groups (tNFA-group-data state))
+               (push (cons (if groups (cons groups seq) seq)
+                           (trie--node-data node))
+                     store))
+             nil)  ; return nil to exit loop
+
+            ;; wildcard transition: add new node stack
+            ((tNFA-wildcard-p state)
+             (push (list seq
+                         (funcall stack-createfun
+                                  (trie--node-subtree node) reverse)
+                         state pos)
+                   store))
+
+            (t ;; non-wildcard transition: add all possible next nodes
+             (dolist (chr (sort (tNFA-transitions state)
+                                (if reverse
+                                    comparison-function
+                                  `(lambda (a b)
+                                     (,comparison-function b a)))))
+               (when (and (setq n (trie--node-find
+                                   node (vector chr) lookupfun))
+                          (setq s (tNFA-next-state state chr pos)))
+                 (push (list (trie--seq-append seq chr) n s (1+ pos))
+                       store)))
+             t)))  ; return t to keep looping
+
+          ;; otherwise, stack element is a node stack...
+          (t
+           ;; if node stack is empty, dump it and keep repopulating
+           (if (funcall stack-emptyfun node)
+               t  ; return t to keep looping
+             ;; otherwise, add node stack back, and add next node from
+             ;; stack
+             (push (list seq node state pos) store)
+             (setq node (funcall stack-popfun node)
+                   state (tNFA-next-state state
+                                          (trie--node-split node) pos))
+             (when state
+               ;; matching data node: add data to the stack and we're
+               ;; done
+               (if (trie--node-data-p node)
+                   (progn
+                     (push (cons seq (trie--node-data node)) store)
+                     nil)  ; return nil to exit loop
+                 ;; normal node: add it to the stack and keep
+                 ;; repopulating
+                 (push (list
+                        (trie--seq-append seq (trie--node-split node))
+                        node state (1+ pos))
+                       store)))))
+          ))))
+  store)
+
+
+
+;; ----------------------------------------------------------------
+;;            Pretty-print tries during edebug
+
+;; Note:
+;; -----
+
+;; We advise the `edebug-prin1' and `edebug-prin1-to-string' functions
+;; (actually, aliases) so that they print "#<trie>" instead of the full
+;; print form for tries.
+;;
+;; This is because, if left to its own devices, edebug hangs for ages
+;; whilst printing large tries, and you either have to wait for a *very*
+;; long time for it to finish, or kill Emacs entirely. (Even C-g C-g
+;; fails!)
+;;
+;; We do this also for lists of tries, since those occur quite often,
+;; but not for other sequence types or deeper nested structures, to keep
+;; the implementation as simple as possible.
+;;
+;; Since the print form of a trie is practically incomprehensible
+;; anyway, we don't lose much by doing this. If you *really* want to
+;; print tries in full whilst edebugging, despite this warning, disable
+;; the advice.
+;;
+;; FIXME: We could use `cedet-edebug-prin1-extensions' instead of advice
+;;        when `cedet-edebug' is loaded, though I believe the current
+;;        implementation still works in that case.
+
+
+(eval-when-compile
+  (require 'edebug)
+  (require 'advice))
+
+
+(defun trie--edebug-pretty-print (object)
+  (cond
+   ((trie-p object) "#<trie>")
+   ((null object) "nil")
+   ((let ((tlist object) (test t))
+      (while (or (trie-p (car-safe tlist))
+                (and tlist (setq test nil)))
+       (setq tlist (cdr tlist)))
+      test)
+    (concat "(" (mapconcat (lambda (dummy) "#<trie>") object " ") ")"))
+   ;; ((vectorp object)
+   ;;  (let ((pretty "[") (len (length object)))
+   ;;    (dotimes (i (1- len))
+   ;;  (setq pretty
+   ;;        (concat pretty
+   ;;                (if (trie-p (aref object i))
+   ;;                    "#<trie>" (prin1-to-string (aref object i))) " ")))
+   ;;    (concat pretty
+   ;;        (if (trie-p (aref object (1- len)))
+   ;;            "#<trie>" (prin1-to-string (aref object (1- len))))
+   ;;        "]")))
+   ))
+
+
+(when (fboundp 'ad-define-subr-args)
+  (ad-define-subr-args 'edebug-prin1 '(object &optional printcharfun)))
+
+(defadvice edebug-prin1
+  (around trie activate compile preactivate)
+  (let ((pretty (trie--edebug-pretty-print object)))
+    (if pretty
+       (progn
+         (prin1 pretty printcharfun)
+         (setq ad-return-value pretty))
+    ad-do-it)))
+
+
+(when (fboundp 'ad-define-subr-args)
+  (ad-define-subr-args 'edebug-prin1-to-string '(object &optional noescape)))
+
+(defadvice edebug-prin1-to-string
+  (around trie activate compile preactivate)
+  (let ((pretty (trie--edebug-pretty-print object)))
+    (if pretty
+       (setq ad-return-value pretty)
+      ad-do-it)))
+
+
+
+(provide 'trie)
+
+;;; trie.el ends here


reply via email to

[Prev in Thread] Current Thread [Next in Thread]